From d66c23d4a81c6165965114e855a4f361c9481649 Mon Sep 17 00:00:00 2001 From: sr55 Date: Fri, 10 May 2019 22:37:25 +0100 Subject: [PATCH] WinGui: Switch the GUI over to using JSON for the queue. (Note, this is not the same format as the CLI and thus cannot be imported. This may happen in the future) Please finish existing queue before installing new version. --- .../Model/HBConfiguration.cs | 7 +- win/CS/HandBrakeWPF/HandBrakeWPF.csproj | 4 +- .../Helpers/QueueRecoveryHelper.cs | 45 +- .../Properties/Resources.Designer.cs | 27 + win/CS/HandBrakeWPF/Properties/Resources.resx | 9 + .../Services/Encode/Model/EncodeTask.cs | 2 + .../Encode/Model/Models/AudioTrack.cs | 4 +- .../Encode/Model/Models/ChapterMarker.cs | 6 +- .../Encode/Model/Models/SubtitleTrack.cs | 6 +- .../Services/PrePostActionService.cs | 4 +- .../{IQueueProcessor.cs => IQueueService.cs} | 432 ++--- .../Services/Queue/Model/QueueStats.cs | 3 + .../Services/Queue/Model/QueueTask.cs | 58 +- .../{QueueProcessor.cs => QueueService.cs} | 1399 ++++++++--------- .../HandBrakeWPF/Startup/AppBootstrapper.cs | 2 +- .../HandBrakeWPF/ViewModels/MainViewModel.cs | 4 +- .../HandBrakeWPF/ViewModels/MiniViewModel.cs | 4 +- .../HandBrakeWPF/ViewModels/QueueViewModel.cs | 4 +- .../HandBrakeWPF/ViewModels/ShellViewModel.cs | 4 +- 19 files changed, 1019 insertions(+), 1005 deletions(-) rename win/CS/HandBrakeWPF/Services/Queue/Interfaces/{IQueueProcessor.cs => IQueueService.cs} (92%) rename win/CS/HandBrakeWPF/Services/Queue/{QueueProcessor.cs => QueueService.cs} (87%) diff --git a/win/CS/HandBrake.Interop/Model/HBConfiguration.cs b/win/CS/HandBrake.Interop/Model/HBConfiguration.cs index 2ec9f8302..06362dd14 100644 --- a/win/CS/HandBrake.Interop/Model/HBConfiguration.cs +++ b/win/CS/HandBrake.Interop/Model/HBConfiguration.cs @@ -9,11 +9,12 @@ namespace HandBrake.Interop.Model { - /// - /// HandBrakes configuration options - /// public class HBConfiguration { + public HBConfiguration() + { + } + /// /// Gets or sets a value indicating whether is dvd nav disabled. /// diff --git a/win/CS/HandBrakeWPF/HandBrakeWPF.csproj b/win/CS/HandBrakeWPF/HandBrakeWPF.csproj index 53235d092..b81e5fce7 100644 --- a/win/CS/HandBrakeWPF/HandBrakeWPF.csproj +++ b/win/CS/HandBrakeWPF/HandBrakeWPF.csproj @@ -223,7 +223,7 @@ - + @@ -233,7 +233,7 @@ - + diff --git a/win/CS/HandBrakeWPF/Helpers/QueueRecoveryHelper.cs b/win/CS/HandBrakeWPF/Helpers/QueueRecoveryHelper.cs index 2a7318814..072baafc7 100644 --- a/win/CS/HandBrakeWPF/Helpers/QueueRecoveryHelper.cs +++ b/win/CS/HandBrakeWPF/Helpers/QueueRecoveryHelper.cs @@ -16,7 +16,6 @@ namespace HandBrakeWPF.Helpers using System.Linq; using System.Text.RegularExpressions; using System.Windows; - using System.Xml.Serialization; using HandBrake.Interop.Utilities; @@ -24,13 +23,14 @@ namespace HandBrakeWPF.Helpers using HandBrakeWPF.Services.Queue.Model; using HandBrakeWPF.Utilities; - using IQueueProcessor = HandBrakeWPF.Services.Queue.Interfaces.IQueueProcessor; + using Newtonsoft.Json; + + using IQueueService = HandBrakeWPF.Services.Queue.Interfaces.IQueueService; - /// - /// Queue Recovery Helper - /// public class QueueRecoveryHelper { + public static string QueueFileName = "hb_queue"; + /// /// Check if the queue recovery file contains records. /// If it does, it means the last queue did not complete before HandBrake closed. @@ -48,7 +48,7 @@ namespace HandBrakeWPF.Helpers { string tempPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); DirectoryInfo info = new DirectoryInfo(tempPath); - IEnumerable foundFiles = info.GetFiles("*.xml").Where(f => f.Name.StartsWith("hb_queue_recovery")); + IEnumerable foundFiles = info.GetFiles("*.json").Where(f => f.Name.StartsWith(QueueFileName)); var queueFiles = GetFilesExcludingActiveProcesses(foundFiles, filterQueueFiles); if (!queueFiles.Any()) @@ -59,14 +59,14 @@ namespace HandBrakeWPF.Helpers List removeFiles = new List(); List acceptedFiles = new List(); - XmlSerializer ser = new XmlSerializer(typeof(List)); + foreach (string file in queueFiles) { try { - using (FileStream strm = new FileStream(file, FileMode.Open, FileAccess.Read)) + using (StreamReader stream = new StreamReader(file)) { - List list = ser.Deserialize(strm) as List; + List list = list = JsonConvert.DeserializeObject>(stream.ReadToEnd()); if (list != null && list.Count == 0) { removeFiles.Add(file); @@ -115,10 +115,13 @@ namespace HandBrakeWPF.Helpers /// /// The silent Recovery. /// + /// + /// The queue Filter. + /// /// /// The . /// - public static bool RecoverQueue(IQueueProcessor encodeQueue, IErrorService errorService, bool silentRecovery, List queueFilter) + public static bool RecoverQueue(IQueueService encodeQueue, IErrorService errorService, bool silentRecovery, List queueFilter) { string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); List queueFiles = CheckQueueRecovery(queueFilter); @@ -129,8 +132,8 @@ namespace HandBrakeWPF.Helpers { result = errorService.ShowMessageBox( - "HandBrake has detected unfinished items on the queue from the last time the application was launched. Would you like to recover these?", - "Queue Recovery Possible", + Properties.Resources.Queue_RecoverQueueQuestionSingular, + Properties.Resources.Queue_RecoveryPossible, MessageBoxButton.YesNo, MessageBoxImage.Question); } @@ -138,8 +141,8 @@ namespace HandBrakeWPF.Helpers { result = errorService.ShowMessageBox( - "HandBrake has detected multiple unfinished queue files. These will be from multiple instances of HandBrake running. Would you like to recover all unfinished jobs?", - "Queue Recovery Possible", + Properties.Resources.Queue_RecoverQueueQuestionPlural, + Properties.Resources.Queue_RecoveryPossible, MessageBoxButton.YesNo, MessageBoxImage.Question); } @@ -173,7 +176,7 @@ namespace HandBrakeWPF.Helpers { string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); DirectoryInfo info = new DirectoryInfo(appDataPath); - IEnumerable foundFiles = info.GetFiles("*.archive").Where(f => f.Name.StartsWith("hb_queue_recovery")); + IEnumerable foundFiles = info.GetFiles("*.archive").Where(f => f.Name.StartsWith(QueueFileName)); return foundFiles.Any(); } @@ -185,7 +188,7 @@ namespace HandBrakeWPF.Helpers // Remove any files where we have an active instnace. foreach (FileInfo file in foundFiles) { - string fileProcessId = file.Name.Replace("hb_queue_recovery", string.Empty).Replace(".xml", string.Empty); + string fileProcessId = file.Name.Replace(QueueFileName, string.Empty).Replace(".json", string.Empty); int processId; if (!string.IsNullOrEmpty(fileProcessId) && int.TryParse(fileProcessId, out processId)) { @@ -216,7 +219,7 @@ namespace HandBrakeWPF.Helpers // Cleanup old/unused queue files for now. foreach (string file in removeFiles) { - Match m = Regex.Match(file, @"([0-9]+).xml"); + Match m = Regex.Match(file, @"([0-9]+).json"); if (m.Success) { int processId = int.Parse(m.Groups[1].ToString()); @@ -249,13 +252,13 @@ namespace HandBrakeWPF.Helpers { string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); DirectoryInfo info = new DirectoryInfo(appDataPath); - IEnumerable foundFiles = info.GetFiles("*.archive").Where(f => f.Name.StartsWith("hb_queue_recovery")); + IEnumerable foundFiles = info.GetFiles("*.archive").Where(f => f.Name.StartsWith(QueueFileName)); - DateTime LastWeek = DateTime.Now.AddDays(-7); + DateTime lastWeek = DateTime.Now.AddDays(-7); foreach (FileInfo file in foundFiles) { - if (file.CreationTime < LastWeek) + if (file.CreationTime < lastWeek) { string fullPath = Path.Combine(appDataPath, file.Name); File.Delete(fullPath); @@ -267,7 +270,7 @@ namespace HandBrakeWPF.Helpers { string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); DirectoryInfo info = new DirectoryInfo(appDataPath); - IEnumerable foundFiles = info.GetFiles("*.archive").Where(f => f.Name.StartsWith("hb_queue_recovery")); + IEnumerable foundFiles = info.GetFiles("*.archive").Where(f => f.Name.StartsWith(QueueFileName)); foreach (FileInfo file in foundFiles) { string fullPath = Path.Combine(appDataPath, file.Name); diff --git a/win/CS/HandBrakeWPF/Properties/Resources.Designer.cs b/win/CS/HandBrakeWPF/Properties/Resources.Designer.cs index 3e04f632c..a3583ef4a 100644 --- a/win/CS/HandBrakeWPF/Properties/Resources.Designer.cs +++ b/win/CS/HandBrakeWPF/Properties/Resources.Designer.cs @@ -3813,6 +3813,33 @@ namespace HandBrakeWPF.Properties { } } + /// + /// Looks up a localized string similar to HandBrake has detected multiple unfinished queue files. These will be from multiple instances of HandBrake running. Would you like to recover all unfinished jobs?. + /// + public static string Queue_RecoverQueueQuestionPlural { + get { + return ResourceManager.GetString("Queue_RecoverQueueQuestionPlural", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to HandBrake has detected unfinished items on the queue from the last time the application was launched. Would you like to recover these?. + /// + public static string Queue_RecoverQueueQuestionSingular { + get { + return ResourceManager.GetString("Queue_RecoverQueueQuestionSingular", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Queue Recovery Possible. + /// + public static string Queue_RecoveryPossible { + get { + return ResourceManager.GetString("Queue_RecoveryPossible", resourceCulture); + } + } + /// /// Looks up a localized string similar to Unable to reset job status as it is not in an Error or Completed state. /// diff --git a/win/CS/HandBrakeWPF/Properties/Resources.resx b/win/CS/HandBrakeWPF/Properties/Resources.resx index 3f2b0a0b8..66a4cf97f 100644 --- a/win/CS/HandBrakeWPF/Properties/Resources.resx +++ b/win/CS/HandBrakeWPF/Properties/Resources.resx @@ -1987,4 +1987,13 @@ Non-Live Options: {date} {time} {creation-date} {creation-time} {quality} {bitra Choice of encoder will be made available on the 'Video' tab. + + HandBrake has detected multiple unfinished queue files. These will be from multiple instances of HandBrake running. Would you like to recover all unfinished jobs? + + + HandBrake has detected unfinished items on the queue from the last time the application was launched. Would you like to recover these? + + + Queue Recovery Possible + \ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Services/Encode/Model/EncodeTask.cs b/win/CS/HandBrakeWPF/Services/Encode/Model/EncodeTask.cs index 5272810cd..a61cd0bf0 100644 --- a/win/CS/HandBrakeWPF/Services/Encode/Model/EncodeTask.cs +++ b/win/CS/HandBrakeWPF/Services/Encode/Model/EncodeTask.cs @@ -18,6 +18,8 @@ namespace HandBrakeWPF.Services.Encode.Model using HandBrakeWPF.Model.Filters; using HandBrakeWPF.Services.Encode.Model.Models; + using Newtonsoft.Json; + using AllowedPassthru = Models.AllowedPassthru; using AudioTrack = Models.AudioTrack; using ChapterMarker = Models.ChapterMarker; diff --git a/win/CS/HandBrakeWPF/Services/Encode/Model/Models/AudioTrack.cs b/win/CS/HandBrakeWPF/Services/Encode/Model/Models/AudioTrack.cs index cda397929..bab9d242a 100644 --- a/win/CS/HandBrakeWPF/Services/Encode/Model/Models/AudioTrack.cs +++ b/win/CS/HandBrakeWPF/Services/Encode/Model/Models/AudioTrack.cs @@ -27,9 +27,7 @@ namespace HandBrakeWPF.Services.Encode.Model.Models using Newtonsoft.Json; - /// - /// Model of a HandBrake Audio Track and it's associated behaviours. - /// + [JsonObject(MemberSerialization.OptOut)] public class AudioTrack : PropertyChangedBase { private int bitrate; diff --git a/win/CS/HandBrakeWPF/Services/Encode/Model/Models/ChapterMarker.cs b/win/CS/HandBrakeWPF/Services/Encode/Model/Models/ChapterMarker.cs index c699ec39c..4e4e84e0e 100644 --- a/win/CS/HandBrakeWPF/Services/Encode/Model/Models/ChapterMarker.cs +++ b/win/CS/HandBrakeWPF/Services/Encode/Model/Models/ChapterMarker.cs @@ -13,9 +13,9 @@ namespace HandBrakeWPF.Services.Encode.Model.Models using Caliburn.Micro; - /// - /// A Movie Chapter - /// + using Newtonsoft.Json; + + [JsonObject(MemberSerialization.OptOut)] public class ChapterMarker : PropertyChangedBase { /// diff --git a/win/CS/HandBrakeWPF/Services/Encode/Model/Models/SubtitleTrack.cs b/win/CS/HandBrakeWPF/Services/Encode/Model/Models/SubtitleTrack.cs index 3eef3bc4f..79ea7a8b6 100644 --- a/win/CS/HandBrakeWPF/Services/Encode/Model/Models/SubtitleTrack.cs +++ b/win/CS/HandBrakeWPF/Services/Encode/Model/Models/SubtitleTrack.cs @@ -17,9 +17,9 @@ namespace HandBrakeWPF.Services.Encode.Model.Models using HandBrakeWPF.Services.Scan.Model; - /// - /// Subtitle Information - /// + using Newtonsoft.Json; + + [JsonObject(MemberSerialization.OptOut)] public class SubtitleTrack : PropertyChangedBase { #region Constants and Fields diff --git a/win/CS/HandBrakeWPF/Services/PrePostActionService.cs b/win/CS/HandBrakeWPF/Services/PrePostActionService.cs index 4cbd4c82a..20a6c39a8 100644 --- a/win/CS/HandBrakeWPF/Services/PrePostActionService.cs +++ b/win/CS/HandBrakeWPF/Services/PrePostActionService.cs @@ -36,7 +36,7 @@ namespace HandBrakeWPF.Services /// public class PrePostActionService : IPrePostActionService { - private readonly IQueueProcessor queueProcessor; + private readonly IQueueService queueProcessor; private readonly IUserSettingService userSettingService; private readonly IWindowManager windowManager; private readonly IScan scanService; @@ -53,7 +53,7 @@ namespace HandBrakeWPF.Services /// /// The window Manager. /// - public PrePostActionService(IQueueProcessor queueProcessor, IUserSettingService userSettingService, IWindowManager windowManager, IScan scanService) + public PrePostActionService(IQueueService queueProcessor, IUserSettingService userSettingService, IWindowManager windowManager, IScan scanService) { this.queueProcessor = queueProcessor; this.userSettingService = userSettingService; diff --git a/win/CS/HandBrakeWPF/Services/Queue/Interfaces/IQueueProcessor.cs b/win/CS/HandBrakeWPF/Services/Queue/Interfaces/IQueueService.cs similarity index 92% rename from win/CS/HandBrakeWPF/Services/Queue/Interfaces/IQueueProcessor.cs rename to win/CS/HandBrakeWPF/Services/Queue/Interfaces/IQueueService.cs index 90723c7a8..add20a27d 100644 --- a/win/CS/HandBrakeWPF/Services/Queue/Interfaces/IQueueProcessor.cs +++ b/win/CS/HandBrakeWPF/Services/Queue/Interfaces/IQueueService.cs @@ -1,217 +1,217 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. -// -// -// The Queue Processor -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace HandBrakeWPF.Services.Queue.Interfaces -{ - using System; - using System.Collections.ObjectModel; - using System.ComponentModel; - - using HandBrakeWPF.Services.Queue.Model; - - using IEncode = Encode.Interfaces.IEncode; - - /// - /// The Queue Processor - /// - public interface IQueueProcessor - { - #region Events - - /// - /// Fires when the Queue has started - /// - event QueueProcessor.QueueProgressStatus JobProcessingStarted; - - /// - /// Fires when a job is Added, Removed or Re-Ordered. - /// Should be used for triggering an update of the Queue Window. - /// - event EventHandler QueueChanged; - - /// - /// Fires when the entire encode queue has completed. - /// - event QueueProcessor.QueueCompletedEventDelegate QueueCompleted; - - /// - /// Fires when a pause to the encode queue has been requested. - /// - event EventHandler QueuePaused; - - #endregion - - #region Properties - - /// - /// Gets the number of jobs in the queue - /// - int Count { get; } - - /// - /// Gets the number of errors detected in the queue. - /// - int ErrorCount { get; } - - /// - /// Gets the IEncodeService instance. - /// - IEncode EncodeService { get; } - - /// - /// Gets a value indicating whether IsProcessing. - /// - bool IsProcessing { get; } - - /// - /// Gets or sets Last Processed Job. - /// This is set when the job is poped of the queue by GetNextJobForProcessing(); - /// - QueueTask LastProcessedJob { get; set; } - - /// - /// Gets The current queue. - /// - ObservableCollection Queue { get; } - - #endregion - - #region Public Methods - - /// - /// Add a job to the Queue. - /// This method is Thread Safe. - /// - /// - /// The encode Job object. - /// - void Add(QueueTask job); - - /// - /// Backup any changes to the queue file - /// - /// - /// If this is not null or empty, this will be used instead of the standard backup location. - /// - void BackupQueue(string exportPath); - - /// - /// Export the Queue the standardised JSON format. - /// - /// - /// The export path. - /// - void ExportJson(string exportPath); - - /// - /// Restore a JSON queue file. - /// - /// - /// Path to the file the user wishes to import. - /// - void ImportJson(string path); - - /// - /// Checks the current queue for an existing instance of the specified destination. - /// - /// - /// The destination of the encode. - /// - /// - /// Whether or not the supplied destination is already in the queue. - /// - bool CheckForDestinationPathDuplicates(string destination); - - /// - /// Clear down all Queue Items - /// - void Clear(); - - /// - /// Clear down the Queue´s completed items - /// - void ClearCompleted(); - - /// - /// Get the first job on the queue for processing. - /// This also removes the job from the Queue and sets the LastProcessedJob - /// - /// - /// An encode Job object. - /// - QueueTask GetNextJobForProcessing(); - - /// - /// Moves an item down one position in the queue. - /// - /// - /// The zero-based location of the job in the queue. - /// - void MoveDown(int index); - - /// - /// Moves an item up one position in the queue. - /// - /// - /// The zero-based location of the job in the queue. - /// - void MoveUp(int index); - - /// - /// Remove a job from the Queue. - /// This method is Thread Safe - /// - /// - /// The job. - /// - void Remove(QueueTask job); - - /// - /// Reset a Queued Item from Error or Completed to Waiting - /// - /// - /// The job. - /// - void ResetJobStatusToWaiting(QueueTask job); - - /// - /// Restore a Queue from file or from the queue backup file. - /// - /// - /// The import path. String.Empty or null will result in the default file being loaded. - /// - void RestoreQueue(string importPath); - - /// - /// Starts encoding the first job in the queue and continues encoding until all jobs - /// have been encoded. - /// - /// - /// The clear Completed. - /// - void Start(bool clearCompleted); - - /// - /// Stop the current encode and pause the queue. - /// - void Stop(); - - /// - /// Pause the queue but allow the current encode to complete. - /// - void Pause(); - - /// - /// Pause and Encode and the Queue. - /// - void PauseEncode(); - - #endregion - } +// -------------------------------------------------------------------------------------------------------------------- +// +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// +// +// The Queue Processor +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Services.Queue.Interfaces +{ + using System; + using System.Collections.ObjectModel; + using System.ComponentModel; + + using HandBrakeWPF.Services.Queue.Model; + + using IEncode = Encode.Interfaces.IEncode; + + /// + /// The Queue Processor + /// + public interface IQueueService + { + #region Events + + /// + /// Fires when the Queue has started + /// + event QueueService.QueueProgressStatus JobProcessingStarted; + + /// + /// Fires when a job is Added, Removed or Re-Ordered. + /// Should be used for triggering an update of the Queue Window. + /// + event EventHandler QueueChanged; + + /// + /// Fires when the entire encode queue has completed. + /// + event QueueService.QueueCompletedEventDelegate QueueCompleted; + + /// + /// Fires when a pause to the encode queue has been requested. + /// + event EventHandler QueuePaused; + + #endregion + + #region Properties + + /// + /// Gets the number of jobs in the queue + /// + int Count { get; } + + /// + /// Gets the number of errors detected in the queue. + /// + int ErrorCount { get; } + + /// + /// Gets the IEncodeService instance. + /// + IEncode EncodeService { get; } + + /// + /// Gets a value indicating whether IsProcessing. + /// + bool IsProcessing { get; } + + /// + /// Gets or sets Last Processed Job. + /// This is set when the job is poped of the queue by GetNextJobForProcessing(); + /// + QueueTask LastProcessedJob { get; set; } + + /// + /// Gets The current queue. + /// + ObservableCollection Queue { get; } + + #endregion + + #region Public Methods + + /// + /// Add a job to the Queue. + /// This method is Thread Safe. + /// + /// + /// The encode Job object. + /// + void Add(QueueTask job); + + /// + /// Backup any changes to the queue file + /// + /// + /// If this is not null or empty, this will be used instead of the standard backup location. + /// + void BackupQueue(string exportPath); + + /// + /// Export the Queue the standardised JSON format. + /// + /// + /// The export path. + /// + void ExportJson(string exportPath); + + /// + /// Restore a JSON queue file. + /// + /// + /// Path to the file the user wishes to import. + /// + void ImportJson(string path); + + /// + /// Checks the current queue for an existing instance of the specified destination. + /// + /// + /// The destination of the encode. + /// + /// + /// Whether or not the supplied destination is already in the queue. + /// + bool CheckForDestinationPathDuplicates(string destination); + + /// + /// Clear down all Queue Items + /// + void Clear(); + + /// + /// Clear down the Queue´s completed items + /// + void ClearCompleted(); + + /// + /// Get the first job on the queue for processing. + /// This also removes the job from the Queue and sets the LastProcessedJob + /// + /// + /// An encode Job object. + /// + QueueTask GetNextJobForProcessing(); + + /// + /// Moves an item down one position in the queue. + /// + /// + /// The zero-based location of the job in the queue. + /// + void MoveDown(int index); + + /// + /// Moves an item up one position in the queue. + /// + /// + /// The zero-based location of the job in the queue. + /// + void MoveUp(int index); + + /// + /// Remove a job from the Queue. + /// This method is Thread Safe + /// + /// + /// The job. + /// + void Remove(QueueTask job); + + /// + /// Reset a Queued Item from Error or Completed to Waiting + /// + /// + /// The job. + /// + void ResetJobStatusToWaiting(QueueTask job); + + /// + /// Restore a Queue from file or from the queue backup file. + /// + /// + /// The import path. String.Empty or null will result in the default file being loaded. + /// + void RestoreQueue(string importPath); + + /// + /// Starts encoding the first job in the queue and continues encoding until all jobs + /// have been encoded. + /// + /// + /// The clear Completed. + /// + void Start(bool clearCompleted); + + /// + /// Stop the current encode and pause the queue. + /// + void Stop(); + + /// + /// Pause the queue but allow the current encode to complete. + /// + void Pause(); + + /// + /// Pause and Encode and the Queue. + /// + void PauseEncode(); + + #endregion + } } \ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Services/Queue/Model/QueueStats.cs b/win/CS/HandBrakeWPF/Services/Queue/Model/QueueStats.cs index 77454c4ad..3378bd6aa 100644 --- a/win/CS/HandBrakeWPF/Services/Queue/Model/QueueStats.cs +++ b/win/CS/HandBrakeWPF/Services/Queue/Model/QueueStats.cs @@ -13,6 +13,9 @@ namespace HandBrakeWPF.Services.Queue.Model using Caliburn.Micro; + using Newtonsoft.Json; + + [JsonObject(MemberSerialization.OptOut)] public class QueueStats : PropertyChangedBase { private DateTime startTime; diff --git a/win/CS/HandBrakeWPF/Services/Queue/Model/QueueTask.cs b/win/CS/HandBrakeWPF/Services/Queue/Model/QueueTask.cs index 77c9b66ad..f3d7bb6e4 100644 --- a/win/CS/HandBrakeWPF/Services/Queue/Model/QueueTask.cs +++ b/win/CS/HandBrakeWPF/Services/Queue/Model/QueueTask.cs @@ -18,22 +18,17 @@ namespace HandBrakeWPF.Services.Queue.Model using HandBrakeWPF.Services.Presets.Model; using HandBrakeWPF.Utilities; + using Newtonsoft.Json; + using EncodeTask = HandBrakeWPF.Services.Encode.Model.EncodeTask; - /// - /// The QueueTask. - /// + public class QueueTask : PropertyChangedBase { private static int id; private QueueItemStatus status; private string presetKey; - #region Properties - - /// - /// Initializes a new instance of the class. - /// public QueueTask() { this.Status = QueueItemStatus.Waiting; @@ -42,21 +37,6 @@ namespace HandBrakeWPF.Services.Queue.Model this.Statistics = new QueueStats(); } - /// - /// Initializes a new instance of the class. - /// - /// - /// The task. - /// - /// - /// The configuration. - /// - /// - /// The scanned Source Path. - /// - /// - /// The currently active preset. - /// public QueueTask(EncodeTask task, HBConfiguration configuration, string scannedSourcePath, Preset currentPreset) { this.Task = task; @@ -74,16 +54,13 @@ namespace HandBrakeWPF.Services.Queue.Model this.Statistics = new QueueStats(); } + [JsonIgnore] public string Id { get; } - /// - /// Gets or sets ScannedSource. - /// + [JsonProperty] public string ScannedSourcePath { get; set; } - /// - /// Gets or sets Status. - /// + [JsonProperty] public QueueItemStatus Status { get @@ -99,18 +76,16 @@ namespace HandBrakeWPF.Services.Queue.Model } } - /// - /// Gets or sets the task. - /// + [JsonProperty] public EncodeTask Task { get; set; } - /// - /// Gets or sets the configuration. - /// + [JsonProperty] public HBConfiguration Configuration { get; set; } + [JsonProperty] public QueueStats Statistics { get; set; } + [JsonIgnore] public string SelectedPresetKey { get @@ -119,15 +94,9 @@ namespace HandBrakeWPF.Services.Queue.Model } } + [JsonIgnore] public bool ShowEncodeProgress => this.Status == QueueItemStatus.InProgress && SystemInfo.IsWindows10(); - #endregion - - protected bool Equals(QueueTask other) - { - return this.Id == other.Id; - } - public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; @@ -145,5 +114,10 @@ namespace HandBrakeWPF.Services.Queue.Model { return string.Format("Encode Task. Title: {0}, Source: {1}, Destination: {2}", this.Task.Title, this.Task.Source, this.Task.Destination); } + + protected bool Equals(QueueTask other) + { + return this.Id == other.Id; + } } } \ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Services/Queue/QueueProcessor.cs b/win/CS/HandBrakeWPF/Services/Queue/QueueService.cs similarity index 87% rename from win/CS/HandBrakeWPF/Services/Queue/QueueProcessor.cs rename to win/CS/HandBrakeWPF/Services/Queue/QueueService.cs index ec0867f2a..0a9c6e8e9 100644 --- a/win/CS/HandBrakeWPF/Services/Queue/QueueProcessor.cs +++ b/win/CS/HandBrakeWPF/Services/Queue/QueueService.cs @@ -1,702 +1,699 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. -// -// -// The HandBrake Queue -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace HandBrakeWPF.Services.Queue -{ - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.ComponentModel; - using System.IO; - using System.Linq; - using System.Xml.Serialization; - - using HandBrake.Interop.Interop.Json.Queue; - using HandBrake.Interop.Model; - using HandBrake.Interop.Utilities; - - using HandBrakeWPF.Factories; - using HandBrakeWPF.Properties; - using HandBrakeWPF.Services.Encode.Factories; - using HandBrakeWPF.Services.Encode.Model; - using HandBrakeWPF.Services.Interfaces; - using HandBrakeWPF.Services.Queue.Model; - using HandBrakeWPF.Utilities; - - using EncodeCompletedEventArgs = HandBrakeWPF.Services.Encode.EventArgs.EncodeCompletedEventArgs; - using Execute = Caliburn.Micro.Execute; - using GeneralApplicationException = HandBrakeWPF.Exceptions.GeneralApplicationException; - using IEncode = HandBrakeWPF.Services.Encode.Interfaces.IEncode; - using LogLevel = HandBrakeWPF.Services.Logging.Model.LogLevel; - using LogMessageType = HandBrakeWPF.Services.Logging.Model.LogMessageType; - using LogService = HandBrakeWPF.Services.Logging.LogService; - using QueueCompletedEventArgs = HandBrakeWPF.EventArgs.QueueCompletedEventArgs; - using QueueProgressEventArgs = HandBrakeWPF.EventArgs.QueueProgressEventArgs; - - /// - /// The HandBrake Queue - /// - public class QueueProcessor : Interfaces.IQueueProcessor - { - #region Constants and Fields - private static readonly object QueueLock = new object(); - private readonly IUserSettingService userSettingService; - private readonly ObservableCollection queue = new ObservableCollection(); - private readonly string queueFile; - private bool clearCompleted; - - #endregion - - #region Constructors and Destructors - - /// - /// Initializes a new instance of the class. - /// - /// - /// The encode Service. - /// - /// - /// The user settings service. - /// - /// - /// The Error Service. - /// - /// - /// Services are not setup - /// - public QueueProcessor(IEncode encodeService, IUserSettingService userSettingService) - { - this.userSettingService = userSettingService; - this.EncodeService = encodeService; - - // If this is the first instance, just use the main queue file, otherwise add the instance id to the filename. - this.queueFile = string.Format("hb_queue_recovery{0}.xml", GeneralUtilities.ProcessId); - } - - #endregion - - #region Delegates - - /// - /// Queue Progress Status - /// - /// - /// The sender. - /// - /// - /// The QueueProgressEventArgs. - /// - public delegate void QueueProgressStatus(object sender, QueueProgressEventArgs e); - - /// - /// The queue completed. - /// - /// - /// The sender. - /// - /// - /// The e. - /// - public delegate void QueueCompletedEventDelegate(object sender, QueueCompletedEventArgs e); - - #endregion - - #region Events - - /// - /// Fires when the Queue has started - /// - public event QueueProgressStatus JobProcessingStarted; - - /// - /// Fires when a job is Added, Removed or Re-Ordered. - /// Should be used for triggering an update of the Queue Window. - /// - public event EventHandler QueueChanged; - - /// - /// Fires when the entire encode queue has completed. - /// - public event QueueCompletedEventDelegate QueueCompleted; - - /// - /// Fires when a pause to the encode queue has been requested. - /// - public event EventHandler QueuePaused; - - #endregion - - #region Properties - - /// - /// Gets the number of jobs in the queue; - /// - public int Count - { - get - { - return this.queue.Count(item => item.Status == QueueItemStatus.Waiting); - } - } - - /// - /// The number of errors detected. - /// - public int ErrorCount - { - get - { - return this.queue.Count(item => item.Status == QueueItemStatus.Error); - } - } - - /// - /// Gets the IEncodeService instance. - /// - public IEncode EncodeService { get; private set; } - - /// - /// Gets a value indicating whether IsProcessing. - /// - public bool IsProcessing { get; private set; } - - /// - /// Gets or sets Last Processed Job. - /// This is set when the job is poped of the queue by GetNextJobForProcessing(); - /// - public QueueTask LastProcessedJob { get; set; } - - /// - /// Gets The current queue. - /// - public ObservableCollection Queue - { - get - { - return this.queue; - } - } - - #endregion - - #region Public Methods - - /// - /// Add a job to the Queue. - /// This method is Thread Safe. - /// - /// - /// The encode Job object. - /// - public void Add(QueueTask job) - { - lock (QueueLock) - { - this.queue.Add(job); - this.InvokeQueueChanged(EventArgs.Empty); - } - } - - /// - /// Backup any changes to the queue file - /// - /// - /// If this is not null or empty, this will be used instead of the standard backup location. - /// - public void BackupQueue(string exportPath) - { - string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); - string tempPath = !string.IsNullOrEmpty(exportPath) - ? exportPath - : Path.Combine(appDataPath, string.Format(this.queueFile, string.Empty)); - - using (var strm = new FileStream(tempPath, FileMode.Create, FileAccess.Write)) - { - List tasks = this.queue.Where(item => item.Status != QueueItemStatus.Completed).ToList(); - var serializer = new XmlSerializer(typeof(List)); - serializer.Serialize(strm, tasks); - strm.Close(); - strm.Dispose(); - } - } - - public void ExportJson(string exportPath) - { - List jobs = this.queue.Where(item => item.Status != QueueItemStatus.Completed).ToList(); - List workUnits = jobs.Select(job => job.Task).ToList(); - HBConfiguration config = HBConfigurationFactory.Create(); // Default to current settings for now. These will hopefully go away in the future. - - string json = QueueFactory.GetQueueJson(workUnits, config); - - using (var strm = new StreamWriter(exportPath, false)) - { - strm.Write(json); - strm.Close(); - strm.Dispose(); - } - } - - public void ImportJson(string path) - { - List tasks; - using (StreamReader reader = new StreamReader(path)) - { - string fileContent = reader.ReadToEnd(); - tasks = QueueFactory.GetQueue(fileContent); - - if (tasks != null) - { - foreach (Task task in tasks) - { - // TODO flesh out. - EncodeTask encodeTask = EncodeTaskImportFactory.Create(task.Job); - QueueTask queueTask = new QueueTask(); - queueTask.Task = encodeTask; - - this.queue.Add(queueTask); - } - } - } - } - - /// - /// Checks the current queue for an existing instance of the specified destination. - /// - /// - /// The destination of the encode. - /// - /// - /// Whether or not the supplied destination is already in the queue. - /// - public bool CheckForDestinationPathDuplicates(string destination) - { - foreach (QueueTask job in this.queue) - { - if (string.Equals( - job.Task.Destination, - destination.Replace("\\\\", "\\"), - StringComparison.OrdinalIgnoreCase) && (job.Status == QueueItemStatus.Waiting || job.Status == QueueItemStatus.InProgress)) - { - return true; - } - } - - return false; - } - - /// - /// Clear down all Queue Items - /// - public void Clear() - { - List deleteList = this.queue.ToList(); - foreach (QueueTask item in deleteList) - { - this.queue.Remove(item); - } - this.InvokeQueueChanged(EventArgs.Empty); - } - - /// - /// Clear down the Queue´s completed items - /// - public void ClearCompleted() - { - Execute.OnUIThread( - () => - { - List deleteList = - this.queue.Where(task => task.Status == QueueItemStatus.Completed).ToList(); - foreach (QueueTask item in deleteList) - { - this.queue.Remove(item); - } - this.InvokeQueueChanged(EventArgs.Empty); - }); - } - - /// - /// Get the first job on the queue for processing. - /// This also removes the job from the Queue and sets the LastProcessedJob - /// - /// - /// An encode Job object. - /// - public QueueTask GetNextJobForProcessing() - { - if (this.queue.Count > 0) - { - return this.queue.FirstOrDefault(q => q.Status == QueueItemStatus.Waiting); - } - - return null; - } - - /// - /// Moves an item down one position in the queue. - /// - /// - /// The zero-based location of the job in the queue. - /// - public void MoveDown(int index) - { - if (index < this.queue.Count - 1) - { - QueueTask item = this.queue[index]; - - this.queue.RemoveAt(index); - this.queue.Insert((index + 1), item); - } - - this.InvokeQueueChanged(EventArgs.Empty); - } - - /// - /// Moves an item up one position in the queue. - /// - /// - /// The zero-based location of the job in the queue. - /// - public void MoveUp(int index) - { - if (index > 0) - { - QueueTask item = this.queue[index]; - - this.queue.RemoveAt(index); - this.queue.Insert((index - 1), item); - } - - this.InvokeQueueChanged(EventArgs.Empty); - } - - /// - /// Remove a job from the Queue. - /// This method is Thread Safe - /// - /// - /// The job. - /// - public void Remove(QueueTask job) - { - lock (QueueLock) - { - this.queue.Remove(job); - this.InvokeQueueChanged(EventArgs.Empty); - } - } - - /// - /// Reset a Queued Item from Error or Completed to Waiting - /// - /// - /// The job. - /// - public void ResetJobStatusToWaiting(QueueTask job) - { - if (job.Status != QueueItemStatus.Error && job.Status != QueueItemStatus.Completed) - { - throw new GeneralApplicationException( - Resources.Error, Resources.Queue_UnableToResetJob, null); - } - - job.Status = QueueItemStatus.Waiting; - } - - /// - /// Restore a Queue from file or from the queue backup file. - /// - /// - /// The import path. String.Empty or null will result in the default file being loaded. - /// - public void RestoreQueue(string importPath) - { - string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); - string tempPath = !string.IsNullOrEmpty(importPath) - ? importPath - : (appDataPath + string.Format(this.queueFile, string.Empty)); - - if (File.Exists(tempPath)) - { - bool invokeUpdate = false; - using ( - var strm = new FileStream( - (!string.IsNullOrEmpty(importPath) ? importPath : tempPath), FileMode.Open, FileAccess.Read)) - { - if (strm.Length != 0) - { - var serializer = new XmlSerializer(typeof(List)); - - List list; - - try - { - list = serializer.Deserialize(strm) as List; - } - catch (Exception exc) - { - throw new GeneralApplicationException(Resources.Queue_UnableToRestoreFile, Resources.Queue_UnableToRestoreFileExtended, exc); - } - - if (list != null) - { - foreach (QueueTask item in list) - { - if (item.Status != QueueItemStatus.Completed) - { - // Reset InProgress/Error to Waiting so it can be processed - if (item.Status == QueueItemStatus.InProgress) - { - item.Status = QueueItemStatus.Error; - } - - this.queue.Add(item); - } - } - } - - invokeUpdate = true; - } - } - - if (invokeUpdate) - { - this.InvokeQueueChanged(EventArgs.Empty); - } - } - } - - /// - /// Requests a pause of the encode queue. - /// - public void Pause() - { - this.IsProcessing = false; - this.InvokeQueuePaused(EventArgs.Empty); - } - - public void PauseEncode() - { - if (this.EncodeService.IsEncoding && !this.EncodeService.IsPasued) - { - this.EncodeService.Pause(); - this.LastProcessedJob.Statistics.SetPaused(true); - } - - this.Pause(); - } - - /// - /// Starts encoding the first job in the queue and continues encoding until all jobs - /// have been encoded. - /// - /// - /// The is Clear Completed. - /// - public void Start(bool isClearCompleted) - { - if (this.IsProcessing) - { - return; - } - - this.clearCompleted = isClearCompleted; - - this.EncodeService.EncodeCompleted -= this.EncodeServiceEncodeCompleted; - this.EncodeService.EncodeCompleted += this.EncodeServiceEncodeCompleted; - - if (this.EncodeService.IsPasued) - { - this.EncodeService.Resume(); - this.IsProcessing = true; - this.InvokeJobProcessingStarted(new QueueProgressEventArgs(this.LastProcessedJob)); - this.LastProcessedJob.Statistics.SetPaused(false); - } - - if (!this.EncodeService.IsEncoding) - { - this.ProcessNextJob(); - } - } - - public void Stop() - { - if (this.EncodeService.IsEncoding) - { - this.EncodeService.Stop(); - } - this.IsProcessing = false; - this.InvokeQueuePaused(EventArgs.Empty); - } - - #endregion - - #region Methods - - /// - /// The on queue completed. - /// - /// - /// The e. - /// - protected virtual void OnQueueCompleted(QueueCompletedEventArgs e) - { - QueueCompletedEventDelegate handler = this.QueueCompleted; - if (handler != null) - { - handler(this, e); - } - - this.IsProcessing = false; - } - - /// - /// After an encode is complete, move onto the next job. - /// - /// - /// The sender. - /// - /// - /// The EncodeCompletedEventArgs. - /// - private void EncodeServiceEncodeCompleted(object sender, EncodeCompletedEventArgs e) - { - this.LastProcessedJob.Status = QueueItemStatus.Completed; - this.LastProcessedJob.Statistics.EndTime = DateTime.Now; - this.LastProcessedJob.Statistics.CompletedActivityLogPath = e.ActivityLogPath; - this.LastProcessedJob.Statistics.FinalFileSize = e.FinalFilesizeInBytes; - - // Clear the completed item of the queue if the setting is set. - if (this.clearCompleted) - { - this.ClearCompleted(); - } - - if (!e.Successful) - { - this.LastProcessedJob.Status = QueueItemStatus.Error; - } - - // Move onto the next job. - if (this.IsProcessing) - { - this.ProcessNextJob(); - } - else - { - this.EncodeService.EncodeCompleted -= this.EncodeServiceEncodeCompleted; - this.BackupQueue(string.Empty); - this.OnQueueCompleted(new QueueCompletedEventArgs(true)); - } - } - - /// - /// Invoke the JobProcessingStarted event - /// - /// - /// The QueueProgressEventArgs. - /// - private void InvokeJobProcessingStarted(QueueProgressEventArgs e) - { - QueueProgressStatus handler = this.JobProcessingStarted; - if (handler != null) - { - handler(this, e); - } - } - - /// - /// Invoke the Queue Changed Event - /// - /// - /// The e. - /// - private void InvokeQueueChanged(EventArgs e) - { - try - { - this.BackupQueue(string.Empty); - } - catch (Exception) - { - // Do Nothing. - } - - EventHandler handler = this.QueueChanged; - if (handler != null) - { - handler(this, e); - } - } - - /// - /// Invoke the QueuePaused event - /// - /// - /// The EventArgs. - /// - private void InvokeQueuePaused(EventArgs e) - { - this.IsProcessing = false; - - EventHandler handler = this.QueuePaused; - if (handler != null) - { - handler(this, e); - } - } - - /// - /// Run through all the jobs on the queue. - /// - private void ProcessNextJob() - { - QueueTask job = this.GetNextJobForProcessing(); - if (job != null) - { - if (this.userSettingService.GetUserSetting(UserSettingConstants.PauseOnLowDiskspace) && !DriveUtilities.HasMinimumDiskSpace(job.Task.Destination, this.userSettingService.GetUserSetting(UserSettingConstants.PauseOnLowDiskspaceLevel))) - { - LogService.GetLogger().LogMessage(Resources.PauseOnLowDiskspace, LogMessageType.ScanOrEncode, LogLevel.Info); - job.Status = QueueItemStatus.Waiting; - this.Pause(); - this.BackupQueue(string.Empty); - return; // Don't start the next job. - } - - job.Status = QueueItemStatus.InProgress; - job.Statistics.StartTime = DateTime.Now; - this.LastProcessedJob = job; - this.IsProcessing = true; - this.InvokeQueueChanged(EventArgs.Empty); - this.InvokeJobProcessingStarted(new QueueProgressEventArgs(job)); - - if (!Directory.Exists(Path.GetDirectoryName(job.Task.Destination))) - { - this.EncodeServiceEncodeCompleted(null, new EncodeCompletedEventArgs(false, null, "Destination Directory Missing", null, null, 0)); - this.BackupQueue(string.Empty); - return; - } - this.EncodeService.Start(job.Task, job.Configuration); - this.BackupQueue(string.Empty); - } - else - { - // No more jobs to process, so unsubscribe the event - this.EncodeService.EncodeCompleted -= this.EncodeServiceEncodeCompleted; - - this.BackupQueue(string.Empty); - - // Fire the event to tell connected services. - this.OnQueueCompleted(new QueueCompletedEventArgs(false)); - } - } - - #endregion - } +// -------------------------------------------------------------------------------------------------------------------- +// +// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. +// +// +// The HandBrake Queue +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace HandBrakeWPF.Services.Queue +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.ComponentModel; + using System.Diagnostics; + using System.IO; + using System.Linq; + + using HandBrake.Interop.Interop.Json.Queue; + using HandBrake.Interop.Model; + using HandBrake.Interop.Utilities; + + using HandBrakeWPF.Factories; + using HandBrakeWPF.Helpers; + using HandBrakeWPF.Properties; + using HandBrakeWPF.Services.Encode.Factories; + using HandBrakeWPF.Services.Encode.Model; + using HandBrakeWPF.Services.Interfaces; + using HandBrakeWPF.Services.Queue.Model; + using HandBrakeWPF.Utilities; + + using Newtonsoft.Json; + + using EncodeCompletedEventArgs = HandBrakeWPF.Services.Encode.EventArgs.EncodeCompletedEventArgs; + using Execute = Caliburn.Micro.Execute; + using GeneralApplicationException = HandBrakeWPF.Exceptions.GeneralApplicationException; + using IEncode = HandBrakeWPF.Services.Encode.Interfaces.IEncode; + using LogLevel = HandBrakeWPF.Services.Logging.Model.LogLevel; + using LogMessageType = HandBrakeWPF.Services.Logging.Model.LogMessageType; + using LogService = HandBrakeWPF.Services.Logging.LogService; + using QueueCompletedEventArgs = HandBrakeWPF.EventArgs.QueueCompletedEventArgs; + using QueueProgressEventArgs = HandBrakeWPF.EventArgs.QueueProgressEventArgs; + + /// + /// The HandBrake Queue + /// + public class QueueService : Interfaces.IQueueService + { + #region Constants and Fields + private static readonly object QueueLock = new object(); + private readonly IUserSettingService userSettingService; + private readonly ObservableCollection queue = new ObservableCollection(); + private readonly string queueFile; + private bool clearCompleted; + + #endregion + + #region Constructors and Destructors + + /// + /// Initializes a new instance of the class. + /// + /// + /// The encode Service. + /// + /// + /// The user settings service. + /// + /// + /// Services are not setup + /// + public QueueService(IEncode encodeService, IUserSettingService userSettingService) + { + this.userSettingService = userSettingService; + this.EncodeService = encodeService; + + // If this is the first instance, just use the main queue file, otherwise add the instance id to the filename. + this.queueFile = string.Format("{0}{1}.json", QueueRecoveryHelper.QueueFileName, GeneralUtilities.ProcessId); + } + + #endregion + + #region Delegates + + /// + /// Queue Progress Status + /// + /// + /// The sender. + /// + /// + /// The QueueProgressEventArgs. + /// + public delegate void QueueProgressStatus(object sender, QueueProgressEventArgs e); + + /// + /// The queue completed. + /// + /// + /// The sender. + /// + /// + /// The e. + /// + public delegate void QueueCompletedEventDelegate(object sender, QueueCompletedEventArgs e); + + #endregion + + #region Events + + /// + /// Fires when the Queue has started + /// + public event QueueProgressStatus JobProcessingStarted; + + /// + /// Fires when a job is Added, Removed or Re-Ordered. + /// Should be used for triggering an update of the Queue Window. + /// + public event EventHandler QueueChanged; + + /// + /// Fires when the entire encode queue has completed. + /// + public event QueueCompletedEventDelegate QueueCompleted; + + /// + /// Fires when a pause to the encode queue has been requested. + /// + public event EventHandler QueuePaused; + + #endregion + + #region Properties + + /// + /// Gets the number of jobs in the queue; + /// + public int Count + { + get + { + return this.queue.Count(item => item.Status == QueueItemStatus.Waiting); + } + } + + /// + /// The number of errors detected. + /// + public int ErrorCount + { + get + { + return this.queue.Count(item => item.Status == QueueItemStatus.Error); + } + } + + /// + /// Gets the IEncodeService instance. + /// + public IEncode EncodeService { get; private set; } + + /// + /// Gets a value indicating whether IsProcessing. + /// + public bool IsProcessing { get; private set; } + + /// + /// Gets or sets Last Processed Job. + /// This is set when the job is poped of the queue by GetNextJobForProcessing(); + /// + public QueueTask LastProcessedJob { get; set; } + + /// + /// Gets The current queue. + /// + public ObservableCollection Queue + { + get + { + return this.queue; + } + } + + #endregion + + #region Public Methods + + /// + /// Add a job to the Queue. + /// This method is Thread Safe. + /// + /// + /// The encode Job object. + /// + public void Add(QueueTask job) + { + lock (QueueLock) + { + this.queue.Add(job); + this.InvokeQueueChanged(EventArgs.Empty); + } + } + + /// + /// Backup any changes to the queue file + /// + /// + /// If this is not null or empty, this will be used instead of the standard backup location. + /// + public void BackupQueue(string exportPath) + { + Stopwatch watch = Stopwatch.StartNew(); + + string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); + string tempPath = !string.IsNullOrEmpty(exportPath) + ? exportPath + : Path.Combine(appDataPath, string.Format(this.queueFile, string.Empty)); + + using (StreamWriter writer = new StreamWriter(tempPath)) + { + List tasks = this.queue.Where(item => item.Status != QueueItemStatus.Completed).ToList(); + string queueJson = JsonConvert.SerializeObject(tasks, Formatting.Indented); + writer.Write(queueJson); + } + + watch.Stop(); + Debug.WriteLine("Queue Save (ms): " + watch.ElapsedMilliseconds); + } + + public void ExportJson(string exportPath) + { + List jobs = this.queue.Where(item => item.Status != QueueItemStatus.Completed).ToList(); + List workUnits = jobs.Select(job => job.Task).ToList(); + HBConfiguration config = HBConfigurationFactory.Create(); // Default to current settings for now. These will hopefully go away in the future. + + string json = QueueFactory.GetQueueJson(workUnits, config); + + using (var strm = new StreamWriter(exportPath, false)) + { + strm.Write(json); + strm.Close(); + strm.Dispose(); + } + } + + public void ImportJson(string path) + { + List tasks; + using (StreamReader reader = new StreamReader(path)) + { + string fileContent = reader.ReadToEnd(); + tasks = QueueFactory.GetQueue(fileContent); + + if (tasks != null) + { + foreach (Task task in tasks) + { + // TODO flesh out. + EncodeTask encodeTask = EncodeTaskImportFactory.Create(task.Job); + QueueTask queueTask = new QueueTask(); + queueTask.Task = encodeTask; + + this.queue.Add(queueTask); + } + } + } + } + + /// + /// Checks the current queue for an existing instance of the specified destination. + /// + /// + /// The destination of the encode. + /// + /// + /// Whether or not the supplied destination is already in the queue. + /// + public bool CheckForDestinationPathDuplicates(string destination) + { + foreach (QueueTask job in this.queue) + { + if (string.Equals( + job.Task.Destination, + destination.Replace("\\\\", "\\"), + StringComparison.OrdinalIgnoreCase) && (job.Status == QueueItemStatus.Waiting || job.Status == QueueItemStatus.InProgress)) + { + return true; + } + } + + return false; + } + + /// + /// Clear down all Queue Items + /// + public void Clear() + { + List deleteList = this.queue.ToList(); + foreach (QueueTask item in deleteList) + { + this.queue.Remove(item); + } + this.InvokeQueueChanged(EventArgs.Empty); + } + + /// + /// Clear down the Queue´s completed items + /// + public void ClearCompleted() + { + Execute.OnUIThread( + () => + { + List deleteList = + this.queue.Where(task => task.Status == QueueItemStatus.Completed).ToList(); + foreach (QueueTask item in deleteList) + { + this.queue.Remove(item); + } + this.InvokeQueueChanged(EventArgs.Empty); + }); + } + + /// + /// Get the first job on the queue for processing. + /// This also removes the job from the Queue and sets the LastProcessedJob + /// + /// + /// An encode Job object. + /// + public QueueTask GetNextJobForProcessing() + { + if (this.queue.Count > 0) + { + return this.queue.FirstOrDefault(q => q.Status == QueueItemStatus.Waiting); + } + + return null; + } + + /// + /// Moves an item down one position in the queue. + /// + /// + /// The zero-based location of the job in the queue. + /// + public void MoveDown(int index) + { + if (index < this.queue.Count - 1) + { + QueueTask item = this.queue[index]; + + this.queue.RemoveAt(index); + this.queue.Insert((index + 1), item); + } + + this.InvokeQueueChanged(EventArgs.Empty); + } + + /// + /// Moves an item up one position in the queue. + /// + /// + /// The zero-based location of the job in the queue. + /// + public void MoveUp(int index) + { + if (index > 0) + { + QueueTask item = this.queue[index]; + + this.queue.RemoveAt(index); + this.queue.Insert((index - 1), item); + } + + this.InvokeQueueChanged(EventArgs.Empty); + } + + /// + /// Remove a job from the Queue. + /// This method is Thread Safe + /// + /// + /// The job. + /// + public void Remove(QueueTask job) + { + lock (QueueLock) + { + this.queue.Remove(job); + this.InvokeQueueChanged(EventArgs.Empty); + } + } + + /// + /// Reset a Queued Item from Error or Completed to Waiting + /// + /// + /// The job. + /// + public void ResetJobStatusToWaiting(QueueTask job) + { + if (job.Status != QueueItemStatus.Error && job.Status != QueueItemStatus.Completed) + { + throw new GeneralApplicationException( + Resources.Error, Resources.Queue_UnableToResetJob, null); + } + + job.Status = QueueItemStatus.Waiting; + } + + /// + /// Restore a Queue from file or from the queue backup file. + /// + /// + /// The import path. String.Empty or null will result in the default file being loaded. + /// + public void RestoreQueue(string importPath) + { + string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); + string tempPath = !string.IsNullOrEmpty(importPath) + ? importPath + : (appDataPath + string.Format(this.queueFile, string.Empty)); + + if (File.Exists(tempPath)) + { + bool invokeUpdate = false; + using (StreamReader stream = new StreamReader(!string.IsNullOrEmpty(importPath) ? importPath : tempPath)) + { + string queueJson = stream.ReadToEnd(); + List list; + + try + { + list = JsonConvert.DeserializeObject>(queueJson); + } + catch (Exception exc) + { + throw new GeneralApplicationException(Resources.Queue_UnableToRestoreFile, Resources.Queue_UnableToRestoreFileExtended, exc); + } + + if (list != null) + { + foreach (QueueTask item in list) + { + if (item.Status != QueueItemStatus.Completed) + { + // Reset InProgress/Error to Waiting so it can be processed + if (item.Status == QueueItemStatus.InProgress) + { + item.Status = QueueItemStatus.Error; + } + + this.queue.Add(item); + } + } + } + + invokeUpdate = true; + } + + if (invokeUpdate) + { + this.InvokeQueueChanged(EventArgs.Empty); + } + } + } + + /// + /// Requests a pause of the encode queue. + /// + public void Pause() + { + this.IsProcessing = false; + this.InvokeQueuePaused(EventArgs.Empty); + } + + public void PauseEncode() + { + if (this.EncodeService.IsEncoding && !this.EncodeService.IsPasued) + { + this.EncodeService.Pause(); + this.LastProcessedJob.Statistics.SetPaused(true); + } + + this.Pause(); + } + + /// + /// Starts encoding the first job in the queue and continues encoding until all jobs + /// have been encoded. + /// + /// + /// The is Clear Completed. + /// + public void Start(bool isClearCompleted) + { + if (this.IsProcessing) + { + return; + } + + this.clearCompleted = isClearCompleted; + + this.EncodeService.EncodeCompleted -= this.EncodeServiceEncodeCompleted; + this.EncodeService.EncodeCompleted += this.EncodeServiceEncodeCompleted; + + if (this.EncodeService.IsPasued) + { + this.EncodeService.Resume(); + this.IsProcessing = true; + this.InvokeJobProcessingStarted(new QueueProgressEventArgs(this.LastProcessedJob)); + this.LastProcessedJob.Statistics.SetPaused(false); + } + + if (!this.EncodeService.IsEncoding) + { + this.ProcessNextJob(); + } + } + + public void Stop() + { + if (this.EncodeService.IsEncoding) + { + this.EncodeService.Stop(); + } + this.IsProcessing = false; + this.InvokeQueuePaused(EventArgs.Empty); + } + + #endregion + + #region Methods + + /// + /// The on queue completed. + /// + /// + /// The e. + /// + protected virtual void OnQueueCompleted(QueueCompletedEventArgs e) + { + QueueCompletedEventDelegate handler = this.QueueCompleted; + if (handler != null) + { + handler(this, e); + } + + this.IsProcessing = false; + } + + /// + /// After an encode is complete, move onto the next job. + /// + /// + /// The sender. + /// + /// + /// The EncodeCompletedEventArgs. + /// + private void EncodeServiceEncodeCompleted(object sender, EncodeCompletedEventArgs e) + { + this.LastProcessedJob.Status = QueueItemStatus.Completed; + this.LastProcessedJob.Statistics.EndTime = DateTime.Now; + this.LastProcessedJob.Statistics.CompletedActivityLogPath = e.ActivityLogPath; + this.LastProcessedJob.Statistics.FinalFileSize = e.FinalFilesizeInBytes; + + // Clear the completed item of the queue if the setting is set. + if (this.clearCompleted) + { + this.ClearCompleted(); + } + + if (!e.Successful) + { + this.LastProcessedJob.Status = QueueItemStatus.Error; + } + + // Move onto the next job. + if (this.IsProcessing) + { + this.ProcessNextJob(); + } + else + { + this.EncodeService.EncodeCompleted -= this.EncodeServiceEncodeCompleted; + this.BackupQueue(string.Empty); + this.OnQueueCompleted(new QueueCompletedEventArgs(true)); + } + } + + /// + /// Invoke the JobProcessingStarted event + /// + /// + /// The QueueProgressEventArgs. + /// + private void InvokeJobProcessingStarted(QueueProgressEventArgs e) + { + QueueProgressStatus handler = this.JobProcessingStarted; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Invoke the Queue Changed Event + /// + /// + /// The e. + /// + private void InvokeQueueChanged(EventArgs e) + { + try + { + this.BackupQueue(string.Empty); + } + catch (Exception) + { + // Do Nothing. + } + + EventHandler handler = this.QueueChanged; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Invoke the QueuePaused event + /// + /// + /// The EventArgs. + /// + private void InvokeQueuePaused(EventArgs e) + { + this.IsProcessing = false; + + EventHandler handler = this.QueuePaused; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Run through all the jobs on the queue. + /// + private void ProcessNextJob() + { + QueueTask job = this.GetNextJobForProcessing(); + if (job != null) + { + if (this.userSettingService.GetUserSetting(UserSettingConstants.PauseOnLowDiskspace) && !DriveUtilities.HasMinimumDiskSpace(job.Task.Destination, this.userSettingService.GetUserSetting(UserSettingConstants.PauseOnLowDiskspaceLevel))) + { + LogService.GetLogger().LogMessage(Resources.PauseOnLowDiskspace, LogMessageType.ScanOrEncode, LogLevel.Info); + job.Status = QueueItemStatus.Waiting; + this.Pause(); + this.BackupQueue(string.Empty); + return; // Don't start the next job. + } + + job.Status = QueueItemStatus.InProgress; + job.Statistics.StartTime = DateTime.Now; + this.LastProcessedJob = job; + this.IsProcessing = true; + this.InvokeQueueChanged(EventArgs.Empty); + this.InvokeJobProcessingStarted(new QueueProgressEventArgs(job)); + + if (!Directory.Exists(Path.GetDirectoryName(job.Task.Destination))) + { + this.EncodeServiceEncodeCompleted(null, new EncodeCompletedEventArgs(false, null, "Destination Directory Missing", null, null, 0)); + this.BackupQueue(string.Empty); + return; + } + this.EncodeService.Start(job.Task, job.Configuration); + this.BackupQueue(string.Empty); + } + else + { + // No more jobs to process, so unsubscribe the event + this.EncodeService.EncodeCompleted -= this.EncodeServiceEncodeCompleted; + + this.BackupQueue(string.Empty); + + // Fire the event to tell connected services. + this.OnQueueCompleted(new QueueCompletedEventArgs(false)); + } + } + + #endregion + } } \ No newline at end of file diff --git a/win/CS/HandBrakeWPF/Startup/AppBootstrapper.cs b/win/CS/HandBrakeWPF/Startup/AppBootstrapper.cs index 458c071c3..4f1f6c159 100644 --- a/win/CS/HandBrakeWPF/Startup/AppBootstrapper.cs +++ b/win/CS/HandBrakeWPF/Startup/AppBootstrapper.cs @@ -60,7 +60,7 @@ namespace HandBrakeWPF.Startup this.container.Singleton(); this.container.Singleton(); this.container.Singleton(); - this.container.Singleton(); + this.container.Singleton(); // Commands diff --git a/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs index d39975244..c93a005d3 100644 --- a/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs +++ b/win/CS/HandBrakeWPF/ViewModels/MainViewModel.cs @@ -70,7 +70,7 @@ namespace HandBrakeWPF.ViewModels { #region Private Variables and Services - private readonly IQueueProcessor queueProcessor; + private readonly IQueueService queueProcessor; private readonly IPresetService presetService; private readonly IErrorService errorService; private readonly IUpdateService updateService; @@ -126,7 +126,7 @@ namespace HandBrakeWPF.ViewModels this.notifyIconService = notifyIconService; this.QueueViewModel = queueViewModel; this.userSettingService = userSettingService; - this.queueProcessor = IoC.Get(); + this.queueProcessor = IoC.Get(); this.SummaryViewModel = summaryViewModel; this.PictureSettingsViewModel = pictureSettingsViewModel; diff --git a/win/CS/HandBrakeWPF/ViewModels/MiniViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/MiniViewModel.cs index 3e5fb83c1..59de994df 100644 --- a/win/CS/HandBrakeWPF/ViewModels/MiniViewModel.cs +++ b/win/CS/HandBrakeWPF/ViewModels/MiniViewModel.cs @@ -25,7 +25,7 @@ namespace HandBrakeWPF.ViewModels public class MiniViewModel : ViewModelBase, IMiniViewModel { private readonly IEncode encodeService; - private readonly IQueueProcessor queueProcessor; + private readonly IQueueService queueProcessor; private string queueStatus; private string progress; private string task; @@ -40,7 +40,7 @@ namespace HandBrakeWPF.ViewModels /// /// The queue Processor. /// - public MiniViewModel(IEncode encodeService, IQueueProcessor queueProcessor) + public MiniViewModel(IEncode encodeService, IQueueService queueProcessor) { this.encodeService = encodeService; this.queueProcessor = queueProcessor; diff --git a/win/CS/HandBrakeWPF/ViewModels/QueueViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/QueueViewModel.cs index a5df085b0..15602f79d 100644 --- a/win/CS/HandBrakeWPF/ViewModels/QueueViewModel.cs +++ b/win/CS/HandBrakeWPF/ViewModels/QueueViewModel.cs @@ -44,7 +44,7 @@ namespace HandBrakeWPF.ViewModels private readonly IErrorService errorService; private readonly IUserSettingService userSettingService; - private readonly IQueueProcessor queueProcessor; + private readonly IQueueService queueProcessor; private string jobStatus; private string jobsPending; private string whenDoneAction; @@ -70,7 +70,7 @@ namespace HandBrakeWPF.ViewModels /// /// The Error Service /// - public QueueViewModel(IUserSettingService userSettingService, IQueueProcessor queueProcessor, IErrorService errorService) + public QueueViewModel(IUserSettingService userSettingService, IQueueService queueProcessor, IErrorService errorService) { this.userSettingService = userSettingService; this.queueProcessor = queueProcessor; diff --git a/win/CS/HandBrakeWPF/ViewModels/ShellViewModel.cs b/win/CS/HandBrakeWPF/ViewModels/ShellViewModel.cs index 60706c3fd..c75981ef2 100644 --- a/win/CS/HandBrakeWPF/ViewModels/ShellViewModel.cs +++ b/win/CS/HandBrakeWPF/ViewModels/ShellViewModel.cs @@ -18,7 +18,7 @@ namespace HandBrakeWPF.ViewModels using HandBrakeWPF.Services.Interfaces; using HandBrakeWPF.ViewModels.Interfaces; - using IQueueProcessor = HandBrakeWPF.Services.Queue.Interfaces.IQueueProcessor; + using IQueueService = HandBrakeWPF.Services.Queue.Interfaces.IQueueService; /// /// The Shell View Model @@ -189,7 +189,7 @@ namespace HandBrakeWPF.ViewModels /// public bool CanClose() { - IQueueProcessor processor = IoC.Get(); + IQueueService processor = IoC.Get(); if (processor != null && processor.EncodeService.IsEncoding) { MessageBoxResult result = -- 2.40.0