private void RefreshCheckBoxes(string language) { _netflixQualityController = new NetflixQualityController { Language = language, VideoFileName = _videoFileName }; checkBoxNoItalics.Checked = !_netflixQualityController.AllowItalics; checkBoxNoItalics.Enabled = !_netflixQualityController.AllowItalics; var sceneChangesExists = false; if (_netflixQualityController.VideoExists) { if (SceneChangeHelper.FromDisk(_videoFileName).Count > 0) { sceneChangesExists = true; } } checkBoxSceneChange.Checked = _netflixQualityController.VideoExists && sceneChangesExists; checkBoxSceneChange.Enabled = _netflixQualityController.VideoExists && sceneChangesExists; var checkFrameRate = _subtitleFormat.GetType() == new NetflixTimedText().GetType(); checkBoxTtmlFrameRate.Checked = checkFrameRate; checkBoxTtmlFrameRate.Enabled = checkFrameRate; checkBoxDialogHypenNoSpace.Text = !_netflixQualityController.DualSpeakersHasHyphenAndNoSpace ? "Dual Speakers: Use a hyphen with a space" : "Dual Speakers: Use a hyphen without a space"; checkBox17CharsPerSecond.Text = string.Format(LanguageSettings.Current.NetflixQualityCheck.MaximumXCharsPerSecond, _netflixQualityController.CharactersPerSecond); checkBoxMaxLineLength.Text = string.Format(LanguageSettings.Current.NetflixQualityCheck.MaximumLineLength, _netflixQualityController.SingleLineMaxLength); }
private void RefreshCheckBoxes(string language) { _netflixQualityController = new NetflixQualityController { Language = language, VideoFileName = _videoFileName, FrameRate = _frameRate }; checkBoxNoItalics.Checked = !_netflixQualityController.AllowItalics; checkBoxNoItalics.Enabled = !_netflixQualityController.AllowItalics; int halfSecGapInFrames = (int)Math.Round(_frameRate / 2); checkBoxGapBridge.Text = $"Frame gap: 3 to {halfSecGapInFrames - 1} frames => 2 frames"; var sceneChangesExist = false; if (_netflixQualityController.VideoExists) { if (SceneChangeHelper.FromDisk(_videoFileName).Count > 0) { sceneChangesExist = true; } } checkBoxSceneChange.Checked = sceneChangesExist; checkBoxSceneChange.Enabled = sceneChangesExist; var checkFrameRate = _subtitleFormat.GetType() == new NetflixTimedText().GetType(); checkBoxTtmlFrameRate.Checked = checkFrameRate; checkBoxTtmlFrameRate.Enabled = checkFrameRate; var speakerStyle = _netflixQualityController.SpeakerStyle; var checkBoxSpeakerStyleText = "Dual Speakers: Use a hyphen without a space"; if (speakerStyle == DialogType.DashBothLinesWithSpace) { checkBoxSpeakerStyleText = "Dual Speakers: Use a hyphen with a space"; } else if (speakerStyle == DialogType.DashSecondLineWithSpace) { checkBoxSpeakerStyleText = "Dual Speakers: Use a hyphen with a space to denote the second speaker only"; } else if (speakerStyle == DialogType.DashSecondLineWithoutSpace) { checkBoxSpeakerStyleText = "Dual Speakers: Use a hyphen without a space to denote the second speaker only"; } checkBoxSpeakerStyle.Text = checkBoxSpeakerStyleText; checkBox17CharsPerSecond.Text = string.Format(LanguageSettings.Current.NetflixQualityCheck.MaximumXCharsPerSecond, _netflixQualityController.CharactersPerSecond); checkBoxMaxLineLength.Text = string.Format(LanguageSettings.Current.NetflixQualityCheck.MaximumLineLength, _netflixQualityController.SingleLineMaxLength); }
/// <summary> /// Check the newly-updated timing to Shot Changes rules. /// https://partnerhelp.netflixstudios.com/hc/en-us/articles/360051554394-Timed-Text-Style-Guide-Subtitle-Timing-Guidelines /// </summary> public void Check(Subtitle subtitle, NetflixQualityController controller) { if (!controller.VideoExists) { return; } var SceneChanges = SceneChangeHelper.FromDisk(controller.VideoFileName); if (SceneChanges == null || SceneChanges.Count == 0) { return; } const int twelveFramesGap = 12; double twoFramesGap = 1000.0 / controller.FrameRate * 2.0; foreach (Paragraph p in subtitle.Paragraphs) { var fixedParagraph = new Paragraph(p, false); string comment = string.Empty; List <double> previousStartSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) < SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds)).ToList(); List <double> nextStartSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) > SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds)).ToList(); List <double> previousEndSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) < SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).ToList(); List <double> nextEndSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) > SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).ToList(); var onSceneChange = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) == SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).FirstOrDefault(); if (previousStartSceneChanges.Count > 0) { double nearestStartPrevSceneChange = previousStartSceneChanges.Aggregate((x, y) => Math.Abs(x - p.StartTime.TotalSeconds) < Math.Abs(y - p.StartTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds - nearestStartPrevSceneChange * 1000) < twelveFramesGap) { fixedParagraph.StartTime.TotalMilliseconds = nearestStartPrevSceneChange * 1000; comment = "The in-cue is within 12 frames after the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (nextStartSceneChanges.Count > 0) { double nearestStartNextSceneChange = nextStartSceneChanges.Aggregate((x, y) => Math.Abs(x - p.StartTime.TotalSeconds) < Math.Abs(y - p.StartTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(nearestStartNextSceneChange * 1000 - p.StartTime.TotalMilliseconds) < twelveFramesGap) { fixedParagraph.StartTime.TotalMilliseconds = nearestStartNextSceneChange * 1000; comment = "The in-cue is within 12 frames before the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (previousEndSceneChanges.Count > 0) { double nearestEndPrevSceneChange = previousEndSceneChanges.Aggregate((x, y) => Math.Abs(x - p.EndTime.TotalSeconds) < Math.Abs(y - p.EndTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds - nearestEndPrevSceneChange * 1000) < twelveFramesGap) { fixedParagraph.EndTime.TotalMilliseconds = nearestEndPrevSceneChange * 1000 - twoFramesGap; comment = "The out-cue is within 12 frames after the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (nextEndSceneChanges.Count > 0) { double nearestEndNextSceneChange = nextEndSceneChanges.Aggregate((x, y) => Math.Abs(x - p.EndTime.TotalSeconds) < Math.Abs(y - p.EndTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(nearestEndNextSceneChange * 1000 - p.EndTime.TotalMilliseconds) - 1 < twelveFramesGap && SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds) != SubtitleFormat.MillisecondsToFrames(nearestEndNextSceneChange * 1000 - twoFramesGap)) { fixedParagraph.EndTime.TotalMilliseconds = nearestEndNextSceneChange * 1000 - twoFramesGap; comment = "The out-cue is within 12 frames of the last frame before the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (onSceneChange > 0) { fixedParagraph.EndTime.TotalMilliseconds = onSceneChange * 1000 - twoFramesGap; comment = "The out-cue is on the shot change (respect the two-frame gap)"; controller.AddRecord(p, fixedParagraph, comment); } } }
public void Check(Subtitle subtitle, NetflixQualityController controller) { if (!controller.VideoExists) { return; } var SceneChanges = SceneChangeHelper.FromDisk(controller.VideoFileName); if (SceneChanges == null || SceneChanges.Count == 0) { return; } int halfSecGapInFrames = (int)Math.Round(controller.FrameRate / 2); double twoFramesGap = 1000.0 / controller.FrameRate * 2.0; foreach (Paragraph p in subtitle.Paragraphs) { var fixedParagraph = new Paragraph(p, false); string comment = string.Empty; List <double> previousStartSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) < SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds)).ToList(); List <double> nextStartSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) > SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds)).ToList(); List <double> previousEndSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) < SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).ToList(); List <double> nextEndSceneChanges = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) > SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).ToList(); var onSceneChange = SceneChanges.Where(x => SubtitleFormat.MillisecondsToFrames(x * 1000) == SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds)).FirstOrDefault(); if (previousStartSceneChanges.Count > 0) { double nearestStartPrevSceneChange = previousStartSceneChanges.Aggregate((x, y) => Math.Abs(x - p.StartTime.TotalSeconds) < Math.Abs(y - p.StartTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(p.StartTime.TotalMilliseconds - nearestStartPrevSceneChange * 1000) < halfSecGapInFrames) { fixedParagraph.StartTime.TotalMilliseconds = nearestStartPrevSceneChange * 1000; comment = $"The in-cue is within {halfSecGapInFrames} frames after the shot change, snap the in-cue to the shot-change"; controller.AddRecord(p, fixedParagraph, comment); } } if (nextStartSceneChanges.Count > 0) { double nearestStartNextSceneChange = nextStartSceneChanges.Aggregate((x, y) => Math.Abs(x - p.StartTime.TotalSeconds) < Math.Abs(y - p.StartTime.TotalSeconds) ? x : y); var gapToSceneChange = SubtitleFormat.MillisecondsToFrames(nearestStartNextSceneChange * 1000 - p.StartTime.TotalMilliseconds); var threshold = (int)Math.Round(halfSecGapInFrames * 0.75); if (gapToSceneChange < halfSecGapInFrames) { if (gapToSceneChange < threshold) { fixedParagraph.StartTime.TotalMilliseconds = nearestStartNextSceneChange * 1000; comment = $"The in-cue is 1-{threshold - 1} frames before the shot change, snap the in-cue to the shot change"; } else { fixedParagraph.StartTime.TotalMilliseconds = nearestStartNextSceneChange * 1000 - (1000.0 / controller.FrameRate * halfSecGapInFrames); comment = $"The in-cue is {threshold}-{halfSecGapInFrames - 1} frames before the shot change, pull the in-cue to half a second ({halfSecGapInFrames} frames) before the shot-change"; } controller.AddRecord(p, fixedParagraph, comment); } } if (previousEndSceneChanges.Count > 0) { double nearestEndPrevSceneChange = previousEndSceneChanges.Aggregate((x, y) => Math.Abs(x - p.EndTime.TotalSeconds) < Math.Abs(y - p.EndTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds - nearestEndPrevSceneChange * 1000) < halfSecGapInFrames) { fixedParagraph.EndTime.TotalMilliseconds = nearestEndPrevSceneChange * 1000 - twoFramesGap; comment = $"The out-cue is within {halfSecGapInFrames} frames after the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (nextEndSceneChanges.Count > 0) { double nearestEndNextSceneChange = nextEndSceneChanges.Aggregate((x, y) => Math.Abs(x - p.EndTime.TotalSeconds) < Math.Abs(y - p.EndTime.TotalSeconds) ? x : y); if (SubtitleFormat.MillisecondsToFrames(nearestEndNextSceneChange * 1000 - p.EndTime.TotalMilliseconds) - 1 < halfSecGapInFrames && SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds) != SubtitleFormat.MillisecondsToFrames(nearestEndNextSceneChange * 1000 - twoFramesGap)) { fixedParagraph.EndTime.TotalMilliseconds = nearestEndNextSceneChange * 1000 - twoFramesGap; comment = $"The out-cue is within {halfSecGapInFrames} frames of the last frame before the shot change"; controller.AddRecord(p, fixedParagraph, comment); } } if (onSceneChange > 0) { fixedParagraph.EndTime.TotalMilliseconds = onSceneChange * 1000 - twoFramesGap; comment = "The out-cue is on the shot change, respect the two-frame gap"; controller.AddRecord(p, fixedParagraph, comment); } } }