示例#1
0
 /// <summary>
 /// Two lines maximum
 /// Text should be on one line if it fits
 /// </summary>
 public void Check(Subtitle subtitle, NetflixQualityController controller)
 {
     foreach (Paragraph p in subtitle.Paragraphs)
     {
         if (p.Text.SplitToLines().Count > 2)
         {
             var fixedParagraph = new Paragraph(p, false);
             fixedParagraph.Text = Utilities.AutoBreakLine(fixedParagraph.Text, controller.SingleLineMaxLength, controller.SingleLineMaxLength - 3, controller.Language);
             if (fixedParagraph.Text.SplitToLines().Count > 2)
             {
                 fixedParagraph = null; // cannot fix text
             }
             string comment = "Two lines maximum";
             controller.AddRecord(p, fixedParagraph, comment);
         }
         else if (p.Text.SplitToLines().Count == 2 && p.Text.Contains(Environment.NewLine) &&
                  p.Text.Replace(Environment.NewLine, " ").Replace("  ", " ").CountCharacters(false, Configuration.Settings.General.IgnoreArabicDiacritics) <= controller.SingleLineMaxLength &&
                  p.Text != Utilities.AutoBreakLine(p.Text, controller.Language))
         {
             var fixedParagraph = new Paragraph(p, false);
             fixedParagraph.Text = Utilities.AutoBreakLine(fixedParagraph.Text, controller.SingleLineMaxLength, controller.SingleLineMaxLength - 3, controller.Language);
             string comment = "Text can fit on one line";
             controller.AddRecord(p, fixedParagraph, comment);
         }
     }
 }
示例#2
0
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            if (controller.Language == "ja")
            {
                return;
            }

            double twoFramesGap = 1000.0 / controller.FrameRate * 2.0;
            int    halfSecGap   = (int)Math.Round(controller.FrameRate / 2, MidpointRounding.AwayFromZero);

            for (int index = 0; index < subtitle.Paragraphs.Count; index++)
            {
                var p    = subtitle.Paragraphs[index];
                var next = subtitle.GetParagraphOrDefault(index + 1);
                if (next == null)
                {
                    continue;
                }

                var gapInFrames = SubtitleFormat.MillisecondsToFrames(next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds);
                if (gapInFrames > 2 && gapInFrames < halfSecGap && !p.StartTime.IsMaxTime)
                {
                    var fixedParagraph = new Paragraph(p, false)
                    {
                        EndTime = { TotalMilliseconds = next.StartTime.TotalMilliseconds - twoFramesGap }
                    };
                    string comment = $"3-{halfSecGap - 1} frames gap => 2 frames gap";
                    controller.AddRecord(p, fixedParagraph, comment);
                }
            }
        }
        /// <summary>
        /// Minimum duration: 5/6 second (833 ms) - also see https://github.com/SubtitleEdit/plugins/issues/129
        /// </summary>
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            for (int index = 0; index < subtitle.Paragraphs.Count; index++)
            {
                var p = subtitle.Paragraphs[index];

                if (controller.Language == "ja")
                {
                    if (p.Duration.TotalMilliseconds < 500)
                    {
                        string comment = "Minimum duration: 0.5 second";
                        controller.AddRecord(p, p.StartTime.ToHHMMSSFF(), comment, p.Duration.TotalSeconds.ToString(CultureInfo.InvariantCulture));
                    }
                    continue;
                }

                var next = subtitle.GetParagraphOrDefault(index + 1);
                if (p.Duration.TotalMilliseconds < 833)
                {
                    Paragraph fixedParagraph = null;
                    if (next == null || next.StartTime.TotalMilliseconds > p.StartTime.TotalMilliseconds + 834)
                    {
                        // we can fix duration
                        fixedParagraph = new Paragraph(p, false);
                        fixedParagraph.Duration.TotalMilliseconds = 834;
                    }
                    string comment = "Minimum duration: 5/6 second (833 ms)";
                    controller.AddRecord(p, fixedParagraph, comment);
                }
            }
        }
        /// <summary>
        /// Speed - max 17 (for most languages) characters per second
        /// </summary>
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            var oldIgnoreWhiteSpace = Configuration.Settings.General.CharactersPerSecondsIgnoreWhiteSpace;

            try
            {
                Configuration.Settings.General.CharactersPerSecondsIgnoreWhiteSpace = false;
                foreach (Paragraph p in subtitle.Paragraphs)
                {
                    var jp = new Paragraph(p);
                    if (controller.Language == "ja")
                    {
                        jp.Text = HtmlUtil.RemoveHtmlTags(jp.Text, true);
                        jp.Text = NetflixImsc11Japanese.RemoveTags(jp.Text);
                    }
                    var charactersPerSeconds = Utilities.GetCharactersPerSecond(jp);
                    if (charactersPerSeconds > controller.CharactersPerSecond)
                    {
                        var fixedParagraph = new Paragraph(p, false);
                        while (Utilities.GetCharactersPerSecond(fixedParagraph) > controller.CharactersPerSecond)
                        {
                            fixedParagraph.EndTime.TotalMilliseconds++;
                        }
                        string comment = "Maximum " + controller.CharactersPerSecond + " characters per second";
                        controller.AddRecord(p, fixedParagraph, comment, charactersPerSeconds.ToString(CultureInfo.InvariantCulture));
                    }
                }
            }
            finally
            {
                Configuration.Settings.General.CharactersPerSecondsIgnoreWhiteSpace = oldIgnoreWhiteSpace;
            }
        }
示例#5
0
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            ICalcLength calc = CalcFactory.MakeCalculator(nameof(CalcAll));
            var         charactersPerSecond = controller.CharactersPerSecond;
            var         comment             = "Maximum " + charactersPerSecond + " characters per second";

            foreach (var p in subtitle.Paragraphs)
            {
                var jp = new Paragraph(p);
                if (controller.Language == "ja")
                {
                    jp.Text = HtmlUtil.RemoveHtmlTags(jp.Text, true);
                    jp.Text = NetflixImsc11Japanese.RemoveTags(jp.Text);
                }

                if (controller.Language == "ko")
                {
                    calc = CalcFactory.MakeCalculator(nameof(CalcCjk));
                }

                var charactersPerSeconds = Utilities.GetCharactersPerSecond(jp, calc);
                if (charactersPerSeconds > charactersPerSecond && !p.StartTime.IsMaxTime)
                {
                    var fixedParagraph = new Paragraph(p, false);
                    while (Utilities.GetCharactersPerSecond(fixedParagraph) > charactersPerSecond)
                    {
                        fixedParagraph.EndTime.TotalMilliseconds++;
                    }

                    controller.AddRecord(p, fixedParagraph, comment, FormattableString.Invariant($"CPS={charactersPerSeconds:0.##}"));
                }
            }
        }
示例#6
0
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            foreach (Paragraph p in subtitle.Paragraphs)
            {
                // Line endings
                if (LineEndingSpaceBefore.IsMatch(p.Text))
                {
                    AddWhiteSpaceWarning(p, controller, 1);
                }

                if (LineEndingSpaceAfter.IsMatch(p.Text))
                {
                    AddWhiteSpaceWarning(p, controller, p.Text.Length);
                }

                // Spaces before punctuation
                foreach (Match m in SpacesBeforePunctuation.Matches(p.Text))
                {
                    AddWhiteSpaceWarning(p, controller, m.Index + 1);
                }

                // 2+ consecutive spaces
                foreach (Match m in TwoPlusConsequentSpaces.Matches(p.Text))
                {
                    AddWhiteSpaceWarning(p, controller, m.Index);
                }
            }
        }
 public void Check(Subtitle subtitle, NetflixQualityController controller)
 {
     foreach (Paragraph p in subtitle.Paragraphs)
     {
         if (p.Text.Contains("i>", StringComparison.OrdinalIgnoreCase))
         {
             if (controller.AllowItalics)
             {
                 var fixedParagraph = new Paragraph(p, false);
                 fixedParagraph.Text = HtmlUtil.FixInvalidItalicTags(fixedParagraph.Text);
                 if (fixedParagraph.Text != p.Text)
                 {
                     string comment = "Fixed italics";
                     controller.AddRecord(p, fixedParagraph, comment);
                 }
             }
             else
             {
                 var fixedParagraph = new Paragraph(p, false);
                 fixedParagraph.Text = HtmlUtil.RemoveHtmlTags(fixedParagraph.Text);
                 if (fixedParagraph.Text != p.Text)
                 {
                     string comment = "Italics not allowed";
                     controller.AddRecord(p, fixedParagraph, comment);
                 }
             }
         }
     }
 }
        /// <summary>
        /// Speed - max 17 (for most languages) characters per second
        /// </summary>
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            var oldIgnoreWhiteSpace = Configuration.Settings.General.CharactersPerSecondsIgnoreWhiteSpace;

            try
            {
                foreach (Paragraph p in subtitle.Paragraphs)
                {
                    var charactersPerSeconds = Utilities.GetCharactersPerSecond(p);
                    if (charactersPerSeconds > controller.CharactersPerSecond)
                    {
                        var fixedParagraph = new Paragraph(p, false);
                        while (Utilities.GetCharactersPerSecond(fixedParagraph) > controller.CharactersPerSecond)
                        {
                            fixedParagraph.EndTime.TotalMilliseconds++;
                        }
                        string comment = "Maximum " + controller.CharactersPerSecond + " characters per second";
                        controller.AddRecord(p, fixedParagraph, comment, charactersPerSeconds.ToString(CultureInfo.InvariantCulture));
                    }
                }
            }
            finally
            {
                Configuration.Settings.General.CharactersPerSecondsIgnoreWhiteSpace = oldIgnoreWhiteSpace;
            }
        }
        /// <summary>
        /// Two frames gap minimum
        /// </summary>
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            if (controller.Language == "ja")
            {
                return;
            }

            for (int index = 0; index < subtitle.Paragraphs.Count; index++)
            {
                Paragraph p            = subtitle.Paragraphs[index];
                var       next         = subtitle.GetParagraphOrDefault(index + 1);
                double    twoFramesGap = 1000.0 / controller.FrameRate * 2.0;
                if (next != null && SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds + twoFramesGap) > SubtitleFormat.MillisecondsToFrames(next.StartTime.TotalMilliseconds) && !p.StartTime.IsMaxTime)
                {
                    var fixedParagraph = new Paragraph(p, false)
                    {
                        EndTime = { TotalMilliseconds = next.StartTime.TotalMilliseconds - twoFramesGap }
                    };
                    string comment;

                    if (p.EndTime.TotalMilliseconds > next.StartTime.TotalMilliseconds)
                    {
                        comment = "Minimum two frames gap (Overlapping)";
                    }
                    else
                    {
                        comment = "Minimum two frames gap";
                    }
                    controller.AddRecord(p, fixedParagraph, comment);
                }
            }
        }
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            if (controller.Language == "ja")
            {
                return;
            }

            for (int index = 0; index < subtitle.Paragraphs.Count; index++)
            {
                var p    = subtitle.Paragraphs[index];
                var next = subtitle.GetParagraphOrDefault(index + 1);
                if (next == null)
                {
                    continue;
                }

                double twoFramesGap = 1000.0 / controller.FrameRate * 2.0;
                var    gapInFrames  = SubtitleFormat.MillisecondsToFrames(next.StartTime.TotalMilliseconds) - SubtitleFormat.MillisecondsToFrames(p.EndTime.TotalMilliseconds);
                if (gapInFrames >= 3 && gapInFrames <= 11 && !p.StartTime.IsMaxTime)
                {
                    var fixedParagraph = new Paragraph(p, false)
                    {
                        EndTime = { TotalMilliseconds = next.StartTime.TotalMilliseconds - twoFramesGap }
                    };
                    string comment = "3-11 frames gap => 2 frames gap";
                    controller.AddRecord(p, fixedParagraph, comment);
                }
            }
        }
示例#11
0
        private static void AddWhiteSpaceWarning(Paragraph p, NetflixQualityController report, int pos)
        {
            string timeCode = p.StartTime.ToHHMMSSFF();
            string context  = NetflixQualityController.StringContext(p.Text, pos, 6);
            string comment  = string.Format(NetflixLanguage.WhiteSpaceCheckReport, pos);

            report.AddRecord(p, timeCode, context, comment);
        }
示例#12
0
        /// <summary>
        /// From 1 to 10, numbers should be written out: one, two, three, etc.
        /// </summary>
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            foreach (Paragraph p in subtitle.Paragraphs)
            {
                string newText = p.Text;
                var    m       = NumberOneToNine.Match(newText);
                while (m.Success)
                {
                    bool ok = newText.Length <= m.Index + 1 || newText.Length > m.Index + 1 && !":.".Contains(newText[m.Index + 1].ToString());
                    if (!ok && newText.Length > m.Index + 1)
                    {
                        var rest = newText.Substring(m.Index + 1);
                        if (rest == "." || rest == "?" || rest == "!" ||
                            rest == ".</i>" || rest == "?</i>" || rest == "!</i>" ||
                            rest == "." + Environment.NewLine || rest == "?" + Environment.NewLine || rest == "!" + Environment.NewLine ||
                            rest == ".</i>" + Environment.NewLine || rest == "?</i>" + Environment.NewLine || rest == "!</i>" + Environment.NewLine)
                        {
                            ok = true;
                        }
                    }
                    if (ok && m.Index > 0 && ":.".Contains(newText[m.Index - 1].ToString()))
                    {
                        ok = false;
                    }
                    if (ok)
                    {
                        newText = newText.Remove(m.Index, 1).Insert(m.Index, NetflixHelper.ConvertNumberToString(m.Value.Substring(0, 1), false, controller.Language));
                    }
                    m = NumberOneToNine.Match(newText, m.Index + 1);
                }

                m = NumberTen.Match(newText);
                while (m.Success)
                {
                    bool ok = newText.Length <= m.Index + 2 || newText.Length > m.Index + 2 && newText[m.Index + 2] != ':';
                    if (ok && m.Index > 0 && ":.".Contains(newText[m.Index - 1].ToString()))
                    {
                        ok = false;
                    }
                    if (ok)
                    {
                        newText = newText.Remove(m.Index, 2).Insert(m.Index, "ten");
                    }
                    m = NumberTen.Match(newText, m.Index + 1);
                }

                if (newText != p.Text)
                {
                    var fixedParagraph = new Paragraph(p, false)
                    {
                        Text = newText
                    };
                    string comment = "From 1 to 10, numbers should be written out: one, two, three, etc";
                    controller.AddRecord(p, fixedParagraph, comment);
                }
            }
        }
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            if (subtitle?.Header != null && subtitle.Header.Contains("ttp:frameRate="))
            {
                var xml = new XmlDocument {
                    XmlResolver = null
                };
                try
                {
                    xml.LoadXml(subtitle.Header);
                }
                catch
                {
                    return;
                }

                if (xml.DocumentElement == null)
                {
                    return;
                }

                const string ns    = "http://www.w3.org/ns/ttml";
                var          nsmgr = new XmlNamespaceManager(xml.NameTable);
                nsmgr.AddNamespace("ttml", ns);

                var frameRateAttr = xml.DocumentElement.Attributes["ttp:frameRate"];
                if (frameRateAttr == null)
                {
                    return;
                }

                double fr;
                if (!double.TryParse(frameRateAttr.Value, out fr))
                {
                    controller.AddRecord(null, null, $"Frame rate is invalid: \'{frameRateAttr.Value}\'");
                }

                var frameRateMultiplier = xml.DocumentElement.Attributes["ttp:frameRateMultiplier"];
                if (frameRateMultiplier != null)
                {
                    var arr = frameRateMultiplier.InnerText.Split();
                    if (arr.Length == 2 && Utilities.IsInteger(arr[0]) && Utilities.IsInteger(arr[1]) && int.Parse(arr[1]) > 0)
                    {
                        fr = double.Parse(arr[0]) * fr / double.Parse(arr[1]);
                        CheckFrameRate(fr, controller);
                    }
                }
                else
                {
                    if (Utilities.IsInteger(frameRateAttr.InnerText))
                    {
                        fr = double.Parse(frameRateAttr.InnerText);
                        CheckFrameRate(fr, controller);
                    }
                }
            }
        }
 private static void CheckFrameRate(double fr, NetflixQualityController controller)
 {
     foreach (var validFrameRate in ValidFrameRates)
     {
         if (Math.Abs(validFrameRate - fr) < 0.01)
         {
             return;
         }
     }
     controller.AddRecord(null, null, "Frame rate is invalid");
 }
示例#15
0
 /// <summary>
 /// Use a hyphen with or without a space to indicate two speakers in one subtitle
 /// </summary>
 public void Check(Subtitle subtitle, NetflixQualityController controller)
 {
     if (controller.DualSpeakersHasHyphenAndNoSpace)
     {
         RemoveSpaceAfterHyphenInDialogues(subtitle, controller);
     }
     else
     {
         AddSpaceAfterHyphenInDialogues(subtitle, controller);
     }
 }
 public void Check(Subtitle subtitle, NetflixQualityController controller)
 {
     foreach (Paragraph p in subtitle.Paragraphs)
     {
         if (p.Duration.TotalMilliseconds > 7000)
         {
             var fixedParagraph = new Paragraph(p, false);
             fixedParagraph.EndTime.TotalMilliseconds = fixedParagraph.StartTime.TotalMilliseconds + 7000;
             string comment = "Maximum duration: 7 seconds per subtitle event";
             controller.AddRecord(p, fixedParagraph, comment);
         }
     }
 }
        /// <summary>
        /// Use a hyphen without a space to indicate two speakers in one subtitle
        /// </summary>
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            if (!controller.DualSpeakersHasHypenAndNoSplace)
            {
                return;
            }

            foreach (Paragraph p in subtitle.Paragraphs)
            {
                var arr = p.Text.SplitToLines();
                if (arr.Length == 2 && p.Text.Contains("-"))
                {
                    string newText = p.Text;
                    if (arr[0].StartsWith("- ") && arr[1].StartsWith("- "))
                    {
                        newText = "-" + arr[0].Remove(0, 2) + Environment.NewLine + "-" + arr[1].Remove(0, 2);
                    }
                    else if (arr[0].StartsWith("<i>- ") && arr[1].StartsWith("<i>- "))
                    {
                        newText = "<i>-" + arr[0].Remove(0, 5) + Environment.NewLine + "<i>-" + arr[1].Remove(0, 5);
                    }
                    else if (arr[0].StartsWith("<i>- ") && arr[1].StartsWith("- "))
                    {
                        newText = "<i>-" + arr[0].Remove(0, 5) + Environment.NewLine + "-" + arr[1].Remove(0, 2);
                    }
                    else if (arr[0].StartsWith("- ") && arr[1].StartsWith("<i>- "))
                    {
                        newText = "-" + arr[0].Remove(0, 2) + Environment.NewLine + "<i>-" + arr[1].Remove(0, 5);
                    }
                    else if ((arr[0].StartsWith("-") || arr[0].StartsWith("<i>-")) && arr[1].StartsWith("- "))
                    {
                        newText = "-" + arr[0] + Environment.NewLine + "-" + arr[1].Remove(0, 2);
                    }
                    else if (arr[0].StartsWith("- ") && (arr[1].StartsWith("-") || arr[1].StartsWith("<i>-")))
                    {
                        newText = "-" + arr[0].Remove(0, 2) + Environment.NewLine + "-" + arr[1];
                    }

                    if (newText != p.Text)
                    {
                        var fixedParagraph = new Paragraph(p, false)
                        {
                            Text = newText
                        };
                        string comment = "Dual Speakers: Use a hyphen without a space";
                        controller.AddRecord(p, fixedParagraph, comment);
                    }
                }
            }
        }
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            string comment = "Use the single smart character (U+2026) as opposed to three dots/periods in a row";

            foreach (var paragraph in subtitle.Paragraphs)
            {
                if (paragraph.Text.Contains("..."))
                {
                    var fixedParagraph = new Paragraph(paragraph, false)
                    {
                        Text = paragraph.Text.Replace("...", "…")
                    };
                    controller.AddRecord(paragraph, fixedParagraph, comment);
                }
            }
        }
 /// <summary>
 /// Maximum 42 chars per line for the majority of languages.
 /// </summary>
 public void Check(Subtitle subtitle, NetflixQualityController controller)
 {
     foreach (var p in subtitle.Paragraphs)
     {
         foreach (var line in p.Text.SplitToLines())
         {
             if (HtmlUtil.RemoveHtmlTags(line, true).Length > controller.SingleLineMaxLength)
             {
                 var fixedParagraph = new Paragraph(p, false);
                 fixedParagraph.Text = Utilities.AutoBreakLine(fixedParagraph.Text, controller.SingleLineMaxLength, controller.SingleLineMaxLength - 3, controller.Language);
                 string comment = "Single line length > " + controller.SingleLineMaxLength;
                 controller.AddRecord(p, fixedParagraph, comment, line.Length.ToString(CultureInfo.InvariantCulture));
             }
         }
     }
 }
示例#20
0
 public void Check(Subtitle subtitle, NetflixQualityController controller)
 {
     foreach (var p in subtitle.Paragraphs)
     {
         foreach (var line in p.Text.SplitToLines())
         {
             if (controller.Language == "ja")
             {
                 var vertical = p.Text.Contains("{\\an7", StringComparison.Ordinal) || p.Text.Contains("{\\an9", StringComparison.Ordinal);
                 var text     = HtmlUtil.RemoveHtmlTags(line, true);
                 text = NetflixImsc11Japanese.RemoveTags(text);
                 if (vertical) // Vertical subtitles - Maximum 11 full-width characters per line
                 {
                     if (CalculateJapaneseLength(text) > 11)
                     {
                         var comment = "Single vertical line length > 11";
                         controller.AddRecord(p, p.StartTime.ToHHMMSSFF(), comment, line.Length.ToString(CultureInfo.InvariantCulture));
                     }
                 }
                 else // Horizontal subtitles - Maximum 13 full-width characters per line
                 {
                     if (CalculateJapaneseLength(text) > 13)
                     {
                         var comment = "Single horizontal line length > 13";
                         controller.AddRecord(p, p.StartTime.ToHHMMSSFF(), comment, line.Length.ToString(CultureInfo.InvariantCulture));
                     }
                 }
             }
             else if (controller.Language == "ko" && line.CountCharacters(nameof(CalcCjk)) > controller.SingleLineMaxLength)
             {
                 var fixedParagraph = new Paragraph(p, false);
                 fixedParagraph.Text = Utilities.AutoBreakLine(fixedParagraph.Text, controller.SingleLineMaxLength, controller.SingleLineMaxLength - 3, controller.Language);
                 var comment = "Single line length > " + controller.SingleLineMaxLength;
                 controller.AddRecord(p, fixedParagraph, comment, line.CountCharacters(nameof(CalcCjk)).ToString(CultureInfo.InvariantCulture));
             }
             else if (line.CountCharacters() > controller.SingleLineMaxLength)
             {
                 var fixedParagraph = new Paragraph(p, false);
                 fixedParagraph.Text = Utilities.AutoBreakLine(fixedParagraph.Text, controller.SingleLineMaxLength, controller.SingleLineMaxLength - 3, controller.Language);
                 var comment = "Single line length > " + controller.SingleLineMaxLength;
                 controller.AddRecord(p, fixedParagraph, comment, line.Length.ToString(CultureInfo.InvariantCulture));
             }
         }
     }
 }
示例#21
0
        private static void RemoveSpaceAfterHyphenInDialogues(Subtitle subtitle, NetflixQualityController controller)
        {
            foreach (Paragraph p in subtitle.Paragraphs)
            {
                var arr = p.Text.SplitToLines();
                if (arr.Count == 2 && p.Text.Contains("-"))
                {
                    string newText = p.Text;
                    if (arr[0].StartsWith("- ", StringComparison.Ordinal) && arr[1].StartsWith("- ", StringComparison.Ordinal))
                    {
                        newText = "-" + arr[0].Remove(0, 2) + Environment.NewLine + "-" + arr[1].Remove(0, 2);
                    }
                    else if (arr[0].StartsWith("<i>- ", StringComparison.Ordinal) && arr[1].StartsWith("<i>- ", StringComparison.Ordinal))
                    {
                        newText = "<i>-" + arr[0].Remove(0, 5) + Environment.NewLine + "<i>-" + arr[1].Remove(0, 5);
                    }
                    else if (arr[0].StartsWith("<i>- ", StringComparison.Ordinal) && arr[1].StartsWith("- ", StringComparison.Ordinal))
                    {
                        newText = "<i>-" + arr[0].Remove(0, 5) + Environment.NewLine + "-" + arr[1].Remove(0, 2);
                    }
                    else if (arr[0].StartsWith("- ", StringComparison.Ordinal) && arr[1].StartsWith("<i>- ", StringComparison.Ordinal))
                    {
                        newText = "-" + arr[0].Remove(0, 2) + Environment.NewLine + "<i>-" + arr[1].Remove(0, 5);
                    }
                    else if ((arr[0].StartsWith("-", StringComparison.Ordinal) || arr[0].StartsWith("<i>-", StringComparison.Ordinal)) && arr[1].StartsWith("- ", StringComparison.Ordinal))
                    {
                        newText = "-" + arr[0] + Environment.NewLine + "-" + arr[1].Remove(0, 2);
                    }
                    else if (arr[0].StartsWith("- ", StringComparison.Ordinal) && (arr[1].StartsWith("-", StringComparison.Ordinal) || arr[1].StartsWith("<i>-", StringComparison.Ordinal)))
                    {
                        newText = "-" + arr[0].Remove(0, 2) + Environment.NewLine + "-" + arr[1];
                    }

                    if (newText != p.Text)
                    {
                        var fixedParagraph = new Paragraph(p, false)
                        {
                            Text = newText
                        };
                        string comment = "Dual Speakers: Use a hyphen without a space";
                        controller.AddRecord(p, fixedParagraph, comment);
                    }
                }
            }
        }
示例#22
0
        private static void AddSpaceAfterHyphenInDialogues(Subtitle subtitle, NetflixQualityController controller)
        {
            var sub = new Subtitle(subtitle);

            for (int i = 0; i < sub.Paragraphs.Count; i++)
            {
                Paragraph p   = new Paragraph(sub.Paragraphs[i]);
                var       arr = p.Text.SplitToLines();
                if (arr.Count == 2 && p.Text.Contains("-") && arr[0].Length > 3 && arr[1].Length > 3)
                {
                    string newText = p.Text;
                    if (arr[0][0] == '-' && char.IsLetter(arr[0][1]) && (arr[1].StartsWith("-", StringComparison.Ordinal) || arr[1].StartsWith("<i>-", StringComparison.Ordinal)))
                    {
                        newText = arr[0].Insert(1, " ") + Environment.NewLine + arr[1];
                        arr     = newText.SplitToLines();
                    }
                    else if (arr[0].StartsWith("<i>-", StringComparison.Ordinal) && arr[0].Length > 5 && char.IsLetter(arr[0][4]) && (arr[1].StartsWith("-", StringComparison.Ordinal) || arr[1].StartsWith("<i>-", StringComparison.Ordinal)))
                    {
                        newText = arr[0].Insert(4, " ") + Environment.NewLine + arr[1];
                        arr     = newText.SplitToLines();
                    }

                    if (arr[1][0] == '-' && char.IsLetter(arr[1][1]) && (arr[0].StartsWith("-", StringComparison.Ordinal) || arr[0].StartsWith("<i>-", StringComparison.Ordinal)))
                    {
                        newText = arr[0] + Environment.NewLine + arr[1].Insert(1, " ");
                    }
                    else if (arr[1].StartsWith("<i>-", StringComparison.Ordinal) && arr[1].Length > 5 && char.IsLetter(arr[1][4]) && (arr[0].StartsWith("-", StringComparison.Ordinal) || arr[0].StartsWith("<i>-", StringComparison.Ordinal)))
                    {
                        newText = arr[0] + Environment.NewLine + arr[1].Insert(4, " ");
                    }

                    if (newText != p.Text)
                    {
                        var fixedParagraph = new Paragraph(p, false)
                        {
                            Text = newText
                        };
                        string comment = "Dual Speakers: Use a space after hyphen";
                        controller.AddRecord(p, fixedParagraph, comment);
                    }
                }
            }
        }
示例#23
0
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            if (controller.Language == "jp")
            {
                return;
            }

            foreach (Paragraph p in subtitle.Paragraphs)
            {
                string newText = p.Text;
                var    arr     = p.Text.SplitToLines();
                if (newText.StartsWith("(", StringComparison.Ordinal) && newText.EndsWith(")", StringComparison.Ordinal))
                {
                    newText = "[" + newText.Substring(1, newText.Length - 2) + "]";
                }
                else if (newText.StartsWith("{", StringComparison.Ordinal) && newText.EndsWith("}", StringComparison.Ordinal))
                {
                    newText = "[" + newText.Substring(1, newText.Length - 2) + "]";
                }
                else if (arr.Count == 2 && arr[0].StartsWith("-", StringComparison.Ordinal) && arr[1].StartsWith("-", StringComparison.Ordinal))
                {
                    if ((arr[0].StartsWith("-(", StringComparison.Ordinal) && arr[0].EndsWith(")", StringComparison.Ordinal)) || (arr[0].StartsWith("-{", StringComparison.Ordinal) && arr[0].EndsWith("}", StringComparison.Ordinal)))
                    {
                        arr[0] = "-[" + newText.Substring(2, newText.Length - 3) + "]";
                    }
                    if ((arr[1].StartsWith("-(", StringComparison.Ordinal) && arr[1].EndsWith(")", StringComparison.Ordinal)) || (arr[1].StartsWith("-{", StringComparison.Ordinal) && arr[1].EndsWith("}", StringComparison.Ordinal)))
                    {
                        arr[1] = "-[" + arr[1].Substring(2, arr[1].Length - 3) + "]";
                    }
                    newText = arr[0] + Environment.NewLine + arr[1];
                }

                if (newText != p.Text)
                {
                    var fixedParagraph = new Paragraph(p, false)
                    {
                        Text = newText
                    };
                    string comment = "Use brackets [ ] to enclose speaker IDs or sound effects";
                    controller.AddRecord(p, fixedParagraph, comment);
                }
            }
        }
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            foreach (Paragraph p in subtitle.Paragraphs)
            {
                string newText = p.Text;

                var m = NumberStart.Match(newText);
                while (m.Success)
                {
                    int length = m.Length - 2;
                    newText = newText.Remove(m.Index, length).Insert(m.Index, NetflixHelper.ConvertNumberToString(m.Value.Substring(0, length), true, controller.Language));
                    m       = NumberStart.Match(newText, m.Index + 1);
                }

                m = NumberStartInside.Match(newText);
                while (m.Success)
                {
                    int length = m.Length - 4;
                    newText = newText.Remove(m.Index + 2, length).Insert(m.Index + 2, NetflixHelper.ConvertNumberToString(m.Value.Substring(2, length), true, controller.Language));
                    m       = NumberStartInside.Match(newText, m.Index + 1);
                }

                m = NumberStartInside2.Match(newText);
                while (m.Success)
                {
                    int length = m.Length - 5;
                    newText = newText.Remove(m.Index + 3, length).Insert(m.Index + 3, NetflixHelper.ConvertNumberToString(m.Value.Substring(3, length), true, controller.Language));
                    m       = NumberStartInside2.Match(newText, m.Index + 1);
                }

                if (newText != p.Text)
                {
                    var fixedParagraph = new Paragraph(p, false)
                    {
                        Text = newText
                    };
                    string comment = "When a number begins a sentence, it should always be spelled out";
                    controller.AddRecord(p, fixedParagraph, comment);
                }
            }
        }
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            if (controller.Language == "ja")
            {
                return;
            }

            var dialogHelper = new DialogSplitMerge {
                DialogStyle = controller.SpeakerStyle
            };
            string comment = "Dual Speakers: Use a hyphen without a space";

            if (controller.SpeakerStyle == DialogType.DashBothLinesWithSpace)
            {
                comment = "Dual Speakers: Use a hyphen with a space";
            }
            else if (controller.SpeakerStyle == DialogType.DashSecondLineWithSpace)
            {
                comment = "Dual Speakers: Use a hyphen with a space to denote the second speaker only";
            }
            else if (controller.SpeakerStyle == DialogType.DashSecondLineWithoutSpace)
            {
                comment = "Dual Speakers: Use a hyphen without a space to denote the second speaker only";
            }

            for (int i = 0; i < subtitle.Paragraphs.Count; i++)
            {
                var    p       = subtitle.Paragraphs[i];
                string oldText = p.Text;
                string newText = dialogHelper.FixDashesAndSpaces(p.Text, p, subtitle.GetParagraphOrDefault(i - 1));
                if (newText != oldText)
                {
                    var fixedParagraph = new Paragraph(p, false)
                    {
                        Text = newText
                    };
                    controller.AddRecord(p, fixedParagraph, comment);
                }
            }
        }
示例#26
0
        public void Check(Subtitle subtitle, NetflixQualityController controller)
        {
            // Load allowed glyphs
            var allowedGlyphsSet = LoadNetflixGlyphs();

            foreach (Paragraph paragraph in subtitle.Paragraphs)
            {
                for (int pos = 0, actualPos = 0; pos < paragraph.Text.Length; pos += char.IsSurrogatePair(paragraph.Text, pos) ? 2 : 1, actualPos++)
                {
                    int curCodepoint = char.ConvertToUtf32(paragraph.Text, pos);

                    if (!allowedGlyphsSet.Contains(curCodepoint))
                    {
                        string timecode = paragraph.StartTime.ToHHMMSSFF();
                        string context  = NetflixQualityController.StringContext(paragraph.Text, pos, 6);
                        string comment  = string.Format(Configuration.Settings.Language.NetflixQualityCheck.GlyphCheckReport, $"U+{curCodepoint:X}", actualPos);

                        controller.AddRecord(paragraph, timecode, context, 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);
                }
            }
        }
        /// <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);
                }
            }
        }