// -------------------------------------------------------------------------------------------------------------------- // // This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. // // // Defines the QueueRecoveryHelper type. // // -------------------------------------------------------------------------------------------------------------------- namespace HandBrakeWPF.Helpers { using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Windows; using HandBrake.Interop.Utilities; using HandBrakeWPF.Services.Interfaces; using HandBrakeWPF.Services.Queue.Model; using HandBrakeWPF.Utilities; using Newtonsoft.Json; using IQueueService = HandBrakeWPF.Services.Queue.Interfaces.IQueueService; 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. /// So, return a boolean if true. /// /// /// The filter Queue Files. /// /// /// True if there is a queue to recover. /// public static List CheckQueueRecovery(List filterQueueFiles) { try { // Check for any Corrupted Backup Files and try recover them RecoverFromBackupFailure(); // Now check for all available recovery files. (There may be more than 1 for multi-instance support) string tempPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); DirectoryInfo info = new DirectoryInfo(tempPath); IEnumerable foundFiles = info.GetFiles("*.json").Where(f => f.Name.StartsWith(QueueFileName)); var queueFiles = GetFilesExcludingActiveProcesses(foundFiles, filterQueueFiles); if (!queueFiles.Any()) { return queueFiles; } List removeFiles = new List(); List acceptedFiles = new List(); foreach (string file in queueFiles) { try { using (StreamReader stream = new StreamReader(file)) { List list = list = JsonConvert.DeserializeObject>(stream.ReadToEnd()); if (list != null && list.Count == 0) { removeFiles.Add(file); } if (list != null && list.Count != 0) { List tasks = list.Where(l => l.Status != QueueItemStatus.Completed).ToList(); if (tasks.Count != 0) { acceptedFiles.Add(Path.GetFileName(file)); } else { removeFiles.Add(file); } } } } catch (Exception exc) { Debug.WriteLine(exc); } } CleanupFiles(removeFiles, false); return acceptedFiles; } catch (Exception exc) { Debug.WriteLine(exc); return new List(); // Keep quiet about the error. } } /// /// Recover a queue from file. /// /// /// The encode Queue. /// /// /// The error Service. /// /// /// The silent Recovery. /// /// /// The queue Filter. /// /// /// The . /// public static bool RecoverQueue(IQueueService encodeQueue, IErrorService errorService, bool silentRecovery, List queueFilter) { string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); List queueFiles = CheckQueueRecovery(queueFilter); MessageBoxResult result = MessageBoxResult.None; if (!silentRecovery) { if (queueFiles.Count == 1) { result = errorService.ShowMessageBox( Properties.Resources.Queue_RecoverQueueQuestionSingular, Properties.Resources.Queue_RecoveryPossible, MessageBoxButton.YesNo, MessageBoxImage.Question); } else if (queueFiles.Count > 1) { result = errorService.ShowMessageBox( Properties.Resources.Queue_RecoverQueueQuestionPlural, Properties.Resources.Queue_RecoveryPossible, MessageBoxButton.YesNo, MessageBoxImage.Question); } } else { result = MessageBoxResult.Yes; } if (result == MessageBoxResult.Yes) { bool isRecovered = false; foreach (string file in queueFiles) { // Recover the Queue encodeQueue.RestoreQueue(Path.Combine(appDataPath, file)); isRecovered = true; // Cleanup CleanupFiles(new List { file }, false); } return isRecovered; } CleanupFiles(queueFiles, true); return false; } public static bool ArchivesExist() { string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); DirectoryInfo info = new DirectoryInfo(appDataPath); IEnumerable foundFiles = info.GetFiles("*.archive").Where(f => f.Name.StartsWith(QueueFileName)); return foundFiles.Any(); } private static void RecoverFromBackupFailure() { string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); DirectoryInfo info = new DirectoryInfo(appDataPath); IEnumerable foundFiles = info.GetFiles("*.last"); foreach (FileInfo file in foundFiles) { string corruptedFile = file.FullName.Replace(".last", string.Empty); if (File.Exists(corruptedFile)) { File.Delete(corruptedFile); } File.Move(file.FullName, corruptedFile); } } private static List GetFilesExcludingActiveProcesses(IEnumerable foundFiles, List filterQueueFiles) { List queueFiles = new List(); // Remove any files where we have an active instnace. foreach (FileInfo file in foundFiles) { string fileProcessId = file.Name.Replace(QueueFileName, string.Empty).Replace(".json", string.Empty); int processId; if (!string.IsNullOrEmpty(fileProcessId) && int.TryParse(fileProcessId, out processId)) { if (!GeneralUtilities.IsPidACurrentHandBrakeInstance(processId)) { if (filterQueueFiles != null && filterQueueFiles.Count > 0) { if (filterQueueFiles.Contains(processId.ToString())) { queueFiles.Add(file.FullName); } } else { queueFiles.Add(file.FullName); } } } } return queueFiles; } private static void CleanupFiles(List removeFiles, bool archive) { string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); // Cleanup old/unused queue files for now. foreach (string file in removeFiles) { Match m = Regex.Match(file, @"([0-9]+).json"); if (m.Success) { int processId = int.Parse(m.Groups[1].ToString()); if (GeneralUtilities.IsPidACurrentHandBrakeInstance(processId)) { continue; } } string fullPath = Path.Combine(appDataPath, file); if (archive) { File.Move(fullPath, fullPath + ".archive"); } else { File.Delete(fullPath); } } TidyArchiveFiles(); } /// /// Tidy up archive files older than 7 days. /// Gives the user an opportunity to recover a queue file they accidentally chose not to import. /// private static void TidyArchiveFiles() { string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); DirectoryInfo info = new DirectoryInfo(appDataPath); IEnumerable foundFiles = info.GetFiles("*.archive").Where(f => f.Name.StartsWith(QueueFileName)); DateTime lastWeek = DateTime.Now.AddDays(-7); foreach (FileInfo file in foundFiles) { if (file.CreationTime < lastWeek) { string fullPath = Path.Combine(appDataPath, file.Name); File.Delete(fullPath); } } } public static void ResetArchives() { string appDataPath = DirectoryUtilities.GetUserStoragePath(VersionHelper.IsNightly()); DirectoryInfo info = new DirectoryInfo(appDataPath); IEnumerable foundFiles = info.GetFiles("*.archive").Where(f => f.Name.StartsWith(QueueFileName)); foreach (FileInfo file in foundFiles) { string fullPath = Path.Combine(appDataPath, file.Name); File.Move(fullPath, fullPath.Replace(".archive", string.Empty)); } } } }