Exemple #1
0
        public VCJob Clone()
        {
            var clone = new VCJob
            {
                SourceType             = this.SourceType,
                SourcePath             = this.SourcePath,
                Title                  = this.Title,
                Angle                  = this.Angle,
                RangeType              = this.RangeType,
                ChapterStart           = this.ChapterStart,
                ChapterEnd             = this.ChapterEnd,
                SecondsStart           = this.SecondsStart,
                SecondsEnd             = this.SecondsEnd,
                FramesStart            = this.FramesStart,
                FramesEnd              = this.FramesEnd,
                ChosenAudioTracks      = new List <int>(this.ChosenAudioTracks),
                Subtitles              = this.Subtitles,
                UseDefaultChapterNames = this.UseDefaultChapterNames,
                OutputPath             = this.OutputPath,
                EncodingProfile        = this.EncodingProfile,
                Length                 = this.Length
            };

            return(clone);
        }
Exemple #2
0
		public EncodeJobViewModel(VCJob job)
		{
			this.job = job;

			Messenger.Default.Register<ScanningChangedMessage>(
				this,
				message =>
					{
						this.EditQueueJobCommand.RaiseCanExecuteChanged();
					});
		}
		public QueueTitlesDialogViewModel(List<Title> allTitles)
		{
			this.main = Ioc.Container.GetInstance<MainViewModel>();

			this.selectedTitles = new ObservableCollection<TitleSelectionViewModel>();
			this.selectRange = Config.QueueTitlesUseRange;
			this.startRange = Config.QueueTitlesStartTime;
			this.endRange = Config.QueueTitlesEndTime;
			this.titleStartOverrideEnabled = Config.QueueTitlesUseTitleOverride;
			this.titleStartOverride = Config.QueueTitlesTitleOverride;
			this.directoryOverrideEnabled = Config.QueueTitlesUseDirectoryOverride;
			this.directoryOverride = Config.QueueTitlesDirectoryOverride;
			this.nameOverrideEnabled = Config.QueueTitlesUseNameOverride;
			this.nameOverride = Config.QueueTitlesNameOverride;

			this.titles = new List<TitleSelectionViewModel>();
			foreach (Title title in allTitles)
			{
				var titleVM = new TitleSelectionViewModel(title, this);
				this.titles.Add(titleVM);
			}

			// Perform range selection if enabled.
			if (this.selectRange)
			{
				this.SetSelectedFromRange();
			}

			this.selectedTitles.CollectionChanged +=
				(sender, args) =>
			    {
					this.RaisePropertyChanged(() => this.TitleDetailsVisible);

					if (this.selectedTitles.Count == 1)
					{
						Title title = this.selectedTitles[0].Title;

						// Do preview
						var previewProfile =
							new VCProfile
							{
								CustomCropping = true,
								Cropping = new Cropping(),
								VideoEncoder = "x264",
								AudioEncodings = new List<AudioEncoding>()
							};

						var previewJob =
							new VCJob
							{
								RangeType = VideoRangeType.All,
								Title = title.TitleNumber,
								EncodingProfile = previewProfile
							};

						this.PreviewImage = this.main.ScanInstance.GetPreview(previewJob.HbJob, 2);
						this.RaisePropertyChanged(() => this.TitleText);
					}
			    };
		}
Exemple #4
0
		private void RefreshPreviews()
		{
			this.originalScanInstance = this.ScanInstance;

			this.job = this.mainViewModel.EncodeJob;
			VCProfile profile = this.job.EncodingProfile;

			int width, height, parWidth, parHeight;
			this.ScanInstance.GetSize(this.job.HbJob, out width, out height, out parWidth, out parHeight);

			// If we're rotating by 90 degrees, swap width and height for sizing purposes.
            if (profile.Rotation == VCPictureRotation.Clockwise90 || profile.Rotation == VCPictureRotation.Clockwise270)
			{
				int temp = width;
				width = height;
				height = temp;

				temp = parWidth;
				parWidth = parHeight;
				parHeight = temp;
			}

			if (parWidth <= 0 || parHeight <= 0)
			{
				this.HasPreview = false;
				this.Title = PreviewRes.NoVideoSourceTitle;

				Ioc.Container.GetInstance<ILogger>().LogError("HandBrake returned a negative pixel aspect ratio. Cannot show preview.");
				return;
			}

			this.PreviewDisplayHeight = height;
			this.PreviewDisplayWidth = width * ((double)parWidth / parHeight);

			// Update the number of previews.
			this.previewCount = this.ScanInstance.PreviewCount;
			if (this.selectedPreview >= this.previewCount)
			{
				this.selectedPreview = this.previewCount - 1;
				this.RaisePropertyChanged(() => this.SelectedPreview);
			}

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

			this.HasPreview = true;

			lock (this.imageSync)
			{
				this.previewImageCache = new BitmapSource[this.previewCount];
				updateVersion++;

				// Clear main work queue.
				this.previewImageWorkQueue.Clear();

				this.imageFileCacheFolder = Path.Combine(Utilities.ImageCacheFolder,
														 Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture),
														 updateVersion.ToString(CultureInfo.InvariantCulture));
				if (!Directory.Exists(this.imageFileCacheFolder))
				{
					Directory.CreateDirectory(this.imageFileCacheFolder);
				}

				// Clear old images out of the file cache.
				this.ClearImageFileCache();

				this.imageFileSync = new List<object>(this.previewCount);
				for (int i = 0; i < this.previewCount; i++)
				{
					this.imageFileSync.Add(new object());
				}

				this.BeginBackgroundImageLoad();
			}

			if (parWidth == parHeight)
			{
				this.Title = string.Format(PreviewRes.PreviewWindowTitleSimple, width, height);
			}
			else
			{
				this.Title = string.Format(
					PreviewRes.PreviewWindowTitleComplex,
					Math.Round(this.PreviewDisplayWidth),
					Math.Round(this.PreviewDisplayHeight),
					width,
					height);
			}
		}
Exemple #5
0
		private void LoadVideoSourceMetadata(VCJob job, VideoSourceMetadata metadata)
		{
			this.SourceName = metadata.Name;
			this.SourcePath = job.SourcePath;

			if (job.SourceType == SourceType.Dvd)
			{
				this.SelectedSource = new SourceOption { Type = SourceType.Dvd, DriveInfo = metadata.DriveInfo };
			}
			else
			{
				this.SelectedSource = new SourceOption { Type = job.SourceType };
			}
		}
		// Automatically pick the correct subtitles on the given job.
		// Only relies on input from settings and the current title.
		private void AutoPickSubtitles(VCJob job, Title title, bool useCurrentContext = false)
		{
			job.Subtitles = new Subtitles { SourceSubtitles = new List<SourceSubtitle>(), SrtSubtitles = new List<SrtSubtitle>() };
			switch (CustomConfig.AutoSubtitle)
			{
				case AutoSubtitleType.Disabled:
					// Only pick subtitles when we have previous context.
					if (useCurrentContext)
					{
						foreach (SourceSubtitle sourceSubtitle in this.main.CurrentSubtitles.SourceSubtitles)
						{
							if (sourceSubtitle.TrackNumber == 0)
							{
								job.Subtitles.SourceSubtitles.Add(sourceSubtitle.Clone());
							}
							else if (
								title.Subtitles.Count > sourceSubtitle.TrackNumber - 1 &&
								this.main.SelectedTitle.Subtitles[sourceSubtitle.TrackNumber - 1].LanguageCode == title.Subtitles[sourceSubtitle.TrackNumber - 1].LanguageCode)
							{
								job.Subtitles.SourceSubtitles.Add(sourceSubtitle.Clone());
							}
						}
					}
					break;
				case AutoSubtitleType.ForeignAudioSearch:
					job.Subtitles.SourceSubtitles.Add(
						new SourceSubtitle
						{
							TrackNumber = 0,
							BurnedIn = Config.AutoSubtitleBurnIn,
							Forced = true,
							Default = true
						});
					break;
				case AutoSubtitleType.Language:
					string languageCode = Config.SubtitleLanguageCode;
					bool audioSame = false;
					bool burnIn = Config.AutoSubtitleLanguageBurnIn;
					if (job.ChosenAudioTracks.Count > 0 && title.AudioTracks.Count > 0)
					{
						if (title.AudioTracks[job.ChosenAudioTracks[0] - 1].LanguageCode == languageCode)
						{
							audioSame = true;
						}
					}

					if (!Config.AutoSubtitleOnlyIfDifferent || !audioSame)
					{
						List<Subtitle> nativeSubtitles = title.Subtitles.Where(subtitle => subtitle.LanguageCode == languageCode).ToList();
						if (nativeSubtitles.Count > 0)
						{
							if (Config.AutoSubtitleAll)
							{
								foreach (Subtitle subtitle in nativeSubtitles)
								{
									job.Subtitles.SourceSubtitles.Add(new SourceSubtitle
									{
										BurnedIn = false,
										Default = false,
										Forced = false,
										TrackNumber = subtitle.TrackNumber
									});
								}
							}
							else
							{
								job.Subtitles.SourceSubtitles.Add(new SourceSubtitle
								{
									BurnedIn = burnIn,
									Default = false,
									Forced = false,
									TrackNumber = nativeSubtitles[0].TrackNumber
								});
							}
						}
					}

					break;
				case AutoSubtitleType.All:
					foreach (Subtitle subtitle in title.Subtitles)
					{
						job.Subtitles.SourceSubtitles.Add(
							new SourceSubtitle
							{
								TrackNumber = subtitle.TrackNumber,
								BurnedIn = false,
								Default = false,
								Forced = false
							});
					}
					break;
				default:
					throw new ArgumentOutOfRangeException();
			}
		}
		// Automatically pick the correct audio on the given job.
		// Only relies on input from settings and the current title.
		private void AutoPickAudio(VCJob job, Title title, bool useCurrentContext = false)
		{
			job.ChosenAudioTracks = new List<int>();
			switch (CustomConfig.AutoAudio)
			{
				case AutoAudioType.Disabled:
					if (title.AudioTracks.Count > 0)
					{
						if (useCurrentContext)
						{
							// With previous context, pick similarly
							foreach (AudioChoiceViewModel audioVM in this.main.AudioChoices)
							{
								int audioIndex = audioVM.SelectedIndex;

								if (title.AudioTracks.Count > audioIndex && this.main.SelectedTitle.AudioTracks[audioIndex].LanguageCode == title.AudioTracks[audioIndex].LanguageCode)
								{
									job.ChosenAudioTracks.Add(audioIndex + 1);
								}
							}

							// If we didn't manage to match any existing audio tracks, use the first audio track.
							if (this.main.AudioChoices.Count > 0 && job.ChosenAudioTracks.Count == 0)
							{
								job.ChosenAudioTracks.Add(1);
							}
						}
						else
						{
							// With no previous context, just pick the first track
							job.ChosenAudioTracks.Add(1);
						}
					}

					break;
				case AutoAudioType.Language:
					List<AudioTrack> nativeTracks = title.AudioTracks.Where(track => track.LanguageCode == Config.AudioLanguageCode).ToList();
					if (nativeTracks.Count > 0)
					{
						if (Config.AutoAudioAll)
						{
							foreach (AudioTrack audioTrack in nativeTracks)
							{
								job.ChosenAudioTracks.Add(audioTrack.TrackNumber);
							}
						}
						else
						{
							job.ChosenAudioTracks.Add(nativeTracks[0].TrackNumber);
						}
					}
					break;
				case AutoAudioType.All:
					foreach (AudioTrack audioTrack in title.AudioTracks)
					{
						job.ChosenAudioTracks.Add(audioTrack.TrackNumber);
					}
					break;
				default:
					throw new ArgumentOutOfRangeException();
			}

			// If none get chosen, pick the first one.
			if (job.ChosenAudioTracks.Count == 0 && title.AudioTracks.Count > 0)
			{
				job.ChosenAudioTracks.Add(1);
			}
		}
		// 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);
			}
		}
Exemple #9
0
		public void StartEncode(VCJob job, ILogger logger, bool preview, int previewNumber, int previewSeconds, double overallSelectedLengthSeconds)
		{
			this.logger = logger;
			this.logger.Log("Starting encode in-process");

			this.encoding = true;

			this.encodeStartEvent = new ManualResetEventSlim(false);
			this.encodeEndEvent = new ManualResetEventSlim(false);

			this.instance = new HandBrakeInstance();
			this.instance.Initialize(Config.LogVerbosity);

			this.instance.ScanCompleted += (o, e) =>
			{
				try
				{
					Title encodeTitle = this.instance.Titles.FirstOrDefault(title => title.TitleNumber == job.Title);

					if (encodeTitle != null)
					{
						lock (this.encoderLock)
						{
							this.instance.StartEncode(job.HbJob, preview, previewNumber, previewSeconds, overallSelectedLengthSeconds, Config.PreviewCount);
							this.IsEncodeStarted = true;
							if (this.EncodeStarted != null)
							{
								this.EncodeStarted(this, new EventArgs());
							}

							this.encodeStartEvent.Set();
						}
					}
					else
					{
						if (this.EncodeCompleted != null)
						{
							this.EncodeCompleted(this, new EncodeCompletedEventArgs { Error = true });
						}

						this.encodeStartEvent.Set();
						this.encodeEndEvent.Set();
					}
				}
				catch (Exception exception)
				{
					this.logger.LogError("Encoding failed. Please report this error so it can be fixed in the future:" + Environment.NewLine + exception);
				}
			};

			this.instance.EncodeProgress += (o, e) =>
			{
				// Dispatch to avoid deadlocks on callbacks
				DispatchService.BeginInvoke(() =>
				{
					lock (this.encoderLock)
					{
						if (this.encoding && this.EncodeProgress != null)
						{
							this.EncodeProgress(this, e);
						}
					}
				});
			};

			this.instance.EncodeCompleted += (o, e) =>
			{
				if (this.encoding)
				{
					if (this.EncodeCompleted != null)
					{
						this.EncodeCompleted(this, e);
					}

					this.encoding = false;
				}

				this.encodeEndEvent.Set();
				this.instance.Dispose();
			};

			this.instance.StartScan(job.SourcePath, Config.PreviewCount, job.Title);

			this.encoding = true;
		}
Exemple #10
0
		public void StartEncode(VCJob job, ILogger logger, bool preview, int previewNumber, int previewSeconds, double overallSelectedLengthSeconds)
		{
			this.logger = logger;

			this.encodeStartEvent = new ManualResetEventSlim(false);
			this.encodeEndEvent = new ManualResetEventSlim(false);

			var task = new Task(() =>
				{
					this.lastWorkerCommunication = DateTimeOffset.UtcNow;
					this.pipeName = PipeNamePrefix + Guid.NewGuid().ToString();
					var startInfo = new ProcessStartInfo(
						"VidCoderWorker.exe",
						Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture) + " " + this.pipeName);
					startInfo.RedirectStandardOutput = true;
					startInfo.UseShellExecute = false;
					startInfo.CreateNoWindow = true;
					this.worker = Process.Start(startInfo);

					// We don't set this any more because the thread priority inside the worker process sets them to lower priority.
					this.worker.PriorityClass = CustomConfig.WorkerProcessPriority;

					// When the process writes out a line, its pipe server is ready and can be contacted for
					// work. Reading line blocks until this happens.
					this.logger.Log("Worker ready: " + this.worker.StandardOutput.ReadLine());
				    bool connectionSucceeded = false;

					this.logger.Log("Connecting to process " + this.worker.Id + " on pipe " + this.pipeName);
					lock (this.encoderLock)
					{
						this.ExecuteProxyOperation(() =>
							{
								connectionSucceeded = this.ConnectToPipe();
								if (!connectionSucceeded)
								{
									return;
								}

								this.channel.StartEncode(
									job.HbJob,
									preview,
									previewNumber,
									previewSeconds,
									overallSelectedLengthSeconds,
									Config.LogVerbosity,
									Config.PreviewCount,
									Config.EnableLibDvdNav);

								// After we do StartEncode (which can take a while), switch the timeout down to normal level to do pings
								var contextChannel = (IContextChannel)this.channel;
								contextChannel.OperationTimeout = TimeSpan.FromSeconds(PipeTimeoutSeconds);
							});
					}

					if (!connectionSucceeded)
					{
						this.EndEncode(error: true);
						return;
					}

			    	this.pingTimer = new Timer
					{
						AutoReset = true,
						Interval = PingTimerIntervalMs
					};

					this.pingTimer.Elapsed += (o, e) =>
					{
						lock (this.encoderLock)
						{
							if (!this.encoding)
							{
								return;
							}
						}

						if (this.encoding)
						{
							try
							{
								this.channel.Ping();

								lock (this.encoderLock)
								{
									this.lastWorkerCommunication = DateTimeOffset.UtcNow;
								}
							}
							catch (CommunicationException exception)
							{
								this.HandlePingError(exception);
							}
							catch (TimeoutException exception)
							{
								this.HandlePingError(exception);
							}
						}
					};

					this.pingTimer.Start();
			    });

			this.encoding = true;
			task.Start();
		}
Exemple #11
0
		public VCJob Clone()
		{
			var clone = new VCJob
			{
				SourceType = this.SourceType,
				SourcePath = this.SourcePath,
				Title = this.Title,
				Angle = this.Angle,
				RangeType = this.RangeType,
				ChapterStart = this.ChapterStart,
				ChapterEnd = this.ChapterEnd,
				SecondsStart = this.SecondsStart,
				SecondsEnd = this.SecondsEnd,
				FramesStart = this.FramesStart,
				FramesEnd = this.FramesEnd,
				ChosenAudioTracks = new List<int>(this.ChosenAudioTracks),
				Subtitles = this.Subtitles,
				UseDefaultChapterNames = this.UseDefaultChapterNames,
				OutputPath = this.OutputPath,
				EncodingProfile = this.EncodingProfile,
				Length = this.Length
			};

			return clone;
		}