// --------------------------------------------------------------------------------------------------------------------
//
// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
//
//
// The Audio View Model
//
// --------------------------------------------------------------------------------------------------------------------
namespace HandBrakeWPF.ViewModels
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Caliburn.Micro;
using HandBrake.ApplicationServices.Interop.Model.Encoding;
using HandBrake.ApplicationServices.Services.Encode.Model;
using HandBrake.ApplicationServices.Services.Encode.Model.Models;
using HandBrake.ApplicationServices.Services.Scan.Model;
using HandBrake.ApplicationServices.Utilities;
using HandBrakeWPF.Model.Audio;
using HandBrakeWPF.Properties;
using HandBrakeWPF.Services.Interfaces;
using HandBrakeWPF.Services.Presets.Model;
using HandBrakeWPF.ViewModels.Interfaces;
///
/// The Audio View Model
///
public class AudioViewModel : ViewModelBase, IAudioViewModel
{
///
/// Backing field for the source tracks list.
///
private IEnumerable sourceTracks;
///
/// The current preset.
///
private Preset currentPreset;
///
/// The show audio defaults panel.
///
private bool showAudioDefaultsPanel;
///
/// The available languages.
///
private BindingList availableLanguages;
///
/// The audio behaviours.
///
private AudioBehaviours audioBehaviours;
#region Constructors and Destructors
///
/// Initializes a new instance of the class.
///
///
/// The window manager.
///
///
/// The user Setting Service.
///
public AudioViewModel(IWindowManager windowManager, IUserSettingService userSettingService)
{
this.Task = new EncodeTask();
this.SampleRates = new ObservableCollection { "Auto", "48", "44.1", "32", "24", "22.05", "16", "12", "11.025", "8" };
this.AudioEncoders = EnumHelper.GetEnumList();
this.AudioMixdowns = EnumHelper.GetEnumList();
this.SourceTracks = new List();
this.AudioBehaviours = new AudioBehaviours();
this.SelectedAvailableToMove = new BindingList();
this.SelectedLangaugesToMove = new BindingList();
this.AvailableLanguages = new BindingList();
this.SetupLanguages(null);
}
#endregion
#region Properties
///
/// Gets or sets the audio behaviours.
///
public AudioBehaviours AudioBehaviours
{
get
{
return this.audioBehaviours;
}
set
{
if (Equals(value, this.audioBehaviours))
{
return;
}
this.audioBehaviours = value;
this.NotifyOfPropertyChange(() => this.AudioBehaviours);
}
}
///
/// Gets the audio behaviour modes.
///
public BindingList AudioBehaviourModeList
{
get
{
return new BindingList(EnumHelper.GetEnumList().ToList());
}
}
///
/// Gets the audio track default behaviour mode list.
///
public BindingList AudioTrackDefaultBehaviourModeList
{
get
{
return new BindingList(EnumHelper.GetEnumList().ToList());
}
}
///
/// Gets or sets AudioBitrates.
///
public IEnumerable AudioBitrates { get; set; }
///
/// Gets or sets AudioEncoders.
///
public IEnumerable AudioEncoders { get; set; }
///
/// Gets or sets AudioMixdowns.
///
public IEnumerable AudioMixdowns { get; set; }
///
/// Gets or sets SampleRates.
///
public IEnumerable SampleRates { get; set; }
///
/// Gets or sets SourceTracks.
///
public IEnumerable SourceTracks
{
get
{
return this.sourceTracks;
}
set
{
this.sourceTracks = value;
this.NotifyOfPropertyChange(() => this.SourceTracks);
}
}
///
/// Gets or sets the EncodeTask.
///
public EncodeTask Task { get; set; }
///
/// Gets or sets a value indicating whether show audio defaults panel.
///
public bool ShowAudioDefaultsPanel
{
get
{
return this.showAudioDefaultsPanel;
}
set
{
if (value.Equals(this.showAudioDefaultsPanel))
{
return;
}
this.showAudioDefaultsPanel = value;
this.NotifyOfPropertyChange(() => this.ShowAudioDefaultsPanel);
this.NotifyOfPropertyChange(() => this.PanelTitle);
this.NotifyOfPropertyChange(() => this.SwitchDisplayTitle);
}
}
///
/// Gets the panel title.
///
public string PanelTitle
{
get
{
return this.ShowAudioDefaultsPanel ? Resources.AudioViewModel_AudioDefaults : Resources.AudioViewModel_AudioTracks;
}
}
///
/// Gets the switch display title.
///
public string SwitchDisplayTitle
{
get
{
return this.ShowAudioDefaultsPanel ? Resources.AudioViewModel_SwitchBackToTracks : Resources.AudioViewModel_ConfigureDefaults;
}
}
///
/// Gets or sets AvailableLanguages.
///
public BindingList AvailableLanguages
{
get
{
return this.availableLanguages;
}
set
{
this.availableLanguages = value;
this.NotifyOfPropertyChange("AvailableLanguages");
}
}
///
/// Gets or sets SelectedLangauges.
///
public BindingList SelectedAvailableToMove { get; set; }
///
/// Gets or sets SelectedLangauges.
///
public BindingList SelectedLangaugesToMove { get; set; }
#endregion
#region Public Methods
///
/// Add an Audio Track
///
public void Add()
{
// Add the first track if available.
this.Add(null, false);
}
///
/// The add all remaining.
///
public void AddAllRemaining()
{
this.AddAllRemainingTracks();
}
///
/// Remove the Selected Track
///
///
/// The track.
///
public void Remove(AudioTrack track)
{
this.Task.AudioTracks.Remove(track);
}
///
/// Clear out the Audio Tracks
///
public void Clear()
{
this.Task.AudioTracks.Clear();
}
///
/// Reload the audio tracks based on the defaults.
///
public void ReloadDefaults()
{
this.SetupTracks();
}
///
/// Trigger a Notify Property Changed on the Task to force various UI elements to update.
///
public void RefreshTask()
{
this.NotifyOfPropertyChange(() => this.Task);
if (this.Task.OutputFormat == OutputFormat.Mp4)
{
foreach (AudioTrack track in this.Task.AudioTracks.Where(track => track.Encoder == AudioEncoder.ffflac || track.Encoder == AudioEncoder.Vorbis))
{
track.Encoder = AudioEncoder.ffaac;
}
}
}
///
/// Open the options screen to the Audio and Subtitles tab.
///
public void SetDefaultBehaviour()
{
this.ShowAudioDefaultsPanel = true;
}
///
/// The show audio defaults.
///
public void ShowAudioDefaults()
{
// OpenOverlayPanelCommand command = new OpenOverlayPanelCommand();
// command.Execute(new AudioDefaultsViewModel(this.WindowManager, this.UserSettingService));
this.ShowAudioDefaultsPanel = !this.ShowAudioDefaultsPanel;
}
///
/// Audio List Move Left
///
public void LanguageMoveRight()
{
if (this.SelectedAvailableToMove.Count > 0)
{
List copiedList = this.SelectedAvailableToMove.ToList();
foreach (string item in copiedList)
{
this.AvailableLanguages.Remove(item);
this.AudioBehaviours.SelectedLangauges.Add(item);
}
this.AvailableLanguages = new BindingList(this.AvailableLanguages.OrderBy(o => o).ToList());
}
}
///
/// Audio List Move Right
///
public void LanguageMoveLeft()
{
if (this.SelectedLangaugesToMove.Count > 0)
{
List copiedList = this.SelectedLangaugesToMove.ToList();
foreach (string item in copiedList)
{
this.AudioBehaviours.SelectedLangauges.Remove(item);
this.AvailableLanguages.Add(item);
}
}
this.AvailableLanguages = new BindingList(this.AvailableLanguages.OrderBy(o => o).ToList());
}
///
/// Audio List Clear all selected languages
///
public void LanguageClearAll()
{
foreach (string item in this.AudioBehaviours.SelectedLangauges)
{
this.AvailableLanguages.Add(item);
}
this.AvailableLanguages = new BindingList(this.AvailableLanguages.OrderBy(o => o).ToList());
this.AudioBehaviours.SelectedLangauges.Clear();
}
#endregion
#region Implemented Interfaces
///
/// Setup this tab for the specified preset.
///
///
/// The preset.
///
///
/// The task.
///
public void SetPreset(Preset preset, EncodeTask task)
{
this.Task = task;
this.currentPreset = preset;
// Audio Behaviours
this.SetupLanguages(preset);
if (preset != null && preset.Task != null)
{
this.Task.AllowedPassthruOptions = new AllowedPassthru(preset.Task.AllowedPassthruOptions);
this.SetupTracks();
}
this.NotifyOfPropertyChange(() => this.Task);
}
///
/// Update all the UI controls based on the encode task passed in.
///
///
/// The task.
///
public void UpdateTask(EncodeTask task)
{
this.Task = task;
this.NotifyOfPropertyChange(() => this.Task.AudioTracks);
this.NotifyOfPropertyChange(() => this.Task);
}
///
/// Set the Source Title
///
///
/// The source.
///
///
/// The title.
///
///
/// The preset.
///
///
/// The task.
///
public void SetSource(Source source, Title title, Preset preset, EncodeTask task)
{
this.SourceTracks = title.AudioTracks;
// Only reset the audio tracks if we have none, or if the task is null.
if (this.Task == null)
{
this.SetPreset(preset, task);
}
// If there are no source tracks, clear the list, otherwise try to Auto-Select the correct tracks
if (this.SourceTracks == null || !this.SourceTracks.Any())
{
this.Task.AudioTracks.Clear();
}
else
{
this.SetupTracks();
}
// Force UI Updates
this.NotifyOfPropertyChange(() => this.Task);
}
#endregion
#region Methods
///
/// Add the specified source track, or the first track in the SourceTracks collection if available.
///
///
/// The source track.
///
///
/// The use Behaviour Template Mode.
///
private void Add(Audio sourceTrack, bool useBehaviourTemplateMode)
{
if (this.SourceTracks != null)
{
Audio track = sourceTrack ?? this.GetPreferredAudioTrack();
if (track != null)
{
if (!useBehaviourTemplateMode)
{
this.Task.AudioTracks.Add(new AudioTrack { ScannedTrack = track });
return;
}
switch (this.AudioBehaviours.SelectedTrackDefaultBehaviour)
{
case AudioTrackDefaultsMode.None:
this.Task.AudioTracks.Add(new AudioTrack { ScannedTrack = track });
break;
case AudioTrackDefaultsMode.FirstTrack:
AudioTrack template = this.currentPreset.Task.AudioTracks.FirstOrDefault();
this.Task.AudioTracks.Add(template != null ? new AudioTrack(template, false) { ScannedTrack = track } : new AudioTrack { ScannedTrack = track });
break;
case AudioTrackDefaultsMode.AllTracks:
foreach (AudioTrack tmpl in this.currentPreset.Task.AudioTracks)
{
this.Task.AudioTracks.Add(tmpl != null ? new AudioTrack(tmpl, false) { ScannedTrack = track } : new AudioTrack { ScannedTrack = track });
}
break;
}
}
}
}
///
/// Add all source tracks that don't currently exist on the list.
///
private void AddAllRemainingTracks()
{
// For all the source audio tracks
foreach (Audio sourceTrack in this.SourceTracks)
{
// Step 2: Check if the track list already contrains this track
bool found = this.Task.AudioTracks.Any(audioTrack => Equals(audioTrack.ScannedTrack, sourceTrack));
if (!found)
{
// If it doesn't, add it.
this.Add(sourceTrack, true);
}
}
}
///
/// Attempt to automatically select the correct audio tracks based on the users settings.
///
private void SetupTracks()
{
if (!this.SourceTracks.Any())
{
// Clear out the old tracks
this.Task.AudioTracks.Clear();
return;
}
// Step 1, Cleanup Previous Tracks
this.Task.AudioTracks.Clear();
// Step 2, Sanity Check
if (this.SourceTracks == null || !this.SourceTracks.Any())
{
return;
}
// Step 3, Setup the tracks from the preset
foreach (AudioTrack track in this.currentPreset.Task.AudioTracks)
{
this.Task.AudioTracks.Add(new AudioTrack(track, false) { ScannedTrack = this.GetPreferredAudioTrack() });
}
// Step 4, Handle the default selection behaviour.
switch (this.AudioBehaviours.SelectedBehaviour)
{
case AudioBehaviourModes.None:
this.Task.AudioTracks.Clear();
break;
case AudioBehaviourModes.FirstMatch: // Adding all remaining audio tracks
this.AddFirstForSelectedLanguages();
break;
case AudioBehaviourModes.AllMatching: // Add Langauges tracks for the additional languages selected, in-order.
this.AddAllRemainingForSelectedLanguages();
break;
}
}
///
/// The add first for selected languages.
///
private void AddFirstForSelectedLanguages()
{
foreach (Audio sourceTrack in this.GetSelectedLanguagesTracks())
{
// Step 2: Check if the track list already contrains this track
bool found = this.Task.AudioTracks.Any(audioTrack => Equals(audioTrack.ScannedTrack, sourceTrack));
if (!found)
{
// Check if we are already using this language
bool foundLanguage = false;
foreach (var item in this.Task.AudioTracks.Where(item => item.ScannedTrack != null && sourceTrack.LanguageCode.Contains(item.ScannedTrack.LanguageCode)))
{
foundLanguage = true;
}
if (foundLanguage)
{
continue;
}
// If it doesn't, add it.
this.Add(sourceTrack, true);
}
}
}
///
/// Add all remaining for selected languages.
///
public void AddAllRemainingForSelectedLanguages()
{
// Add them if they are not already added.
foreach (Audio sourceTrack in this.GetSelectedLanguagesTracks())
{
// Step 2: Check if the track list already contrains this track
bool found = this.Task.AudioTracks.Any(audioTrack => Equals(audioTrack.ScannedTrack, sourceTrack));
if (!found)
{
// If it doesn't, add it.
this.Add(sourceTrack, true);
}
}
}
///
/// The get preferred audio track, or the first if none available.
///
///
/// The users preferred language, or the first if none available.
///
private Audio GetPreferredAudioTrack()
{
// The first track in the selected languages list is considered the preferred language.
// So, try match tracks on this.
IEnumerable preferredAudioTracks = new List();
if (this.AudioBehaviours.SelectedLangauges.Count > 0)
{
string langName = this.AudioBehaviours.SelectedLangauges.FirstOrDefault(w => !w.Equals(Constants.Any));
if (!string.IsNullOrEmpty(langName))
{
preferredAudioTracks = this.SourceTracks.Where(item => item.Language.Contains(langName));
}
}
return preferredAudioTracks.FirstOrDefault() ?? this.SourceTracks.FirstOrDefault();
}
///
/// Gets a list of source tracks for the users selected languages.
///
///
/// A list of source audio tracks.
///
private IEnumerable GetSelectedLanguagesTracks()
{
List trackList = new List();
List isoCodes = this.AudioBehaviours.SelectedLangauges.Contains(Constants.Any)
? LanguageUtilities.GetIsoCodes()
: LanguageUtilities.GetLanguageCodes(
this.AudioBehaviours.SelectedLangauges.ToArray());
foreach (string code in isoCodes)
{
trackList.AddRange(this.SourceTracks.Where(source => source.LanguageCode.Trim() == code));
}
return trackList;
}
///
/// The setup languages.
///
///
/// The preset.
///
private void SetupLanguages(Preset preset)
{
// Step 1, Set the behaviour mode
this.AudioBehaviours.SelectedBehaviour = AudioBehaviourModes.None;
this.AudioBehaviours.SelectedLangauges.Clear();
// Step 2, Get all the languages
IDictionary langList = LanguageUtilities.MapLanguages();
langList = (from entry in langList orderby entry.Key ascending select entry).ToDictionary(pair => pair.Key, pair => pair.Value);
// Step 3, Setup Available Languages
this.AvailableLanguages.Clear();
foreach (string item in langList.Keys)
{
this.AvailableLanguages.Add(item);
}
// Step 4, Set the Selected Languages
if (preset != null && preset.AudioTrackBehaviours != null)
{
this.AudioBehaviours.SelectedBehaviour = preset.AudioTrackBehaviours.SelectedBehaviour;
this.AudioBehaviours.SelectedTrackDefaultBehaviour = preset.AudioTrackBehaviours.SelectedTrackDefaultBehaviour;
foreach (string selectedItem in preset.AudioTrackBehaviours.SelectedLangauges)
{
this.AvailableLanguages.Remove(selectedItem);
this.AudioBehaviours.SelectedLangauges.Add(selectedItem);
}
}
}
#endregion
}
}