Example #1
0
 internal string ToShortStringHHMMSSFF()
 {
     if (_time.Minutes == 0 && _time.Hours == 0)
     {
         return(string.Format("{0:00}:{1:00}", _time.Seconds, SubtitleFormat.MillisecondsToFrames(_time.Milliseconds)));
     }
     if (_time.Hours == 0)
     {
         return(string.Format("{0:00}:{1:00}:{2:00}", _time.Minutes, _time.Seconds, SubtitleFormat.MillisecondsToFrames(_time.Milliseconds)));
     }
     return(string.Format("{0:00}:{1:00}:{2:00}:{3:00}", _time.Hours, _time.Minutes, _time.Seconds, SubtitleFormat.MillisecondsToFrames(_time.Milliseconds)));
 }
Example #2
0
        public static int BridgeGaps(Subtitle subtitle, int minMsBetweenLines, bool divideEven, double maxMs, List <int> fixedIndexes, Dictionary <string, string> dic, bool useFrames)
        {
            int fixedCount = 0;

            if (minMsBetweenLines > maxMs)
            {
                string message = $"{nameof(DurationsBridgeGaps)}: {nameof(minMsBetweenLines)} cannot be larger than {nameof(maxMs)}!";
                SeLogger.Error(new InvalidOperationException(message), message);
                return(0);
            }

            int count = subtitle.Paragraphs.Count - 1;

            for (int i = 0; i < count; i++)
            {
                var cur  = subtitle.Paragraphs[i];
                var next = subtitle.Paragraphs[i + 1];

                double currentGap = next.StartTime.TotalMilliseconds - cur.EndTime.TotalMilliseconds;

                // there shouldn't be adjustment if current gaps is shorter or equal than minimum gap or greater than maximum gaps
                if (currentGap <= minMsBetweenLines || currentGap > maxMs)
                {
                    continue;
                }

                // next paragraph start-time will be pull to try to meet the current paragraph
                if (divideEven)
                {
                    next.StartTime.TotalMilliseconds -= currentGap / 2.0;
                }

                cur.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - minMsBetweenLines;
                if (fixedIndexes != null)
                {
                    fixedIndexes.Add(i);
                    fixedIndexes.Add(i + 1);
                }
                fixedCount++;

                double newGap = next.StartTime.TotalMilliseconds - cur.EndTime.TotalMilliseconds;
                if (useFrames)
                {
                    dic?.Add(cur.Id, $"{SubtitleFormat.MillisecondsToFrames(currentGap)} => {SubtitleFormat.MillisecondsToFrames(newGap)}");
                }
                else
                {
                    dic?.Add(cur.Id, $"{currentGap / TimeCode.BaseUnit:0.000} => {newGap / TimeCode.BaseUnit:0.000}");
                }
            }

            return(fixedCount);
        }
        public void MillisecondsToFrames()
        {
            Configuration.Settings.General.CurrentFrameRate = 23.976;
            var fr = SubtitleFormat.MillisecondsToFrames(100);

            Assert.AreEqual(2, fr);
            fr = SubtitleFormat.MillisecondsToFrames(999);
            Assert.AreEqual(24, fr);

            Configuration.Settings.General.CurrentFrameRate = 30;
            fr = SubtitleFormat.MillisecondsToFrames(100);
            Assert.AreEqual(3, fr);
            fr = SubtitleFormat.MillisecondsToFrames(999);
            Assert.AreEqual(30, fr);

            fr = SubtitleFormat.MillisecondsToFrames(2000);
            Assert.AreEqual(60, fr);
        }
Example #4
0
        /// <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 = "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);
                }
            }
        }
        internal static string GetParagraph(string template, string start, string end, string text, string translation, int number, string actor, TimeCode duration, string timeCodeTemplate, Paragraph p)
        {
            var cps = Utilities.GetCharactersPerSecond(p);
            var d   = duration.ToString();

            if (timeCodeTemplate == "ff" || timeCodeTemplate == "f")
            {
                d = SubtitleFormat.MillisecondsToFrames(duration.TotalMilliseconds).ToString(CultureInfo.InvariantCulture);
            }

            if (timeCodeTemplate == "zzz" || timeCodeTemplate == "zz" || timeCodeTemplate == "z")
            {
                d = duration.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
            }

            if (timeCodeTemplate == "sss" || timeCodeTemplate == "ss" || timeCodeTemplate == "s")
            {
                d = duration.Seconds.ToString(CultureInfo.InvariantCulture);
            }
            else if (timeCodeTemplate.EndsWith("ss.ff", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00}.{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss:ff", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00}:{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss,ff", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00},{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss;ff", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00};{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss.zzz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00}.{duration.Milliseconds:000}";
            }
            else if (timeCodeTemplate.EndsWith("ss:zzz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00}:{duration.Milliseconds:000}";
            }
            else if (timeCodeTemplate.EndsWith("ss,zzz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00},{duration.Milliseconds:000}";
            }
            else if (timeCodeTemplate.EndsWith("ss;zzz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00};{duration.Milliseconds:000}";
            }
            else if (timeCodeTemplate.EndsWith("ss.zz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00}.{Math.Round(duration.Milliseconds / 10.0):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss:zz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00}:{Math.Round(duration.Milliseconds / 10.0):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss,zz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00},{Math.Round(duration.Milliseconds / 10.0):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss;zz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00};{Math.Round(duration.Milliseconds / 10.0):00}";
            }

            var lines = text.SplitToLines();
            var line1 = string.Empty;
            var line2 = string.Empty;

            if (lines.Count > 0)
            {
                line1 = lines[0];
            }

            if (lines.Count > 1)
            {
                line2 = lines[1];
            }

            var s = template;

            s = s.Replace("{{", "@@@@_@@@{");
            s = s.Replace("}}", "}@@@_@@@@");
            s = string.Format(s, start, end, text, translation, number + 1, number, d, actor, line1, line2,
                              cps.ToString(CultureInfo.InvariantCulture).Replace(".", ","),
                              cps.ToString(CultureInfo.InvariantCulture),
                              text.Length,
                              p.Text.RemoveChar('\r', '\n').Length,
                              p.Text.RemoveChar('\r', '\n').Length + lines.Count - 1,
                              p.Text.RemoveChar('\r', '\n').Length + (lines.Count - 1) * 2);
            s = s.Replace("@@@@_@@@", "{");
            s = s.Replace("@@@_@@@@", "}");
            return(s);
        }
        public static string GetTimeCode(TimeCode timeCode, string template)
        {
            var t = template;
            var templateTrimmed = t.Trim();

            if (templateTrimmed == "ss")
            {
                t = t.Replace("ss", $"{timeCode.TotalSeconds:00}");
            }

            if (templateTrimmed == "s")
            {
                t = t.Replace("s", $"{timeCode.TotalSeconds}");
            }

            if (templateTrimmed == "zzz")
            {
                t = t.Replace("zzz", $"{timeCode.TotalMilliseconds:000}");
            }

            if (templateTrimmed == "z")
            {
                t = t.Replace("z", $"{timeCode.TotalMilliseconds}");
            }

            if (templateTrimmed == "ff")
            {
                t = t.Replace("ff", $"{SubtitleFormat.MillisecondsToFrames(timeCode.TotalMilliseconds)}");
            }

            var totalSeconds = (int)timeCode.TotalSeconds;

            if (t.StartsWith("ssssssss", StringComparison.Ordinal))
            {
                t = t.Replace("ssssssss", $"{totalSeconds:00000000}");
            }

            if (t.StartsWith("sssssss", StringComparison.Ordinal))
            {
                t = t.Replace("sssssss", $"{totalSeconds:0000000}");
            }

            if (t.StartsWith("ssssss", StringComparison.Ordinal))
            {
                t = t.Replace("ssssss", $"{totalSeconds:000000}");
            }

            if (t.StartsWith("sssss", StringComparison.Ordinal))
            {
                t = t.Replace("sssss", $"{totalSeconds:00000}");
            }

            if (t.StartsWith("ssss", StringComparison.Ordinal))
            {
                t = t.Replace("ssss", $"{totalSeconds:0000}");
            }

            if (t.StartsWith("sss", StringComparison.Ordinal))
            {
                t = t.Replace("sss", $"{totalSeconds:000}");
            }

            if (t.StartsWith("ss", StringComparison.Ordinal))
            {
                t = t.Replace("ss", $"{totalSeconds:00}");
            }

            var totalMilliseconds = (long)timeCode.TotalMilliseconds;

            if (t.StartsWith("zzzzzzzz", StringComparison.Ordinal))
            {
                t = t.Replace("zzzzzzzz", $"{totalMilliseconds:00000000}");
            }

            if (t.StartsWith("zzzzzzz", StringComparison.Ordinal))
            {
                t = t.Replace("zzzzzzz", $"{totalMilliseconds:0000000}");
            }

            if (t.StartsWith("zzzzzz", StringComparison.Ordinal))
            {
                t = t.Replace("zzzzzz", $"{totalMilliseconds:000000}");
            }

            if (t.StartsWith("zzzzz", StringComparison.Ordinal))
            {
                t = t.Replace("zzzzz", $"{totalMilliseconds:00000}");
            }

            if (t.StartsWith("zzzz", StringComparison.Ordinal))
            {
                t = t.Replace("zzzz", $"{totalMilliseconds:0000}");
            }

            if (t.StartsWith("zzz", StringComparison.Ordinal))
            {
                t = t.Replace("zzz", $"{totalMilliseconds:000}");
            }

            t = t.Replace("hh", $"{timeCode.Hours:00}");
            t = t.Replace("h", $"{timeCode.Hours}");
            t = t.Replace("mm", $"{timeCode.Minutes:00}");
            t = t.Replace("m", $"{timeCode.Minutes}");
            t = t.Replace("ss", $"{timeCode.Seconds:00}");
            t = t.Replace("s", $"{timeCode.Seconds}");
            t = t.Replace("zzz", $"{timeCode.Milliseconds:000}");
            t = t.Replace("zz", $"{Math.Round(timeCode.Milliseconds / 10.0):00}");
            t = t.Replace("z", $"{Math.Round(timeCode.Milliseconds / 100.0):0}");
            t = t.Replace("ff", $"{SubtitleFormat.MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds):00}");
            t = t.Replace("f", $"{SubtitleFormat.MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds)}");
            return(t);
        }
Example #8
0
        internal static string GetParagraph(string template, string start, string end, string text, string translation, int number, string actor, TimeCode duration, string timeCodeTemplate)
        {
            string d = duration.ToString();

            if (timeCodeTemplate == "ff" || timeCodeTemplate == "f")
            {
                d = SubtitleFormat.MillisecondsToFrames(duration.TotalMilliseconds).ToString(CultureInfo.InvariantCulture);
            }
            if (timeCodeTemplate == "zzz" || timeCodeTemplate == "zz" || timeCodeTemplate == "z")
            {
                d = duration.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
            }
            if (timeCodeTemplate == "sss" || timeCodeTemplate == "ss" || timeCodeTemplate == "s")
            {
                d = duration.Seconds.ToString(CultureInfo.InvariantCulture);
            }
            else if (timeCodeTemplate.EndsWith("ss.ff", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00}.{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss:ff", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00}:{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss,ff", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00},{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss;ff", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00};{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss;ff", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00};{SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss.zzz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00}.{duration.Milliseconds:000}";
            }
            else if (timeCodeTemplate.EndsWith("ss:zzz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00}:{duration.Milliseconds:000}";
            }
            else if (timeCodeTemplate.EndsWith("ss,zzz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00},{duration.Milliseconds:000}";
            }
            else if (timeCodeTemplate.EndsWith("ss;zzz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00};{duration.Milliseconds:000}";
            }
            else if (timeCodeTemplate.EndsWith("ss;zzz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00};{duration.Milliseconds:000}";
            }
            else if (timeCodeTemplate.EndsWith("ss.zz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00}.{Math.Round(duration.Milliseconds / 10.0):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss:zz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00}:{Math.Round(duration.Milliseconds / 10.0):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss,zz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00},{Math.Round(duration.Milliseconds / 10.0):00}";
            }
            else if (timeCodeTemplate.EndsWith("ss;zz", StringComparison.Ordinal))
            {
                d = $"{duration.Seconds:00};{Math.Round(duration.Milliseconds / 10.0):00}";
            }

            string s = template;

            s = s.Replace("{{", "@@@@_@@@{");
            s = s.Replace("}}", "}@@@_@@@@");
            s = string.Format(s, start, end, text, translation, number + 1, number, d, actor);
            s = s.Replace("@@@@_@@@", "{");
            s = s.Replace("@@@_@@@@", "}");
            return(s);
        }
Example #9
0
        public static string GetTimeCode(TimeCode timeCode, string template)
        {
            if (template.Trim() == "ss")
            {
                template = template.Replace("ss", string.Format("{0:00}", timeCode.TotalSeconds));
            }
            if (template.Trim() == "s")
            {
                template = template.Replace("s", string.Format("{0}", timeCode.TotalSeconds));
            }
            if (template.Trim() == "zzz")
            {
                template = template.Replace("zzz", string.Format("{0:000}", timeCode.TotalMilliseconds));
            }
            if (template.Trim() == "z")
            {
                template = template.Replace("z", string.Format("{0}", timeCode.TotalMilliseconds));
            }
            if (template.Trim() == "ff")
            {
                template = template.Replace("ff", string.Format("{0}", SubtitleFormat.MillisecondsToFrames(timeCode.TotalMilliseconds)));
            }

            if (template.StartsWith("ssssssss"))
            {
                template = template.Replace("ssssssss", string.Format("{0:00000000}", timeCode.TotalSeconds));
            }
            if (template.StartsWith("sssssss"))
            {
                template = template.Replace("sssssss", string.Format("{0:0000000}", timeCode.TotalSeconds));
            }
            if (template.StartsWith("ssssss"))
            {
                template = template.Replace("ssssss", string.Format("{0:000000}", timeCode.TotalSeconds));
            }
            if (template.StartsWith("sssss"))
            {
                template = template.Replace("sssss", string.Format("{0:00000}", timeCode.TotalSeconds));
            }
            if (template.StartsWith("ssss"))
            {
                template = template.Replace("ssss", string.Format("{0:0000}", timeCode.TotalSeconds));
            }
            if (template.StartsWith("sss"))
            {
                template = template.Replace("sss", string.Format("{0:000}", timeCode.TotalSeconds));
            }
            if (template.StartsWith("ss"))
            {
                template = template.Replace("ss", string.Format("{0:00}", timeCode.TotalSeconds));
            }

            if (template.StartsWith("zzzzzzzz"))
            {
                template = template.Replace("zzzzzzzz", string.Format("{0:00000000}", timeCode.TotalMilliseconds));
            }
            if (template.StartsWith("zzzzzzz"))
            {
                template = template.Replace("zzzzzzz", string.Format("{0:0000000}", timeCode.TotalMilliseconds));
            }
            if (template.StartsWith("zzzzzz"))
            {
                template = template.Replace("zzzzzz", string.Format("{0:000000}", timeCode.TotalMilliseconds));
            }
            if (template.StartsWith("zzzzz"))
            {
                template = template.Replace("zzzzz", string.Format("{0:00000}", timeCode.TotalMilliseconds));
            }
            if (template.StartsWith("zzzz"))
            {
                template = template.Replace("zzzz", string.Format("{0:0000}", timeCode.TotalMilliseconds));
            }
            if (template.StartsWith("zzz"))
            {
                template = template.Replace("zzz", string.Format("{0:000}", timeCode.TotalMilliseconds));
            }

            template = template.Replace("hh", string.Format("{0:00}", timeCode.Hours));
            template = template.Replace("h", string.Format("{0}", timeCode.Hours));
            template = template.Replace("mm", string.Format("{0:00}", timeCode.Minutes));
            template = template.Replace("m", string.Format("{0}", timeCode.Minutes));
            template = template.Replace("ss", string.Format("{0:00}", timeCode.Seconds));
            template = template.Replace("s", string.Format("{0}", timeCode.Seconds));
            template = template.Replace("zzz", string.Format("{0:000}", timeCode.Milliseconds));
            template = template.Replace("zz", string.Format("{0:00}", Math.Round(timeCode.Milliseconds / 10.0)));
            template = template.Replace("z", string.Format("{0:0}", Math.Round(timeCode.Milliseconds / 100.0)));
            template = template.Replace("ff", string.Format("{0:00}", SubtitleFormat.MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds)));
            template = template.Replace("f", string.Format("{0}", SubtitleFormat.MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds)));
            return(template);
        }
Example #10
0
        internal static string GetParagraph(string template, string start, string end, string text, string translation, int number, TimeCode duration, string timeCodeTemplate)
        {
            string d = duration.ToString();

            if (timeCodeTemplate == "ff" || timeCodeTemplate == "f")
            {
                d = SubtitleFormat.MillisecondsToFrames(duration.TotalMilliseconds).ToString();
            }
            if (timeCodeTemplate == "zzz" || timeCodeTemplate == "zz" || timeCodeTemplate == "z")
            {
                d = duration.TotalMilliseconds.ToString();
            }
            if (timeCodeTemplate == "sss" || timeCodeTemplate == "ss" || timeCodeTemplate == "s")
            {
                d = duration.Seconds.ToString();
            }
            else if (timeCodeTemplate.EndsWith("ss.ff"))
            {
                d = string.Format("{0:00}.{1:00}", duration.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds));
            }
            else if (timeCodeTemplate.EndsWith("ss:ff"))
            {
                d = string.Format("{0:00}:{1:00}", duration.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds));
            }
            else if (timeCodeTemplate.EndsWith("ss,ff"))
            {
                d = string.Format("{0:00},{1:00}", duration.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds));
            }
            else if (timeCodeTemplate.EndsWith("ss;ff"))
            {
                d = string.Format("{0:00};{1:00}", duration.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds));
            }
            else if (timeCodeTemplate.EndsWith("ss;ff"))
            {
                d = string.Format("{0:00};{1:00}", duration.Seconds, SubtitleFormat.MillisecondsToFramesMaxFrameRate(duration.Milliseconds));
            }
            else if (timeCodeTemplate.EndsWith("ss.zzz"))
            {
                d = string.Format("{0:00}.{1:000}", duration.Seconds, duration.Milliseconds);
            }
            else if (timeCodeTemplate.EndsWith("ss:zzz"))
            {
                d = string.Format("{0:00}:{1:000}", duration.Seconds, duration.Milliseconds);
            }
            else if (timeCodeTemplate.EndsWith("ss,zzz"))
            {
                d = string.Format("{0:00},{1:000}", duration.Seconds, duration.Milliseconds);
            }
            else if (timeCodeTemplate.EndsWith("ss;zzz"))
            {
                d = string.Format("{0:00};{1:000}", duration.Seconds, duration.Milliseconds);
            }
            else if (timeCodeTemplate.EndsWith("ss;zzz"))
            {
                d = string.Format("{0:00};{1:000}", duration.Seconds, duration.Milliseconds);
            }
            else if (timeCodeTemplate.EndsWith("ss.zz"))
            {
                d = string.Format("{0:00}.{1:00}", duration.Seconds, Math.Round(duration.Milliseconds / 10.0));
            }
            else if (timeCodeTemplate.EndsWith("ss:zz"))
            {
                d = string.Format("{0:00}:{1:00}", duration.Seconds, Math.Round(duration.Milliseconds / 10.0));
            }
            else if (timeCodeTemplate.EndsWith("ss,zz"))
            {
                d = string.Format("{0:00},{1:00}", duration.Seconds, Math.Round(duration.Milliseconds / 10.0));
            }
            else if (timeCodeTemplate.EndsWith("ss;zz"))
            {
                d = string.Format("{0:00};{1:00}", duration.Seconds, Math.Round(duration.Milliseconds / 10.0));
            }

            string s = template;

            s = s.Replace("{{", "@@@@_@@@{");
            s = s.Replace("}}", "}@@@_@@@@");
            s = string.Format(s, start, end, text, translation, number + 1, number, d);
            s = s.Replace("@@@@_@@@", "{");
            s = s.Replace("@@@_@@@@", "}");
            return(s);
        }
        /// <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);
                }
            }
        }
Example #13
0
        public void MillisecondsToFrames2()
        {
            var frames = SubtitleFormat.MillisecondsToFrames(499, 25);

            Assert.AreEqual(12, frames);
        }
Example #14
0
        public void MillisecondsToFrames1()
        {
            var frames = SubtitleFormat.MillisecondsToFrames(500, 25);

            Assert.AreEqual(13, frames);
        }
Example #15
0
 public string ToHHMMSSPeriodFF()
 {
     return(string.Format("{0:00}:{1:00}:{2:00}.{3:00}", _time.Hours, _time.Minutes, _time.Seconds, SubtitleFormat.MillisecondsToFrames(_time.Milliseconds)));
 }