Пример #1
0
        /// <summary>
        /// Replace arguments with the given job information.
        /// </summary>
        /// <param name="nameFormat">The name format to use.</param>
        /// <param name="picker">The picker.</param>
        /// <param name="jobViewModel">The job to pick information from.</param>
        /// <returns>The string with arguments replaced.</returns>
        public string ReplaceArguments(string nameFormat, Picker picker, EncodeJobViewModel jobViewModel)
        {
            // The jobViewModel might have null VideoSource and VideoSourceMetadata from an earlier version < 4.17.

            VCJob       job   = jobViewModel.Job;
            SourceTitle title = jobViewModel.VideoSource?.Titles.Single(t => t.Index == job.Title);

            string   sourceName        = jobViewModel.VideoSourceMetadata != null ? jobViewModel.VideoSourceMetadata.Name : string.Empty;
            TimeSpan titleDuration     = title?.Duration.ToSpan() ?? TimeSpan.Zero;
            int      chapterCount      = title?.ChapterList.Count ?? 0;
            bool     hasMultipleTitles = jobViewModel.VideoSource != null && jobViewModel.VideoSource.Titles.Count > 1;

            return(this.ReplaceArguments(
                       job.SourcePath,
                       sourceName,
                       job.Title,
                       titleDuration,
                       job.RangeType,
                       job.ChapterStart,
                       job.ChapterEnd,
                       chapterCount,
                       TimeSpan.FromSeconds(job.SecondsStart),
                       TimeSpan.FromSeconds(job.SecondsEnd),
                       job.FramesStart,
                       job.FramesEnd,
                       nameFormat,
                       hasMultipleTitles,
                       picker));
        }
Пример #2
0
        public Main()
        {
            Ioc.Container.RegisterSingleton <Main>(() => this);
            this.InitializeComponent();

            this.sourceRow.Height = new GridLength(Config.SourcePaneHeightStar, GridUnitType.Star);
            this.queueRow.Height  = new GridLength(Config.QueuePaneHeightStar, GridUnitType.Star);

            this.Activated += (sender, args) =>
            {
                DispatchUtilities.BeginInvoke(async() =>
                {
                    // Need to yield here for some reason, otherwise the activation is blocked.
                    await Task.Yield();
                    this.toastNotificationService.Clear();
                });
            };

            this.notifyIcon = new NotifyIcon
            {
                Visible = false
            };
            this.notifyIcon.Click       += (sender, args) => { this.RestoreWindow(); };
            this.notifyIcon.DoubleClick += (sender, args) => { this.RestoreWindow(); };

            StreamResourceInfo streamResourceInfo = System.Windows.Application.GetResourceStream(new Uri("pack://application:,,,/VidCoder_icon.ico"));

            if (streamResourceInfo != null)
            {
                Stream iconStream = streamResourceInfo.Stream;
                this.notifyIcon.Icon = new Icon(iconStream);
            }

            this.RefreshQueueColumns();
            this.LoadCompletedColumnWidths();

#if DEBUG
            var debugDropDown = new DropDownButton {
                Header = "Debug"
            };

            var loadScanFromJsonItem = new Fluent.MenuItem {
                Header = "Load scan from JSON..."
            };
            loadScanFromJsonItem.Click += (sender, args) =>
            {
                DebugJsonDialog dialog = new DebugJsonDialog("Debug Scan JSON");
                dialog.ShowDialog();
                if (!string.IsNullOrWhiteSpace(dialog.Json))
                {
                    try
                    {
                        var scanObject = JsonConvert.DeserializeObject <JsonScanObject>(dialog.Json);
                        this.viewModel.UpdateFromNewVideoSource(new VideoSource {
                            Titles = scanObject.TitleList, FeatureTitle = scanObject.MainFeature
                        });
                    }
                    catch (Exception exception)
                    {
                        MessageBox.Show(this, "Could not parse scan JSON:" + Environment.NewLine + Environment.NewLine + exception.ToString());
                    }
                }
            };
            debugDropDown.Items.Add(loadScanFromJsonItem);

            var queueFromJsonItem = new Fluent.MenuItem {
                Header = "Queue job from JSON..."
            };
            queueFromJsonItem.Click += (sender, args) =>
            {
                if (!this.viewModel.HasVideoSource)
                {
                    StaticResolver.Resolve <IMessageBoxService>().Show("Must open source before adding queue job from JSON");
                    return;
                }

                EncodeJobViewModel jobViewModel = this.viewModel.CreateEncodeJobVM();
                DebugJsonDialog    dialog       = new DebugJsonDialog("Debug Encode JSON");
                dialog.ShowDialog();

                if (!string.IsNullOrWhiteSpace(dialog.Json))
                {
                    try
                    {
                        JsonEncodeObject encodeObject = JsonConvert.DeserializeObject <JsonEncodeObject>(dialog.Json);

                        jobViewModel.DebugEncodeJsonOverride = dialog.Json;
                        jobViewModel.Job.FinalOutputPath     = encodeObject.Destination.File;
                        jobViewModel.Job.SourcePath          = encodeObject.Source.Path;

                        this.processingService.Queue(jobViewModel);
                    }
                    catch (Exception exception)
                    {
                        MessageBox.Show(this, "Could not parse encode JSON:" + Environment.NewLine + Environment.NewLine + exception.ToString());
                    }
                }
            };
            debugDropDown.Items.Add(queueFromJsonItem);

            var throwExceptionItem = new Fluent.MenuItem {
                Header = "Throw exception"
            };
            throwExceptionItem.Click += (sender, args) =>
            {
                throw new InvalidOperationException("Rats.");
            };
            debugDropDown.Items.Add(throwExceptionItem);

            var addLogItem = new Fluent.MenuItem {
                Header = "Add 1 log item"
            };
            addLogItem.Click += (sender, args) =>
            {
                StaticResolver.Resolve <IAppLogger>().Log("This is a log item");
            };
            debugDropDown.Items.Add(addLogItem);

            var addTenLogItems = new Fluent.MenuItem {
                Header = "Add 10 log items"
            };
            addTenLogItems.Click += (sender, args) =>
            {
                for (int i = 0; i < 10; i++)
                {
                    StaticResolver.Resolve <IAppLogger>().Log("This is a log item");
                }
            };
            debugDropDown.Items.Add(addTenLogItems);

            var addLongLogItem = new Fluent.MenuItem {
                Header = "Add long log item"
            };
            addLongLogItem.Click += (sender, args) =>
            {
                StaticResolver.Resolve <IAppLogger>().Log("This is a log item\r\nthat is split into multiple lines\r\nOh yes indeed");
            };
            debugDropDown.Items.Add(addLongLogItem);

            var doAnActionItem = new Fluent.MenuItem {
                Header = "Perform action"
            };
            doAnActionItem.Click += (sender, args) =>
            {
                var app = (App)System.Windows.Application.Current;
                app.ChangeTheme(new Uri("/Themes/Dark.xaml", UriKind.Relative));
            };
            debugDropDown.Items.Add(doAnActionItem);

            this.toolsRibbonGroupBox.Items.Add(debugDropDown);
#endif

            this.DataContextChanged += this.OnDataContextChanged;
            TheDispatcher            = this.Dispatcher;

            this.statusText.Opacity = 0.0;

            NameScope.SetNameScope(this, new NameScope());
            this.RegisterName("StatusText", this.statusText);

            var storyboard = (Storyboard)this.FindResource("StatusTextStoryboard");
            storyboard.Completed += (sender, args) =>
            {
                this.statusText.Visibility = Visibility.Collapsed;
            };

            this.presetTreeViewContainer.PresetTreeView.OnHierarchyMouseUp += (sender, args) =>
            {
                this.presetButton.IsDropDownOpen = false;
            };

            this.presetButton.DropDownOpened += (sender, args) =>
            {
                var item = UIUtilities.FindDescendant <TreeViewItem>(this.presetTreeViewContainer.PresetTreeView, viewItem =>
                {
                    return(viewItem.Header == this.viewModel.PresetsService.SelectedPreset);
                });

                if (item != null)
                {
                    UIUtilities.BringIntoView(item);
                }
            };

            this.Loaded += (e, o) =>
            {
                this.RestoredWindowState = this.WindowState;
            };

            this.statusService.MessageShown += (o, e) =>
            {
                this.ShowStatusMessage(e.Value);
            };

            this.queueView.SelectionChanged += this.QueueView_SelectionChanged;
        }
Пример #3
0
        public Main()
        {
            Ioc.Container.RegisterInstance(typeof(Main), this, new ContainerControlledLifetimeManager());
            this.InitializeComponent();

            this.RefreshQueueColumns();
            this.LoadCompletedColumnWidths();

            if (CommonUtilities.DebugMode)
            {
                MenuItem queueFromJsonItem = new MenuItem {
                    Header = "Queue job from JSON..."
                };
                queueFromJsonItem.Click += (sender, args) =>
                {
                    EncodeJobViewModel    jobViewModel = this.viewModel.CreateEncodeJobVM();
                    DebugEncodeJsonDialog dialog       = new DebugEncodeJsonDialog();
                    dialog.ShowDialog();

                    if (!string.IsNullOrWhiteSpace(dialog.EncodeJson))
                    {
                        try
                        {
                            JsonEncodeObject encodeObject = JsonConvert.DeserializeObject <JsonEncodeObject>(dialog.EncodeJson);

                            jobViewModel.DebugEncodeJsonOverride = dialog.EncodeJson;
                            jobViewModel.Job.OutputPath          = encodeObject.Destination.File;
                            jobViewModel.Job.SourcePath          = encodeObject.Source.Path;

                            this.processingService.Queue(jobViewModel);
                        }
                        catch (Exception exception)
                        {
                            MessageBox.Show(this, "Could not parse encode JSON:" + Environment.NewLine + Environment.NewLine + exception.ToString());
                        }
                    }
                };

                this.toolsMenu.Items.Add(new Separator());
                this.toolsMenu.Items.Add(queueFromJsonItem);
            }

            this.DataContextChanged += this.OnDataContextChanged;
            TheDispatcher            = this.Dispatcher;

            this.presetGlowEffect.Opacity = 0.0;
            this.pickerGlowEffect.Opacity = 0.0;
            this.statusText.Opacity       = 0.0;

            NameScope.SetNameScope(this, new NameScope());
            this.RegisterName("PresetGlowEffect", this.presetGlowEffect);
            this.RegisterName("PickerGlowEffect", this.pickerGlowEffect);
            this.RegisterName("StatusText", this.statusText);

            var storyboard = (Storyboard)this.FindResource("statusTextStoryboard");

            storyboard.Completed += (sender, args) =>
            {
                this.statusText.Visibility = Visibility.Collapsed;
            };

            var presetGlowFadeUp = new DoubleAnimation
            {
                From     = 0.0,
                To       = 1.0,
                Duration = new Duration(TimeSpan.FromSeconds(0.1))
            };

            var presetGlowFadeDown = new DoubleAnimation
            {
                From      = 1.0,
                To        = 0.0,
                BeginTime = TimeSpan.FromSeconds(0.1),
                Duration  = new Duration(TimeSpan.FromSeconds(1.6))
            };

            this.presetGlowStoryboard = new Storyboard();
            this.presetGlowStoryboard.Children.Add(presetGlowFadeUp);
            this.presetGlowStoryboard.Children.Add(presetGlowFadeDown);

            Storyboard.SetTargetName(presetGlowFadeUp, "PresetGlowEffect");
            Storyboard.SetTargetProperty(presetGlowFadeUp, new PropertyPath("Opacity"));
            Storyboard.SetTargetName(presetGlowFadeDown, "PresetGlowEffect");
            Storyboard.SetTargetProperty(presetGlowFadeDown, new PropertyPath("Opacity"));

            var pickerGlowFadeUp = new DoubleAnimation
            {
                From     = 0.0,
                To       = 1.0,
                Duration = new Duration(TimeSpan.FromSeconds(0.1))
            };

            var pickerGlowFadeDown = new DoubleAnimation
            {
                From      = 1.0,
                To        = 0.0,
                BeginTime = TimeSpan.FromSeconds(0.1),
                Duration  = new Duration(TimeSpan.FromSeconds(1.6))
            };

            this.pickerGlowStoryboard = new Storyboard();
            this.pickerGlowStoryboard.Children.Add(pickerGlowFadeUp);
            this.pickerGlowStoryboard.Children.Add(pickerGlowFadeDown);

            Storyboard.SetTargetName(pickerGlowFadeUp, "PickerGlowEffect");
            Storyboard.SetTargetProperty(pickerGlowFadeUp, new PropertyPath("Opacity"));
            Storyboard.SetTargetName(pickerGlowFadeDown, "PickerGlowEffect");
            Storyboard.SetTargetProperty(pickerGlowFadeDown, new PropertyPath("Opacity"));

            this.Loaded += (e, o) =>
            {
                this.RestoredWindowState = this.WindowState;
            };

            this.statusService.MessageShown += (o, e) =>
            {
                this.ShowStatusMessage(e.Value);
            };
        }
Пример #4
0
		/// <summary>
		/// Adds the given source to the encode queue.
		/// </summary>
		/// <param name="source">The path to the source file to encode.</param>
		/// <param name="destination">The destination path for the encoded file.</param>
		/// <param name="presetName">The name of the preset to use to encode.</param>
		/// <returns>True if the item was successfully queued for processing.</returns>
		public void Process(string source, string destination, string presetName)
		{
			if (string.IsNullOrWhiteSpace(source))
			{
				throw new ArgumentException("source cannot be null or empty.");
			}

			if (string.IsNullOrWhiteSpace(destination) && !this.EnsureDefaultOutputFolderSet())
			{
				throw new ArgumentException("If destination is not set, the default output folder must be set.");
			}

			if (destination != null && !Utilities.IsValidFullPath(destination))
			{
				throw new ArgumentException("Destination path is not valid: " + destination);
			}

			VCProfile profile = this.presetsViewModel.GetProfileByName(presetName);
			if (profile == null)
			{
				throw new ArgumentException("Cannot find preset: " + presetName);
			}

			var jobVM = new EncodeJobViewModel(new VCJob
				{
					SourcePath = source,
					SourceType = Utilities.GetSourceType(source),
					Title = 1,
					RangeType = VideoRangeType.All,
					EncodingProfile = profile,
					ChosenAudioTracks = new List<int> { 1 },
					OutputPath = destination,
                    UseDefaultChapterNames = true
				});

			jobVM.PresetName = presetName;
			jobVM.ManualOutputPath = !string.IsNullOrWhiteSpace(destination);

			var scanMultipleDialog = new ScanMultipleDialogViewModel(new List<EncodeJobViewModel>{ jobVM });
			WindowManager.OpenDialog(scanMultipleDialog, this.main);

			VCJob job = jobVM.Job;

			Title title = jobVM.HandBrakeInstance.Titles.SingleOrDefault(t => t.TitleNumber == job.Title);
			if (title == null)
			{
				title = jobVM.HandBrakeInstance.Titles[0];
			}

			// Choose the correct audio/subtitle tracks based on settings
			this.AutoPickAudio(job, title);
			this.AutoPickSubtitles(job, title);

			// Now that we have the title and subtitles we can determine the final output file name
			if (string.IsNullOrWhiteSpace(destination))
			{
				// Exclude all current queued files if overwrite is disabled
				HashSet<string> excludedPaths;
				if (CustomConfig.WhenFileExistsBatch == WhenFileExists.AutoRename)
				{
					excludedPaths = this.GetQueuedFiles();
				}
				else
				{
					excludedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				}

				string pathToQueue = job.SourcePath;

				excludedPaths.Add(pathToQueue);
				string outputFolder = this.outputVM.GetOutputFolder(pathToQueue);
				string outputFileName = this.outputVM.BuildOutputFileName(pathToQueue, Utilities.GetSourceName(pathToQueue), job.Title, title.Duration, title.Chapters.Count, usesScan: false);
				string outputExtension = this.outputVM.GetOutputExtension();
				string queueOutputPath = Path.Combine(outputFolder, outputFileName + outputExtension);
				queueOutputPath = this.outputVM.ResolveOutputPathConflicts(queueOutputPath, excludedPaths, isBatch: true);

				job.OutputPath = queueOutputPath;
			}

			this.Queue(jobVM);

			if (!this.Encoding)
			{
				this.StartEncodeQueue();
			}
		}
Пример #5
0
		public void RemoveQueueJob(EncodeJobViewModel job)
		{
			this.EncodeQueue.Remove(job);

			if (this.Encoding)
			{
				this.totalTasks--;
				this.totalQueueCost -= job.Cost;

				if (this.totalTasks == 1)
				{
					this.EncodeQueue[0].IsOnlyItem = true;
				}
			}

			this.RaisePropertyChanged(() => this.QueuedTabHeader);
		}
Пример #6
0
		// Queues a list of files or video folders.
		public void QueueMultiple(IEnumerable<SourcePath> sourcePaths)
		{
			if (!this.EnsureDefaultOutputFolderSet())
			{
				return;
			}

			// Exclude all current queued files if overwrite is disabled
			HashSet<string> excludedPaths;
			if (CustomConfig.WhenFileExistsBatch == WhenFileExists.AutoRename)
			{
				excludedPaths = this.GetQueuedFiles();
			}
			else
			{
				excludedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			}

			var itemsToQueue = new List<EncodeJobViewModel>();
			foreach (SourcePath sourcePath in sourcePaths)
			{
				var job = new VCJob
				{
					SourcePath = sourcePath.Path,
					EncodingProfile = this.presetsViewModel.SelectedPreset.Preset.EncodingProfile.Clone(),
					Title = 1,
					RangeType = VideoRangeType.All,
					UseDefaultChapterNames = true
				};

				if (sourcePath.SourceType == SourceType.None)
				{
					if (Directory.Exists(sourcePath.Path))
					{
						job.SourceType = SourceType.VideoFolder;
					}
					else if (File.Exists(sourcePath.Path))
					{
						job.SourceType = SourceType.File;
					}
				}
				else
				{
					job.SourceType = sourcePath.SourceType;
				}

				if (job.SourceType != SourceType.None)
				{
					var jobVM = new EncodeJobViewModel(job);
					jobVM.SourceParentFolder = sourcePath.ParentFolder;
					jobVM.ManualOutputPath = false;
					jobVM.PresetName = this.presetsViewModel.SelectedPreset.DisplayName;
					itemsToQueue.Add(jobVM);
				}
			}

			// This dialog will scan the items in the list, calculating length.
			var scanMultipleDialog = new ScanMultipleDialogViewModel(itemsToQueue);
			WindowManager.OpenDialog(scanMultipleDialog, this.main);

			var failedFiles = new List<string>();
			foreach (EncodeJobViewModel jobVM in itemsToQueue)
			{
				// Skip over any cancelled jobs
				if (jobVM.HandBrakeInstance == null)
				{
					continue;
				}

				// Only queue items with a successful scan
				if (jobVM.HandBrakeInstance.Titles.Count > 0)
				{
					VCJob job = jobVM.Job;
					Title title = jobVM.HandBrakeInstance.Titles.SingleOrDefault(t => t.TitleNumber == job.Title);
					if (title == null)
					{
						title = jobVM.HandBrakeInstance.Titles[0];
					}

					// Choose the correct audio/subtitle tracks based on settings
					this.AutoPickAudio(job, title);
					this.AutoPickSubtitles(job, title);

					// Now that we have the title and subtitles we can determine the final output file name
					string fileToQueue = job.SourcePath;

					excludedPaths.Add(fileToQueue);
					string outputFolder = this.outputVM.GetOutputFolder(fileToQueue, jobVM.SourceParentFolder);
					string outputFileName = this.outputVM.BuildOutputFileName(fileToQueue, Utilities.GetSourceNameFile(fileToQueue), job.Title, title.Duration, title.Chapters.Count, usesScan: false);
					string outputExtension = this.outputVM.GetOutputExtension();
					string queueOutputPath = Path.Combine(outputFolder, outputFileName + outputExtension);
					queueOutputPath = this.outputVM.ResolveOutputPathConflicts(queueOutputPath, excludedPaths, isBatch: true);

					job.OutputPath = queueOutputPath;

					excludedPaths.Add(queueOutputPath);

					this.Queue(jobVM);
				}
				else
				{
					failedFiles.Add(jobVM.Job.SourcePath);
				}
			}

			if (failedFiles.Count > 0)
			{
				Utilities.MessageBox.Show(
					string.Format(MainRes.QueueMultipleScanErrorMessage, string.Join(Environment.NewLine, failedFiles)),
					MainRes.QueueMultipleScanErrorTitle,
					MessageBoxButton.OK,
					MessageBoxImage.Warning);
			}
		}
Пример #7
0
		/// <summary>
		/// Queues the given Job. Assumed that the job has an associated HandBrake instance and populated Length.
		/// </summary>
		/// <param name="encodeJobVM">The job to add.</param>
		public void Queue(EncodeJobViewModel encodeJobVM)
		{
			if (this.Encoding)
			{
				if (this.totalTasks == 1)
				{
					this.EncodeQueue[0].IsOnlyItem = false;
				}

				this.totalTasks++;
				this.totalQueueCost += encodeJobVM.Cost;
			}

			this.EncodeQueue.Add(encodeJobVM);

			this.profileEditedSinceLastQueue = false;

			this.RaisePropertyChanged(() => this.QueuedTabHeader);

			// Select the Queued tab.
			if (this.SelectedTabIndex != QueuedTabIndex)
			{
				this.SelectedTabIndex = QueuedTabIndex;
			}
		}