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); }
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); } }; }
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); } }
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); } }
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; }
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(); }
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; }