diff --git a/src/Forms/BatchConvert.cs b/src/Forms/BatchConvert.cs index 0c335314d..e7373c5ce 100644 --- a/src/Forms/BatchConvert.cs +++ b/src/Forms/BatchConvert.cs @@ -342,7 +342,6 @@ namespace Nikse.SubtitleEdit.Forms format = nciCaption; } } - if (format == null) { var avidStl = new AvidStl(); @@ -351,6 +350,14 @@ namespace Nikse.SubtitleEdit.Forms format = avidStl; } } + if (format == null) + { + var asc = new TimeLineAscii(); + if (asc.IsMine(null, fileName)) + { + format = asc; + } + } } if (format == null) diff --git a/src/Forms/Main.cs b/src/Forms/Main.cs index 570ff4dc3..139a15495 100644 --- a/src/Forms/Main.cs +++ b/src/Forms/Main.cs @@ -2118,6 +2118,21 @@ namespace Nikse.SubtitleEdit.Forms } } + if (format == null) + { + var asc = new TimeLineAscii(); + if (asc.IsMine(null, fileName)) + { + asc.LoadSubtitle(_subtitle, null, fileName); + _oldSubtitleFormat = asc; + SetCurrentFormat(Configuration.Settings.General.DefaultSubtitleFormat); + SetEncoding(Configuration.Settings.General.DefaultEncoding); + encoding = GetCurrentEncoding(); + justConverted = true; + format = GetCurrentSubtitleFormat(); + } + } + if (format == null) { var mtv = new TimeLineMvt(); diff --git a/src/Logic/SubtitleFormats/TimelineAscii.cs b/src/Logic/SubtitleFormats/TimelineAscii.cs new file mode 100644 index 000000000..aac95a66b --- /dev/null +++ b/src/Logic/SubtitleFormats/TimelineAscii.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using Nikse.SubtitleEdit.Core; + +namespace Nikse.SubtitleEdit.Logic.SubtitleFormats +{ + /// + /// Timeline Ascii export - THE MOVIE TITRE EDITOR - http://www.pld.ttu.ee/~priidu/timeline/ by priidu@pld.ttu.ee + /// + /// Sample: + /// 1. + /// 00:00:43.02 + /// 00:00:47.03 + /// ±NE/SEVÎ + /// ³ÂÍÅ/ÑÅÁß + /// + /// 2. + /// 00:01:36.00 + /// 00:01:37.00 + /// ±Viòð ir klât. + /// ³Îí ïðèøåë. + /// + public class TimeLineAscii : SubtitleFormat + { + + private static readonly Regex RegexTimeCode = new Regex(@"^\d\d:\d\d:\d\d\.\d\d$", RegexOptions.Compiled); + + private enum ExpectingLine + { + Number, + TimeStart, + TimeEnd, + Text + } + + public override string Extension + { + get { return ".asc"; } + } + + public override string Name + { + get { return "Timeline ascii"; } + } + + public override bool IsTimeBased + { + get { return true; } + } + + public override bool IsMine(List lines, string fileName) + { + if (fileName == null || !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase)) + return false; + + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + return subtitle.Paragraphs.Count > _errorCount; + } + + public override string ToText(Subtitle subtitle, string title) + { + return string.Empty; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + Paragraph paragraph = null; + var expecting = ExpectingLine.Number; + _errorCount = 0; + + subtitle.Paragraphs.Clear(); + IEnumerable byteLines = SplitBytesToLines(File.ReadAllBytes(fileName)); + foreach (byte[] bytes in byteLines) + { + var line = Encoding.ASCII.GetString(bytes); + if (line.EndsWith('.') && Utilities.IsInteger(line.TrimEnd('.'))) + { + if (paragraph != null && !string.IsNullOrEmpty(paragraph.Text)) + subtitle.Paragraphs.Add(paragraph); + paragraph = new Paragraph(); + expecting = ExpectingLine.TimeStart; + } + else if (paragraph != null && expecting == ExpectingLine.TimeStart && RegexTimeCode.IsMatch(line)) + { + string[] parts = line.Split(new[] { ':', '.' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + var tc = DecodeTimeCode(parts); + paragraph.StartTime = tc; + expecting = ExpectingLine.TimeEnd; + } + catch + { + _errorCount++; + expecting = ExpectingLine.Number; + } + } + } + else if (paragraph != null && expecting == ExpectingLine.TimeEnd && RegexTimeCode.IsMatch(line)) + { + string[] parts = line.Split(new[] { ':', '.' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 4) + { + try + { + var tc = DecodeTimeCode(parts); + paragraph.EndTime = tc; + expecting = ExpectingLine.Text; + } + catch + { + _errorCount++; + expecting = ExpectingLine.Number; + } + } + } + else + { + if (paragraph != null && expecting == ExpectingLine.Text) + { + if (bytes.Length > 1) + { + // get text from encoding + var enc = GetEncodingFromLanguage(bytes[0]); + string s = enc.GetString(bytes, 1, bytes.Length - 1).Trim(); + + // italic text + if (s.StartsWith('#')) + s = "" + s.Remove(0, 1) + ""; + + paragraph.Text = (paragraph.Text + Environment.NewLine + s).Trim(); + if (paragraph.Text.Length > 2000) + { + _errorCount += 100; + return; + } + } + } + } + } + if (paragraph != null && !string.IsNullOrEmpty(paragraph.Text)) + subtitle.Paragraphs.Add(paragraph); + + subtitle.Renumber(); + } + + private IEnumerable SplitBytesToLines(byte[] bytes) + { + var list = new List(); + int start = 0; + int index = 0; + while (index < bytes.Length) + { + if (bytes[index] == 13) + { + int length = index - start; + var lineBytes = new byte[length]; + Array.Copy(bytes, start, lineBytes, 0, length); + list.Add(lineBytes); + index += 2; + start = index; + } + else + { + index++; + } + } + return list; + } + + private static TimeCode DecodeTimeCode(string[] parts) + { + int hours = int.Parse(parts[0]); + int minutes = int.Parse(parts[1]); + int seconds = int.Parse(parts[2]); + int frames = int.Parse(parts[3]); + return new TimeCode(hours, minutes, seconds, FramesToMillisecondsMax999(frames)); + } + + private Encoding GetEncodingFromLanguage(byte language) + { + if (language == 179) // Russian + return Encoding.GetEncoding(1251); + + if (language == 177) // Baltic + return Encoding.GetEncoding(1257); + + return Encoding.GetEncoding(1252); + } + + } +} \ No newline at end of file diff --git a/src/SubtitleEdit.csproj b/src/SubtitleEdit.csproj index 59179a4f5..b757c7902 100644 --- a/src/SubtitleEdit.csproj +++ b/src/SubtitleEdit.csproj @@ -902,6 +902,7 @@ +