// --------------------------------------------------------------------------------------------------------------------
//
// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
//
//
// The Preview View Model
//
// --------------------------------------------------------------------------------------------------------------------
namespace HandBrakeWPF.ViewModels
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using Caliburn.Micro;
using HandBrakeWPF.EventArgs;
using HandBrakeWPF.Extensions;
using HandBrakeWPF.Model.Options;
using HandBrakeWPF.Properties;
using HandBrakeWPF.Services.Interfaces;
using HandBrakeWPF.Services.Queue.Interfaces;
using HandBrakeWPF.Services.Queue.Model;
using HandBrakeWPF.Utilities;
using HandBrakeWPF.ViewModels.Interfaces;
using Microsoft.Win32;
public class QueueViewModel : ViewModelBase, IQueueViewModel
{
private readonly IErrorService errorService;
private readonly IUserSettingService userSettingService;
private readonly IQueueService queueProcessor;
private string jobsPending;
private WhenDone whenDoneAction;
private QueueTask selectedTask;
private bool isQueueRunning;
public QueueViewModel(IUserSettingService userSettingService, IQueueService queueProcessor, IErrorService errorService)
{
this.userSettingService = userSettingService;
this.queueProcessor = queueProcessor;
this.errorService = errorService;
this.Title = Resources.QueueViewModel_Queue;
this.JobsPending = Resources.QueueViewModel_NoEncodesPending;
this.SelectedItems = new BindingList();
this.SelectedItems.ListChanged += this.SelectedItems_ListChanged;
this.DisplayName = "Queue";
this.IsQueueRunning = false;
this.SelectedTabIndex = 0;
this.WhenDoneAction = (WhenDone)this.userSettingService.GetUserSetting(UserSettingConstants.WhenCompleteAction);
}
public bool IsQueueRunning
{
get
{
return this.isQueueRunning;
}
set
{
if (value == this.isQueueRunning) return;
this.isQueueRunning = value;
this.NotifyOfPropertyChange(() => this.IsQueueRunning);
}
}
public string JobsPending
{
get
{
return this.jobsPending;
}
set
{
this.jobsPending = value;
this.NotifyOfPropertyChange(() => this.JobsPending);
}
}
public WhenDone WhenDoneAction
{
get
{
return this.whenDoneAction;
}
set
{
this.whenDoneAction = value;
this.NotifyOfPropertyChange(() => this.WhenDoneAction);
}
}
public ObservableCollection QueueTasks
{
get
{
return this.queueProcessor.Queue;
}
}
public BindingList SelectedItems { get; }
public QueueTask SelectedTask
{
get
{
return this.selectedTask;
}
set
{
if (Equals(value, this.selectedTask)) return;
this.selectedTask = value;
this.NotifyOfPropertyChange(() => this.SelectedTask);
this.HandleLogData();
this.NotifyOfPropertyChange(() => this.CanRetryJob);
this.NotifyOfPropertyChange(() => this.CanEditJob);
this.NotifyOfPropertyChange(() => this.CanRemoveJob);
this.NotifyOfPropertyChange(() => this.CanPerformActionOnSource);
this.NotifyOfPropertyChange(() => this.CanPlayFile);
this.NotifyOfPropertyChange(() => this.StatsVisible);
this.NotifyOfPropertyChange(() => this.JobInfoVisible);
}
}
public bool JobInfoVisible
{
get
{
return SelectedItems.Count == 1;
}
}
public int SelectedTabIndex { get; set; }
public string ActivityLog { get; private set; }
public bool CanRetryJob => this.SelectedTask != null && this.SelectedTask.Status != QueueItemStatus.Waiting && this.SelectedTask.Status != QueueItemStatus.InProgress;
public bool CanEditJob => this.SelectedTask != null;
public bool CanRemoveJob => this.SelectedTask != null;
public bool CanPerformActionOnSource => this.SelectedTask != null;
public bool CanPlayFile =>
this.SelectedTask != null && this.SelectedTask.Task.Destination != null &&
this.SelectedTask.Status == QueueItemStatus.Completed && File.Exists(this.SelectedTask.Task.Destination);
public bool StatsVisible
{
get
{
if (this.SelectedTask != null &&
(this.selectedTask.Status == QueueItemStatus.Completed || this.selectedTask.Status == QueueItemStatus.Error || this.selectedTask.Status == QueueItemStatus.InProgress))
{
return true;
}
return false;
}
}
public void WhenDone(int action)
{
this.WhenDone(action, true);
}
public void WhenDone(int action, bool saveChange)
{
this.WhenDoneAction = (WhenDone)action;
if (saveChange)
{
this.userSettingService.SetUserSetting(UserSettingConstants.WhenCompleteAction, action);
}
IOptionsViewModel ovm = IoC.Get();
ovm.UpdateSettings();
}
public void Clear()
{
MessageBoxResult result = this.errorService.ShowMessageBox(
Resources.QueueViewModel_ClearQueueConfrimation, Resources.Confirm, MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
this.queueProcessor.Clear();
}
}
public void ClearCompleted()
{
this.queueProcessor.ClearCompleted();
}
public void Close()
{
this.TryClose();
}
public override void OnLoad()
{
// Setup the window to the correct state.
this.IsQueueRunning = this.queueProcessor.IsProcessing;
this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count);
base.OnLoad();
}
public bool CanPauseQueue()
{
return this.IsQueueRunning;
}
public void PauseQueue()
{
this.queueProcessor.Pause(true);
this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count);
this.IsQueueRunning = false;
}
public void PauseQueueToolbar()
{
this.PauseQueue();
}
public void StopQueue()
{
if (this.queueProcessor.IsEncoding)
{
MessageBoxResult result = this.errorService.ShowMessageBox(
"There are currently jobs running. Would you like to complete the current jobs before stopping the queue?",
"Confirm",
MessageBoxButton.YesNoCancel,
MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
this.queueProcessor.Stop(false);
}
else if (result == MessageBoxResult.Cancel)
{
return;
}
else
{
this.queueProcessor.Stop(true);
}
}
else
{
this.queueProcessor.Stop(true);
}
this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count);
this.IsQueueRunning = false;
}
public void RemoveSelectedJobs()
{
if (this.SelectedItems.Count == 0)
{
return;
}
MessageBoxResult result =
this.errorService.ShowMessageBox(
Resources.QueueViewModel_DelSelectedJobConfirmation,
Resources.Question,
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result == MessageBoxResult.No)
{
return;
}
List tasksToRemove = this.SelectedItems.OrderBy(s => s.Status).ToList();
foreach (QueueTask job in tasksToRemove)
{
this.RemoveJob(job);
}
}
public void RemoveJob(object queueTask)
{
QueueTask task = queueTask as QueueTask;
if (task == null)
{
return;
}
bool removed = false;
int index = this.QueueTasks.IndexOf(task);
if (task.Status == QueueItemStatus.InProgress)
{
MessageBoxResult result =
this.errorService.ShowMessageBox(
Resources.QueueViewModel_JobCurrentlyRunningWarning,
Resources.Warning,
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
this.queueProcessor.Remove(task);
removed = true;
}
}
else
{
this.queueProcessor.Remove(task);
removed = true;
}
if (this.QueueTasks.Any() && removed)
{
this.SelectedTask = index > 1 ? this.QueueTasks[index - 1] : this.QueueTasks.FirstOrDefault();
}
}
public void RetryJob(QueueTask task)
{
this.queueProcessor.RetryJob(task);
this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count);
this.NotifyOfPropertyChange(() => this.CanRetryJob);
}
public void StartQueue()
{
if (!this.QueueTasks.Any(a => a.Status == QueueItemStatus.Waiting || a.Status == QueueItemStatus.InProgress))
{
this.errorService.ShowMessageBox(
Resources.QueueViewModel_NoPendingJobs, Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
var firstOrDefault = this.QueueTasks.FirstOrDefault(s => s.Status == QueueItemStatus.Waiting);
if (firstOrDefault != null && !DriveUtilities.HasMinimumDiskSpace(firstOrDefault.Task.Destination,
this.userSettingService.GetUserSetting(UserSettingConstants.PauseQueueOnLowDiskspaceLevel)))
{
MessageBoxResult result = this.errorService.ShowMessageBox(Resources.Main_LowDiskspace, Resources.Warning, MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.No)
{
return;
}
}
this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count);
this.IsQueueRunning = true;
this.queueProcessor.Start();
}
public void ExportCli()
{
SaveFileDialog dialog = new SaveFileDialog
{
Filter = "Json (*.json)|*.json",
OverwritePrompt = true,
DefaultExt = ".json",
AddExtension = true
};
if (dialog.ShowDialog() == true)
{
this.queueProcessor.ExportCliJson(dialog.FileName);
}
}
public void Export()
{
SaveFileDialog dialog = new SaveFileDialog
{
Filter = "Json (*.json)|*.json",
OverwritePrompt = true,
DefaultExt = ".json",
AddExtension = true
};
if (dialog.ShowDialog() == true)
{
this.queueProcessor.ExportJson(dialog.FileName);
}
}
public void Import()
{
OpenFileDialog dialog = new OpenFileDialog { Filter = "Json (*.json)|*.json", CheckFileExists = true };
if (dialog.ShowDialog() == true)
{
this.queueProcessor.ImportJson(dialog.FileName);
}
}
public void EditJob(QueueTask task)
{
MessageBoxResult result = this.errorService.ShowMessageBox(
Resources.QueueViewModel_EditConfrimation,
"Modify Job?",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result != MessageBoxResult.Yes)
{
return;
}
// Remove the job if it is not already encoding. Let the user decide if they want to cancel or not.
this.RemoveJob(task);
// Pass a copy of the job back to the Main Screen
IMainViewModel mvm = IoC.Get();
mvm.EditQueueJob(task);
}
public void OpenSourceDir()
{
this.OpenSourceDirectory(this.SelectedTask);
}
public void OpenDestDir()
{
this.OpenDestinationDirectory(this.SelectedTask);
}
public void OpenSourceDirectory(QueueTask task)
{
if (task != null)
{
this.OpenDirectory(task.ScannedSourcePath);
}
}
public void OpenDestinationDirectory(QueueTask task)
{
if (task != null)
{
this.OpenDirectory(task.Task.Destination);
}
}
public void ResetSelectedJobs()
{
foreach (var task in this.SelectedItems)
{
if (task.Status == QueueItemStatus.Completed || task.Status == QueueItemStatus.Error)
{
this.RetryJob(task);
}
}
}
public void ResetAllJobs()
{
foreach (var task in this.QueueTasks)
{
if (task.Status == QueueItemStatus.Completed || task.Status == QueueItemStatus.Error)
{
this.RetryJob(task);
}
}
}
public void ResetFailed()
{
foreach (var task in this.QueueTasks)
{
if (task.Status == QueueItemStatus.Error)
{
this.RetryJob(task);
}
}
}
public void PlayFile()
{
if (this.SelectedTask != null && this.SelectedTask.Task != null && File.Exists(this.SelectedTask.Task.Destination))
{
Process.Start(this.SelectedTask.Task.Destination);
}
}
public void MoveUp()
{
Dictionary tasks = new Dictionary();
foreach (var item in this.SelectedItems)
{
tasks.Add(this.QueueTasks.IndexOf(item), item);
}
foreach (var item in tasks.OrderBy(s => s.Key))
{
this.QueueTasks.MoveUp(item.Value);
}
}
public void MoveDown()
{
Dictionary tasks = new Dictionary();
foreach (var item in this.SelectedItems)
{
tasks.Add(this.QueueTasks.IndexOf(item), item);
}
foreach (var item in tasks.OrderByDescending(s => s.Key))
{
this.QueueTasks.MoveDown(item.Value);
}
}
public void Activate()
{
this.OnActivate();
}
public void Deactivate()
{
this.OnDeactivate(false);
}
protected override void OnActivate()
{
this.Load();
this.queueProcessor.QueueCompleted += this.QueueProcessor_QueueCompleted;
this.queueProcessor.QueueChanged += this.QueueManager_QueueChanged;
this.queueProcessor.JobProcessingStarted += this.QueueProcessorJobProcessingStarted;
this.queueProcessor.QueuePaused += this.QueueProcessor_QueuePaused;
this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count);
base.OnActivate();
}
protected override void OnDeactivate(bool close)
{
this.queueProcessor.QueueCompleted -= this.QueueProcessor_QueueCompleted;
this.queueProcessor.QueueChanged -= this.QueueManager_QueueChanged;
this.queueProcessor.JobProcessingStarted -= this.QueueProcessorJobProcessingStarted;
this.queueProcessor.QueuePaused -= this.QueueProcessor_QueuePaused;
base.OnDeactivate(close);
}
private void OpenDirectory(string directory)
{
try
{
if (!string.IsNullOrEmpty(directory))
{
if (File.Exists(directory))
{
string argument = "/select, \"" + directory + "\"";
Process.Start("explorer.exe", argument);
return;
}
if (!File.Exists(directory) && !directory.EndsWith("\\"))
{
directory = Path.GetDirectoryName(directory) + "\\";
}
directory = Path.GetDirectoryName(directory);
if (directory != null && Directory.Exists(directory))
{
Process.Start(directory);
}
}
}
catch (Exception exc)
{
this.errorService.ShowError(Resources.MainViewModel_UnableToLaunchDestDir, Resources.MainViewModel_UnableToLaunchDestDirSolution, exc);
}
}
public void OpenLogDirectory()
{
string logDir = DirectoryUtilities.GetLogDirectory();
string windir = Environment.GetEnvironmentVariable("WINDIR");
Process prc = new Process { StartInfo = { FileName = windir + @"\explorer.exe", Arguments = logDir } };
prc.Start();
}
public void CopyLog()
{
try
{
Clipboard.SetDataObject(this.ActivityLog, true);
}
catch (Exception exc)
{
this.errorService.ShowError(Resources.Clipboard_Unavailable, Resources.Clipboard_Unavailable_Solution, exc);
}
}
private void HandleLogData()
{
if (this.SelectedTask == null || this.SelectedTask.Status == QueueItemStatus.InProgress || this.SelectedTask.Status == QueueItemStatus.Waiting)
{
this.ActivityLog = Resources.QueueView_LogNotAvailableYet;
}
else
{
try
{
// TODO full log path
if (!string.IsNullOrEmpty(this.SelectedTask.Statistics.CompletedActivityLogPath)
&& File.Exists(this.SelectedTask.Statistics.CompletedActivityLogPath))
{
using (StreamReader logReader = new StreamReader(this.SelectedTask.Statistics.CompletedActivityLogPath))
{
string logContent = logReader.ReadToEnd();
this.ActivityLog = logContent;
}
}
else
{
this.ActivityLog = string.Empty;
}
}
catch (Exception exc)
{
Debug.WriteLine(exc);
this.ActivityLog = exc.ToString();
}
}
this.NotifyOfPropertyChange(() => this.ActivityLog);
}
private void SelectedItems_ListChanged(object sender, ListChangedEventArgs e)
{
this.NotifyOfPropertyChange(() => this.JobInfoVisible);
if (!this.JobInfoVisible)
{
this.SelectedTabIndex = 0;
this.NotifyOfPropertyChange(() => this.SelectedTabIndex);
}
}
private void QueueManager_QueueChanged(object sender, EventArgs e)
{
this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count);
if (!queueProcessor.IsProcessing)
{
this.IsQueueRunning = false;
}
this.NotifyOfPropertyChange(() => this.CanRetryJob);
this.NotifyOfPropertyChange(() => this.CanEditJob);
this.NotifyOfPropertyChange(() => this.CanRemoveJob);
this.NotifyOfPropertyChange(() => this.CanPerformActionOnSource);
this.NotifyOfPropertyChange(() => this.CanPlayFile);
this.NotifyOfPropertyChange(() => this.StatsVisible);
this.NotifyOfPropertyChange(() => this.JobInfoVisible);
this.HandleLogData();
}
private void QueueProcessor_QueueCompleted(object sender, EventArgs e)
{
this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count);
this.IsQueueRunning = false;
this.NotifyOfPropertyChange(() => this.SelectedTask);
this.NotifyOfPropertyChange(() => this.StatsVisible);
this.NotifyOfPropertyChange(() => this.CanRetryJob);
this.NotifyOfPropertyChange(() => this.JobInfoVisible);
}
private void QueueProcessorJobProcessingStarted(object sender, QueueProgressEventArgs e)
{
this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count);
this.IsQueueRunning = true;
}
private void QueueProcessor_QueuePaused(object sender, EventArgs e)
{
this.JobsPending = string.Format(Resources.QueueViewModel_JobsPending, this.queueProcessor.Count);
this.IsQueueRunning = false;
}
}
}