Advertisement
ivandrofly

SubtitleEdit: RealTime.cs

Nov 6th, 2024 (edited)
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.69 KB | None | 0 0
  1. using Nikse.SubtitleEdit.Core.Common;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.Text;
  6.  
  7. namespace Nikse.SubtitleEdit.Core.SubtitleFormats
  8. {
  9.     public class RealTime : SubtitleFormat
  10.     {
  11.         private readonly StringBuilder _accumulator = new StringBuilder();
  12.  
  13.         public override string Extension => ".rt";
  14.  
  15.         public override string Name => "RealTime";
  16.  
  17.         private readonly ITimeParser _timeParser;
  18.  
  19.         public RealTime() : this(useStrictMode: true)
  20.         {
  21.         }
  22.  
  23.         public RealTime(bool useStrictMode) => _timeParser = useStrictMode ? (ITimeParser)new StrictTimeParser() : new RelaxTimeParser();
  24.  
  25.         public override string ToText(Subtitle subtitle, string title)
  26.         {
  27.             var sb = new StringBuilder();
  28.             sb.AppendLine("<Window" + Environment.NewLine +
  29.                 "  Width    = \"640\"" + Environment.NewLine +
  30.                 "  Height   = \"480\"" + Environment.NewLine +
  31.                 "  WordWrap = \"true\"" + Environment.NewLine +
  32.                 "  Loop     = \"true\"" + Environment.NewLine +
  33.                 "  bgcolor  = \"black\"" + Environment.NewLine +
  34.                 ">" + Environment.NewLine +
  35.                 "<Font" + Environment.NewLine +
  36.                 "  Color = \"white\"" + Environment.NewLine +
  37.                 "  Face  = \"Arial\"" + Environment.NewLine +
  38.                 "  Size  = \"+2\"" + Environment.NewLine +
  39.                 ">" + Environment.NewLine +
  40.                 "<center>" + Environment.NewLine +
  41.                 "<b>" + Environment.NewLine);
  42.             const string writeFormat = "<Time begin=\"{0}\" end=\"{1}\" /><clear/>{2}";
  43.             foreach (Paragraph p in subtitle.Paragraphs)
  44.             {
  45.                 //<Time begin="0:03:24.8" end="0:03:29.4" /><clear/>Man stjæler ikke fra Chavo, nej.
  46.                 sb.AppendLine(string.Format(writeFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), p.Text.Replace(Environment.NewLine, " ")));
  47.             }
  48.             sb.AppendLine("</b>");
  49.             sb.AppendLine("</center>");
  50.             return sb.ToString();
  51.         }
  52.  
  53.         private static string EncodeTimeCode(TimeCode time)
  54.         {
  55.             //0:03:24.8
  56.             return $"{time.Hours:0}:{time.Minutes:00}:{time.Seconds:00}.{time.Milliseconds / 100:0}";
  57.         }
  58.  
  59.         private Paragraph ParseLine(string line)
  60.         {
  61.             // <Time begin="1:19:04.2" end="1:19:08.6" /><clear/>I'm coming with you. You hear me, you ugly creature?
  62.             var tokenizer = new Tokenizer(line);
  63.             while (true)
  64.             {
  65.                 var ch = tokenizer.GetNextChar();
  66.                 if (ch == '\"')
  67.                 {
  68.                     return StartTime(ref tokenizer);
  69.                 }
  70.                 else if (ch == char.MaxValue)
  71.                 {
  72.                     return null;
  73.                 }
  74.             }
  75.         }
  76.  
  77.         private Paragraph StartTime(ref Tokenizer tokenizer)
  78.         {
  79.             while (true)
  80.             {
  81.                 var ch = tokenizer.GetNextChar();
  82.                 if (char.IsDigit(ch) || ch == ':' || ch == '.')
  83.                 {
  84.                     Append(ch);
  85.                 }
  86.                 else if (ch == '"' && _accumulator.Length > 0)
  87.                 {
  88.                     return EndTime(ref tokenizer, FlushAccumulator());
  89.                 }
  90.                 else if (ch == char.MaxValue)
  91.                 {
  92.                     return null;
  93.                 }
  94.             }
  95.         }
  96.        
  97.         private Paragraph EndTime(ref Tokenizer tokenizer, string st)
  98.         {
  99.             while (true)
  100.             {
  101.                 var ch = tokenizer.GetNextChar();
  102.                 if (char.IsDigit(ch) || ch == ':' || ch == '.')
  103.                 {
  104.                     Append(ch);
  105.                 }
  106.                 else if (ch == '"' && _accumulator.Length > 0)
  107.                 {
  108.                     return Text(ref tokenizer, st, FlushAccumulator());
  109.                 }
  110.                 else if (ch == char.MaxValue)
  111.                 {
  112.                     return null;
  113.                 }
  114.             }
  115.         }
  116.  
  117.         private Paragraph Text(ref Tokenizer tokenizer, string st, string et)
  118.         {
  119.             // <Time begin="1:19:04.2" end="1:19:08.6" /><clear/>I'm coming with you. You hear me, you ugly creature?
  120.             tokenizer.AdvanceIndex(" /><clear/>".Length);
  121.             while (true)
  122.             {
  123.                 var ch = tokenizer.GetNextChar();
  124.                 if (ch == char.MaxValue)
  125.                 {
  126.                     if (_timeParser.TryParse(st, out TimeSpan sts) && _timeParser.TryParse(et, out TimeSpan ets))
  127.                     {
  128.                         return new Paragraph(FlushAccumulator(), sts.TotalMilliseconds, ets.TotalMilliseconds);
  129.                     }
  130.                 }
  131.  
  132.                 Append(ch);
  133.             }
  134.         }
  135.        
  136.         private void Append(char ch) => _accumulator.Append(ch);
  137.  
  138.         public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
  139.         {
  140.             //<Time begin="0:03:24.8" end="0:03:29.4" /><clear/>Man stjæler ikke fra Chavo, nej.
  141.             subtitle.Paragraphs.Clear();
  142.             _errorCount = 0;
  143.             foreach (string line in lines)
  144.             {
  145.                 try
  146.                 {
  147.                     if (!line.StartsWith("<time begin=", StringComparison.OrdinalIgnoreCase))
  148.                     {
  149.                         continue;
  150.                     }
  151.  
  152.                     switch (ParseLine(line))
  153.                     {
  154.                         case Paragraph paragraph:
  155.                             subtitle.Paragraphs.Add(paragraph);
  156.                             break;
  157.  
  158.                         case null:
  159.                             if (++_errorCount >= lines.Count / 2)
  160.                             {
  161.                                 return;
  162.                             }
  163.                             break;
  164.                     }
  165.                 }
  166.                 catch
  167.                 {
  168.                     _errorCount++;
  169.                 }
  170.             }
  171.  
  172.             subtitle.Renumber();
  173.         }
  174.  
  175.         /// <summary>
  176.         /// Clears the accumulated text from the internal StringBuilder and returns the cleared text.
  177.         /// </summary>
  178.         /// <returns>A string containing the accumulated text before it was cleared.</returns>
  179.         private string FlushAccumulator()
  180.         {
  181.             var temp = _accumulator.ToString();
  182.             _accumulator.Clear();
  183.             return temp;
  184.         }
  185.  
  186.         /// <summary>
  187.         /// The <c>Tokenizer</c> struct is responsible for iterating through a string,
  188.         /// character by character, and providing methods to advance the reading index.
  189.         /// </summary>
  190.         private struct Tokenizer
  191.         {
  192.             private readonly string _line;
  193.             private int _index;
  194.  
  195.             public Tokenizer(string line) => (_index, _line) = (0, line);
  196.  
  197.             public char GetNextChar()
  198.             {
  199.                 if (_index < _line.Length)
  200.                 {
  201.                     return _line[_index++];
  202.                 }
  203.  
  204.                 return char.MaxValue;
  205.             }
  206.  
  207.             public void AdvanceIndex(int indexIncrement) => _index += indexIncrement;
  208.         }
  209.  
  210.         /// <summary>
  211.         /// The <c>ITimeParser</c> interface defines a method for parsing time strings
  212.         /// into <c>TimeSpan</c> objects.
  213.         /// </summary>
  214.         private interface ITimeParser
  215.         {
  216.             bool TryParse(string line, out TimeSpan time);
  217.         }
  218.  
  219.         /// <summary>
  220.         /// The <c>StrictTimeParser</c> class provides functionality to parse
  221.         /// time strings into <c>TimeSpan</c> objects using a strict format.
  222.         /// </summary>
  223.         private class StrictTimeParser : ITimeParser
  224.         {
  225.             private const string TimeFormat = @"h\:mm\:ss\.f";
  226.  
  227.             public bool TryParse(string timeStamp, out TimeSpan ts)
  228.             {
  229.                 return TimeSpan.TryParseExact(timeStamp, TimeFormat, CultureInfo.InvariantCulture, TimeSpanStyles.None, out ts);
  230.             }
  231.         }
  232.  
  233.         /// <summary>
  234.         /// The <c>RelaxTimeParser</c> class provides functionality to parse time strings
  235.         /// into <c>TimeSpan</c> objects using a relaxed format.
  236.         /// </summary>
  237.         private class RelaxTimeParser : ITimeParser
  238.         {
  239.             public bool TryParse(string timeStamp, out TimeSpan timeSpan)
  240.             {
  241.                 return TimeSpan.TryParse(timeStamp, CultureInfo.InvariantCulture, out timeSpan);
  242.             }
  243.         }
  244.     }
  245. }
  246.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement