// --------------------------------------------------------------------------------------------------------------------
//
// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
//
//
// The Summary View Model
//
// --------------------------------------------------------------------------------------------------------------------
namespace HandBrakeWPF.ViewModels
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Windows.Media.Imaging;
using HandBrake.Interop.Interop;
using HandBrake.Interop.Interop.Model.Encoding;
using HandBrakeWPF.EventArgs;
using HandBrakeWPF.Factories;
using HandBrakeWPF.Helpers;
using HandBrakeWPF.Properties;
using HandBrakeWPF.Services.Encode.Model;
using HandBrakeWPF.Services.Encode.Model.Models;
using HandBrakeWPF.Services.Interfaces;
using HandBrakeWPF.Services.Presets.Model;
using HandBrakeWPF.Services.Scan.Interfaces;
using HandBrakeWPF.Services.Scan.Model;
using HandBrakeWPF.Utilities;
using HandBrakeWPF.ViewModelItems.Filters;
using HandBrakeWPF.ViewModels.Interfaces;
public class SummaryViewModel : ViewModelBase, ISummaryViewModel
{
private readonly IScan scanService;
private readonly IUserSettingService userSettingService;
private Preset preset;
private EncodeTask task;
private Source source;
private Title currentTitle;
private int selectedPreview = 2;
private bool isPreviousPreviewControlVisible;
private bool isNextPreviewControlVisible;
private bool showPreview;
public SummaryViewModel(IScan scanService, IUserSettingService userSettingService)
{
this.scanService = scanService;
this.userSettingService = userSettingService;
}
public event EventHandler TabStatusChanged;
public event EventHandler OutputFormatChanged;
public Preset Preset
{
get
{
return this.preset;
}
private set
{
if (Equals(value, this.preset)) return;
this.preset = value;
this.NotifyOfPropertyChange(() => this.Preset);
}
}
public EncodeTask Task
{
get
{
return this.task;
}
set
{
if (Equals(value, this.task)) return;
this.task = value;
this.NotifyOfPropertyChange(() => this.Task);
}
}
public Source Source
{
get
{
return this.source;
}
set
{
if (Equals(value, this.source)) return;
this.source = value;
this.NotifyOfPropertyChange(() => this.Source);
}
}
public Title CurrentTitle
{
get
{
return this.currentTitle;
}
set
{
if (Equals(value, this.currentTitle)) return;
this.currentTitle = value;
this.NotifyOfPropertyChange(() => this.CurrentTitle);
}
}
public IEnumerable OutputFormats
{
get
{
return new List
{
OutputFormat.Mp4, OutputFormat.Mkv, OutputFormat.WebM
};
}
}
#region DisplayProperties
public BitmapImage PreviewImage { get; set; }
public bool PreviewNotAvailable { get; set; }
public int MaxWidth { get; set; }
public int MaxHeight { get; set; }
public string VideoTrackInfo { get; set; }
public string AudioTrackInfo { get; set; }
public string SubtitleTrackInfo { get; set; }
public string ChapterInfo { get; set; }
public string FiltersInfo { get; set; }
public string DimensionInfo { get; set; }
public string AspectInfo { get; set; }
public bool IsPreviewInfoVisible { get; set; }
public string PreviewInfo { get; set; }
public bool IsPreviousPreviewControlVisible
{
get
{
return this.isPreviousPreviewControlVisible;
}
set
{
if (value == this.isPreviousPreviewControlVisible) return;
this.isPreviousPreviewControlVisible = value;
this.NotifyOfPropertyChange(() => this.IsPreviousPreviewControlVisible);
}
}
public bool IsNextPreviewControlVisible
{
get
{
return this.isNextPreviewControlVisible;
}
set
{
if (value == this.isNextPreviewControlVisible) return;
this.isNextPreviewControlVisible = value;
this.NotifyOfPropertyChange(() => this.IsNextPreviewControlVisible);
}
}
public bool ShowPreview
{
get
{
return this.showPreview;
}
set
{
if (value == this.showPreview) return;
this.showPreview = value;
this.NotifyOfPropertyChange(() => this.ShowPreview);
}
}
#endregion
#region Task Properties
///
/// Gets or sets SelectedOutputFormat.
///
public OutputFormat SelectedOutputFormat
{
get
{
return this.Task?.OutputFormat ?? OutputFormat.Mp4;
}
set
{
if (this.Task != null && !Equals(this.Task.OutputFormat, value))
{
this.Task.OutputFormat = value;
this.Task.OutputFormat = value;
this.NotifyOfPropertyChange(() => this.SelectedOutputFormat);
this.NotifyOfPropertyChange(() => this.Task.OutputFormat);
this.NotifyOfPropertyChange(() => this.IsMkvOrWebm);
this.SetExtension(string.Format(".{0}", this.Task.OutputFormat.ToString().ToLower()));
this.UpdateDisplayedInfo(); // output format may coreced to another due to container incompatibility
this.OnOutputFormatChanged(new OutputFormatChangedEventArgs(null));
this.OnTabStatusChanged(null);
}
}
}
///
/// Gets or sets a value indicating whether IsMkvOrWebm.
///
public bool IsMkvOrWebm
{
get
{
return this.SelectedOutputFormat == OutputFormat.Mkv || this.SelectedOutputFormat == OutputFormat.WebM;
}
}
///
/// Optimise MP4 Checkbox
///
public bool OptimizeMP4
{
get
{
return this.Task?.OptimizeMP4 ?? false;
}
set
{
if (value == this.Task.OptimizeMP4)
{
return;
}
this.Task.OptimizeMP4 = value;
this.NotifyOfPropertyChange(() => this.OptimizeMP4);
this.OnTabStatusChanged(null);
}
}
///
/// iPod 5G Status
///
public bool IPod5GSupport
{
get
{
return this.Task?.IPod5GSupport ?? false;
}
set
{
if (value == this.Task.IPod5GSupport)
{
return;
}
this.Task.IPod5GSupport = value;
this.NotifyOfPropertyChange(() => this.IPod5GSupport);
this.OnTabStatusChanged(null);
}
}
public bool AlignAVStart
{
get
{
return this.Task?.AlignAVStart ?? false;
}
set
{
if (value == this.Task.AlignAVStart)
{
return;
}
this.Task.AlignAVStart = value;
this.NotifyOfPropertyChange(() => this.AlignAVStart);
this.OnTabStatusChanged(null);
}
}
#endregion
public void SetSource(Source scannedSource, Title selectedTitle, Preset currentPreset, EncodeTask encodeTask)
{
this.Source = scannedSource;
this.CurrentTitle = selectedTitle;
this.Task = encodeTask;
this.UpdateDisplayedInfo();
this.SetPreviewControlVisibility();
}
public void SetPreset(Preset currentPreset, EncodeTask encodeTask)
{
this.Preset = currentPreset;
this.Task = encodeTask;
this.UpdateSettings(currentPreset);
this.UpdateDisplayedInfo();
}
public void UpdateTask(EncodeTask updatedTask)
{
this.Task = updatedTask;
this.UpdateDisplayedInfo();
this.NotifyOfPropertyChange(() => this.SelectedOutputFormat);
this.NotifyOfPropertyChange(() => this.IsMkvOrWebm);
this.NotifyOfPropertyChange(() => this.OptimizeMP4);
this.NotifyOfPropertyChange(() => this.IPod5GSupport);
this.NotifyOfPropertyChange(() => this.AlignAVStart);
}
public bool MatchesPreset(Preset preset)
{
if (preset.Task.OutputFormat != this.SelectedOutputFormat)
{
return false;
}
if (preset.Task.OptimizeMP4 != this.OptimizeMP4)
{
return false;
}
if (preset.Task.AlignAVStart != this.AlignAVStart)
{
return false;
}
if (preset.Task.IPod5GSupport != this.IPod5GSupport)
{
return false;
}
return true;
}
public void UpdateDisplayedInfo()
{
if (this.CurrentTitle == null)
{
this.ClearDisplay();
return;
}
this.PopulateSummaryTab();
this.UpdatePreviewFrame();
}
public void SetContainer(OutputFormat container)
{
this.SelectedOutputFormat = container;
}
public void NextPreview()
{
int maxPreview = this.userSettingService.GetUserSetting(UserSettingConstants.PreviewScanCount, typeof(int));
if (this.selectedPreview == maxPreview)
{
return;
}
this.selectedPreview = this.selectedPreview + 1;
this.UpdatePreviewFrame();
this.PreviewInfo = string.Format(Resources.SummaryView_PreviewInfo, this.selectedPreview, maxPreview);
this.NotifyOfPropertyChange(() => this.PreviewInfo);
this.SetPreviewControlVisibility();
}
public void PreviousPreview()
{
int maxPreview = this.userSettingService.GetUserSetting(UserSettingConstants.PreviewScanCount, typeof(int));
if (this.selectedPreview <= 1)
{
return;
}
this.selectedPreview = this.selectedPreview - 1;
this.UpdatePreviewFrame();
this.PreviewInfo = string.Format(Resources.SummaryView_PreviewInfo, this.selectedPreview, maxPreview);
this.NotifyOfPropertyChange(() => this.PreviewInfo);
this.SetPreviewControlVisibility();
}
public void SetPreviewControlVisibility()
{
if (this.selectedPreview > 1)
{
this.IsPreviousPreviewControlVisible = true;
}
else
{
this.IsPreviousPreviewControlVisible = false;
}
if (this.selectedPreview < this.userSettingService.GetUserSetting(UserSettingConstants.PreviewScanCount, typeof(int)))
{
this.IsNextPreviewControlVisible = true;
}
else
{
this.IsNextPreviewControlVisible = false;
}
}
#region Private Methods
protected virtual void OnTabStatusChanged(TabStatusEventArgs e)
{
this.TabStatusChanged?.Invoke(this, e);
}
private void UpdateSettings(Preset selectedPreset)
{
// Main Window Settings
this.SelectedOutputFormat = selectedPreset.Task.OutputFormat;
this.OptimizeMP4 = selectedPreset.Task.OptimizeMP4;
this.IPod5GSupport = selectedPreset.Task.IPod5GSupport;
this.AlignAVStart = selectedPreset.Task.AlignAVStart;
}
private void SetExtension(string newExtension)
{
// Make sure the output extension is set correctly based on the users preferences and selection.
if (newExtension == ".mp4" || newExtension == ".m4v")
{
switch (this.userSettingService.GetUserSetting(UserSettingConstants.UseM4v, typeof(int)))
{
case 0: // Auto
newExtension = MP4Helper.RequiresM4v(this.Task) ? ".m4v" : ".mp4";
break;
case 1: // MP4
newExtension = ".mp4";
break;
case 2: // M4v
newExtension = ".m4v";
break;
}
}
// Now disable controls that are not required. The Following are for MP4 only!
if (newExtension == ".mkv" || newExtension == ".webm")
{
this.OptimizeMP4 = false;
this.IPod5GSupport = false;
this.AlignAVStart = false;
}
this.NotifyOfPropertyChange(() => this.IsMkvOrWebm);
// Update The browse file extension display
if (Path.HasExtension(newExtension))
{
this.OnOutputFormatChanged(new OutputFormatChangedEventArgs(newExtension));
}
// Update the UI Display
this.NotifyOfPropertyChange(() => this.Task);
}
private void PopulateSummaryTab()
{
if (this.Task == null)
{
this.ClearDisplay();
return;
}
// Dimension Section
this.VideoTrackInfo = string.Format("{0}, {1} FPS {2}", EnumHelper.GetDisplay(this.Task.VideoEncoder), this.Task.Framerate, this.Task.FramerateMode);
this.NotifyOfPropertyChange(() => this.VideoTrackInfo);
this.AudioTrackInfo = this.GetAudioDescription();
this.NotifyOfPropertyChange(() => this.AudioTrackInfo);
this.SubtitleTrackInfo = this.GetSubtitleDescription();
this.NotifyOfPropertyChange(() => this.SubtitleTrackInfo);
this.ChapterInfo = this.Task.IncludeChapterMarkers ? Resources.SummaryView_Chapters : Resources.SummaryView_NoChapters;
this.NotifyOfPropertyChange(() => this.ChapterInfo);
this.FiltersInfo = this.GetFilterDescription();
this.NotifyOfPropertyChange(() => this.FiltersInfo);
// Picutre Section
this.DimensionInfo = string.Format("{0}x{1} {2}, {3}x{4} {5}", this.Task.Width, this.Task.Height, Resources.SummaryView_storage, this.Task.DisplayWidth, this.Task.Height, Resources.SummaryView_display);
this.NotifyOfPropertyChange(() => this.DimensionInfo);
this.AspectInfo = string.Empty;
this.NotifyOfPropertyChange(() => this.AspectInfo);
// Preview
this.PreviewInfo = string.Format(Resources.SummaryView_PreviewInfo, this.selectedPreview, this.userSettingService.GetUserSetting(UserSettingConstants.PreviewScanCount, typeof(int)));
this.NotifyOfPropertyChange(() => this.PreviewInfo);
this.ShowPreview = this.userSettingService.GetUserSetting(UserSettingConstants.ShowPreviewOnSummaryTab);
}
private string GetFilterDescription()
{
if (this.Task == null)
{
return Resources.SummaryView_NoFilters;
}
List filters = new List();
if (this.Task.Detelecine != Detelecine.Off)
{
filters.Add(Resources.SummaryView_Detelecine);
}
if (this.Task.DeinterlaceFilter != DeinterlaceFilter.Off)
{
filters.Add(EnumHelper.GetShortName(this.task.DeinterlaceFilter));
}
if (this.Task.Denoise != Denoise.Off)
{
filters.Add(this.Task.Denoise.ToString());
}
if (this.Task.Sharpen != Sharpen.Off)
{
filters.Add(this.Task.Sharpen.ToString());
}
if (this.Task.DeblockPreset != null && this.Task.DeblockPreset.Key != DeblockFilter.Off)
{
filters.Add(Resources.SummaryView_Deblock);
}
if (this.Task.Grayscale)
{
filters.Add(Resources.SummaryView_Grayscale);
}
if (this.Task.Rotation != 0 || this.task.FlipVideo)
{
filters.Add(Resources.SummaryView_Rotation);
}
return string.Join(", ", filters).TrimEnd(',').Trim();
}
private string GetAudioDescription()
{
if (this.Task.AudioTracks.Count == 0)
{
return Resources.SummaryView_NoAudioTracks;
}
StringBuilder desc = new StringBuilder();
if (this.Task.AudioTracks.Count >= 1)
{
AudioTrack track1 = this.Task.AudioTracks[0];
HBMixdown mixdownName = HandBrakeEncoderHelpers.GetMixdown(track1.MixDown);
string mixdown = mixdownName != null ? ", " + mixdownName.DisplayName : string.Empty;
desc.AppendLine(string.Format("{0}{1}", EnumHelper.GetDisplay(track1.Encoder), mixdown));
}
if (this.Task.AudioTracks.Count >= 2)
{
AudioTrack track2 = this.Task.AudioTracks[1];
HBMixdown mixdownName = HandBrakeEncoderHelpers.GetMixdown(track2.MixDown);
string mixdown = mixdownName != null ? ", " + mixdownName.DisplayName : string.Empty;
desc.AppendLine(string.Format("{0}{1}", EnumHelper.GetDisplay(track2.Encoder), mixdown));
}
if (this.Task.AudioTracks.Count > 2)
{
desc.AppendLine(string.Format("+ {0} {1}", this.Task.AudioTracks.Count - 2, Resources.SummaryView_AdditionalAudioTracks));
}
return desc.ToString().Trim();
}
private string GetSubtitleDescription()
{
if (this.Task.AudioTracks.Count == 0)
{
return Resources.SummaryView_NoSubtitleTracks;
}
StringBuilder desc = new StringBuilder();
if (this.Task.SubtitleTracks.Count >= 1)
{
SubtitleTrack track1 = this.Task.SubtitleTracks[0];
string subtitleName = track1.IsSrtSubtitle ? track1.SrtFileName : track1.SourceTrack.ToString();
string burned = track1.Burned ? ", " + Resources.SummaryView_Burned : string.Empty;
desc.AppendLine(string.Format("{0}{1}", subtitleName, burned));
}
if (this.Task.SubtitleTracks.Count >= 2)
{
SubtitleTrack track2 = this.Task.SubtitleTracks[1];
string subtitleName = track2.IsSrtSubtitle ? track2.SrtFileName : track2.SourceTrack.ToString();
string burned = track2.Burned ? ", " + Resources.SummaryView_Burned : string.Empty;
desc.AppendLine(string.Format("{0}{1}", subtitleName, burned));
}
if (this.Task.SubtitleTracks.Count > 2)
{
desc.AppendLine(string.Format("+ {0} {1}", this.Task.SubtitleTracks.Count - 2, Resources.SummaryView_AdditionalSubtitleTracks));
}
return desc.ToString().Trim();
}
private void ClearDisplay()
{
this.VideoTrackInfo = Resources.SummaryView_NoTracks;
this.NotifyOfPropertyChange(() => this.VideoTrackInfo);
this.AudioTrackInfo = string.Empty;
this.NotifyOfPropertyChange(() => this.AudioTrackInfo);
this.SubtitleTrackInfo = string.Empty;
this.NotifyOfPropertyChange(() => this.SubtitleTrackInfo);
this.ChapterInfo = string.Empty;
this.NotifyOfPropertyChange(() => this.ChapterInfo);
this.FiltersInfo = Resources.SummaryView_NoFilters;
this.NotifyOfPropertyChange(() => this.FiltersInfo);
this.DimensionInfo = Resources.SummaryView_NoSource;
this.NotifyOfPropertyChange(() => this.ChapterInfo);
this.AspectInfo = string.Empty;
this.NotifyOfPropertyChange(() => this.FiltersInfo);
}
[HandleProcessCorruptedStateExceptions]
private void UpdatePreviewFrame()
{
// Don't preview for small images.
if (this.Task.Anamorphic == Anamorphic.Loose && this.Task.Width < 32)
{
this.PreviewNotAvailable = true;
this.IsPreviewInfoVisible = false;
this.NotifyOfPropertyChange(() => this.IsPreviewInfoVisible);
return;
}
if ((this.Task.Anamorphic == Anamorphic.None || this.Task.Anamorphic == Anamorphic.Custom) && (this.Task.Width < 32 || this.Task.Height < 32))
{
this.PreviewNotAvailable = true;
return;
}
BitmapImage image = null;
try
{
image = this.scanService.GetPreview(this.Task, this.selectedPreview - 1, HBConfigurationFactory.Create());
}
catch (Exception exc)
{
this.PreviewNotAvailable = true;
Debug.WriteLine(exc);
}
if (image != null)
{
this.PreviewNotAvailable = false;
this.PreviewImage = image;
this.MaxWidth = (int)image.Width;
this.MaxHeight = (int)image.Height;
this.IsPreviewInfoVisible = true;
this.NotifyOfPropertyChange(() => this.IsPreviewInfoVisible);
this.NotifyOfPropertyChange(() => this.PreviewImage);
this.NotifyOfPropertyChange(() => this.MaxWidth);
this.NotifyOfPropertyChange(() => this.MaxHeight);
}
}
protected virtual void OnOutputFormatChanged(OutputFormatChangedEventArgs e)
{
this.OutputFormatChanged?.Invoke(this, e);
}
#endregion
}
}