Some improvements to format "Dvd Studio Pro" + added format "YouTube annotations"

git-svn-id: https://subtitleedit.googlecode.com/svn/trunk@582 99eadd0c-20b8-1223-b5c4-2a2b2df33de2
This commit is contained in:
niksedk 2011-08-08 22:12:07 +00:00
parent 10302daa21
commit 285010299c
6 changed files with 350 additions and 14 deletions

View File

@ -60,13 +60,20 @@ $HorzAlign = Center
sb.AppendLine(header);
foreach (Paragraph p in subtitle.Paragraphs)
{
string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, (int)Math.Round((p.StartTime.Milliseconds / 10.0) / 4.0));
string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, (int)Math.Round((p.EndTime.Milliseconds / 10.0) / 4.0));
double factor = (1000.0 / Configuration.Settings.General.CurrentFrameRate);
string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, (int)Math.Round(p.StartTime.Milliseconds / factor));
string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, (int)Math.Round(p.EndTime.Milliseconds / factor));
sb.Append(string.Format(paragraphWriteFormat, startTime, endTime, p.Text.Replace(Environment.NewLine, " | ")));
}
return sb.ToString().Trim();
}
public static byte GetFrameFromMilliseconds(int milliseconds, double frameRate)
{
int frame = (int)(milliseconds / (1000.0 / frameRate));
return (byte)(frame);
}
public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
{
_errorCount = 0;
@ -105,8 +112,13 @@ $HorzAlign = Center
timeCode.Hours = int.Parse(timeParts[0]);
timeCode.Minutes = int.Parse(timeParts[1]);
timeCode.Seconds = int.Parse(timeParts[2]);
int milliseconds = int.Parse(timeParts[3]);
timeCode.Milliseconds = (int)Math.Round(milliseconds * 4.0 * 10.0);
int frames = int.Parse(timeParts[3]);
int milliseconds = (int)(1000.0 / Configuration.Settings.General.CurrentFrameRate * frames);
if (milliseconds > 999)
milliseconds = 999;
timeCode.Milliseconds = milliseconds;
return true;
}
catch

View File

@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace Nikse.SubtitleEdit.Logic.SubtitleFormats
{
public class DvdStudioProSpace : SubtitleFormat
{
readonly Regex _regexTimeCodes = new Regex(@"^\d+:\d+:\d+:\d+ , \d+:\d+:\d+:\d+ , .*$", RegexOptions.Compiled);
public override string Extension
{
get { return ".STL"; }
}
public override string Name
{
get { return "DVD Studio Pro with space"; }
}
public override bool HasLineNumber
{
get { return false; }
}
public override bool IsTimeBased
{
get { return true; }
}
public override bool IsMine(List<string> lines, string fileName)
{
Subtitle subtitle = new Subtitle();
LoadSubtitle(subtitle, lines, fileName);
return subtitle.Paragraphs.Count > _errorCount;
}
public override string ToText(Subtitle subtitle, string title)
{
const string paragraphWriteFormat = "{0} , {1} , {2}\r\n";
const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}";
const string header = @"$VertAlign = Bottom
$Bold = FALSE
$Underlined = FALSE
$Italic = 0
$XOffset = 0
$YOffset = -5
$TextContrast = 15
$Outline1Contrast = 15
$Outline2Contrast = 13
$BackgroundContrast = 0
$ForceDisplay = FALSE
$FadeIn = 0
$FadeOut = 0
$HorzAlign = Center
";
StringBuilder sb = new StringBuilder();
sb.AppendLine(header);
foreach (Paragraph p in subtitle.Paragraphs)
{
double factor = (1000.0 / Configuration.Settings.General.CurrentFrameRate);
string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, (int)Math.Round(p.StartTime.Milliseconds / factor));
string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, (int)Math.Round(p.EndTime.Milliseconds / factor));
sb.Append(string.Format(paragraphWriteFormat, startTime, endTime, p.Text.Replace(Environment.NewLine, " | ")));
}
return sb.ToString().Trim();
}
public static byte GetFrameFromMilliseconds(int milliseconds, double frameRate)
{
int frame = (int)(milliseconds / (1000.0 / frameRate));
return (byte)(frame);
}
public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
{
_errorCount = 0;
int number = 0;
foreach (string line in lines)
{
if (line.Trim().Length > 0 && line[0] != '$' && !line.StartsWith("//"))
{
if (_regexTimeCodes.Match(line).Success)
{
string[] toPart = line.Substring(0, 25).Split(new[] { " ," }, StringSplitOptions.None);
Paragraph p = new Paragraph();
if (toPart.Length == 2 &&
GetTimeCode(p.StartTime, toPart[0]) &&
GetTimeCode(p.EndTime, toPart[1]))
{
number++;
p.Number = number;
string text = line.Substring(27).Trim();
p.Text = text.Replace(" | ", Environment.NewLine).Replace("|", Environment.NewLine);
subtitle.Paragraphs.Add(p);
}
}
else
{
_errorCount++;
}
}
}
}
private static bool GetTimeCode(TimeCode timeCode, string timeString)
{
try
{
string[] timeParts = timeString.Split(':');
timeCode.Hours = int.Parse(timeParts[0]);
timeCode.Minutes = int.Parse(timeParts[1]);
timeCode.Seconds = int.Parse(timeParts[2]);
int frames = int.Parse(timeParts[3]);
int milliseconds = (int)(1000.0 / Configuration.Settings.General.CurrentFrameRate * frames);
if (milliseconds > 999)
milliseconds = 999;
timeCode.Milliseconds = milliseconds;
return true;
}
catch
{
return false;
}
}
}
}

View File

@ -63,7 +63,7 @@ namespace Nikse.SubtitleEdit.Logic.SubtitleFormats
//0001 00:00:19:13 00:00:22:10 a_0001.tif
Paragraph p = null;
subtitle.Paragraphs.Clear();
var regexTimeCodes = new Regex(@"^\d\d\d\d\t\d\d:\d\d:\d\d:\d\d\t\d\d:\d\d:\d\d:\d\d\t[^\s]+\.(tif|tiff|png|bmp|TIF|TIFF|PNG|BMP)", RegexOptions.Compiled);
var regexTimeCodes = new Regex(@"^\d\d\d\d[\t]+\d\d:\d\d:\d\d:\d\d\t\d\d:\d\d:\d\d:\d\d\t[^\s]+\.(tif|tiff|png|bmp|TIF|TIFF|PNG|BMP)", RegexOptions.Compiled);
int index = 0;
foreach (string line in lines)
{

View File

@ -139,6 +139,10 @@ namespace Nikse.SubtitleEdit.Logic.SubtitleFormats
_paragraph.Text += Environment.NewLine;
_paragraph.Text += RemoveBadChars(line).TrimEnd();
}
else if (string.IsNullOrEmpty(line) && string.IsNullOrEmpty(_paragraph.Text))
{
_paragraph.Text = string.Empty;
}
else
{
subtitle.Paragraphs.Add(_paragraph);
@ -161,15 +165,6 @@ namespace Nikse.SubtitleEdit.Logic.SubtitleFormats
return false;
return true;
//string letters = Utilities.GetLetters(true, true, false);
//foreach (char ch in next)
//{
// if (letters.Contains(ch.ToString()))
// return true;
//}
//return false;
}
private string RemoveBadChars(string line)

View File

@ -27,6 +27,7 @@ namespace Nikse.SubtitleEdit.Logic.SubtitleFormats
new DCSubtitle(),
new DigiBeta(),
new DvdStudioPro(),
new DvdStudioProSpace(),
new DvdSubtitle(),
new Eeg708(),
new FabSubtitler(),
@ -68,6 +69,7 @@ namespace Nikse.SubtitleEdit.Logic.SubtitleFormats
new UnknownSubtitle6(),
new UnknownSubtitle7(),
// new Wsb(),
new YouTubeAnnotations(),
new YouTubeSbv(),
new ZeroG(),
};

View File

@ -0,0 +1,196 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
namespace Nikse.SubtitleEdit.Logic.SubtitleFormats
{
class YouTubeAnnotations : SubtitleFormat
{
public override string Extension
{
get { return ".xml"; }
}
public override string Name
{
get { return "YouTube Annotations"; }
}
public override bool HasLineNumber
{
get { return false; }
}
public override bool IsTimeBased
{
get { return true; }
}
public override bool IsMine(List<string> lines, string fileName)
{
var subtitle = new Subtitle();
LoadSubtitle(subtitle, lines, fileName);
return subtitle.Paragraphs.Count > 0;
}
public override string ToText(Subtitle subtitle, string title)
{
string xmlStructure =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" + Environment.NewLine +
"<document>" + Environment.NewLine +
" <requestheader video_id=\"X\"/>" + Environment.NewLine +
" <annotations/>" + Environment.NewLine +
"</document>";
var xml = new XmlDocument();
xml.LoadXml(xmlStructure);
XmlNode annotations = xml.DocumentElement.SelectSingleNode("annotations");
int count = 1;
foreach (Paragraph p in subtitle.Paragraphs)
{
//<annotation id="annotation_126995" author="StopFear" type="text" style="speech">
// <TEXT>BUT now something inside is BROKEN!</TEXT>
// <segment>
// <movingRegion type="anchored">
// <anchoredRegion x="6.005" y="9.231" w="26.328" h="18.154" sx="40.647" sy="14.462" t="0:01:08.0" d="0"/>
// <anchoredRegion x="6.005" y="9.231" w="26.328" h="18.154" sx="40.647" sy="14.462" t="0:01:13.0" d="0"/>
// </movingRegion>
// </segment>
//</annotation>
XmlNode annotation = xml.CreateElement("annotation");
XmlAttribute att = xml.CreateAttribute("id");
att.InnerText = "annotation_" + count;
annotation.Attributes.Append(att);
att = xml.CreateAttribute("author");
att.InnerText = "Subtitle Edit";
annotation.Attributes.Append(att);
att = xml.CreateAttribute("type");
att.InnerText = "text";
annotation.Attributes.Append(att);
att = xml.CreateAttribute("style");
att.InnerText = "speech";
annotation.Attributes.Append(att);
XmlNode text = xml.CreateElement("TEXT");
text.InnerText = p.Text;
annotation.AppendChild(text);
XmlNode segment = xml.CreateElement("segment");
annotation.AppendChild(segment);
XmlNode movingRegion = xml.CreateElement("movingRegion");
segment.AppendChild(movingRegion);
att = xml.CreateAttribute("type");
att.InnerText = "anchored";
movingRegion.Attributes.Append(att);
XmlNode anchoredRegion = xml.CreateElement("anchoredRegion");
movingRegion.AppendChild(anchoredRegion);
att = xml.CreateAttribute("t");
att.InnerText = EncodeTime(p.StartTime);
anchoredRegion.Attributes.Append(att);
att = xml.CreateAttribute("d");
att.InnerText = "0";
anchoredRegion.Attributes.Append(att);
anchoredRegion = xml.CreateElement("anchoredRegion");
movingRegion.AppendChild(anchoredRegion);
att = xml.CreateAttribute("t");
att.InnerText = EncodeTime(p.EndTime);
anchoredRegion.Attributes.Append(att);
att = xml.CreateAttribute("d");
att.InnerText = "0";
anchoredRegion.Attributes.Append(att);
annotations.AppendChild(annotation);
count++;
}
var ms = new MemoryStream();
var writer = new XmlTextWriter(ms, Encoding.UTF8) { Formatting = Formatting.Indented };
xml.Save(writer);
string returnXml = Encoding.UTF8.GetString(ms.ToArray()).Trim();
return returnXml;
}
public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
{
_errorCount = 0;
var sb = new StringBuilder();
lines.ForEach(line => sb.AppendLine(line));
if (!sb.ToString().Contains("</annotations>") || !sb.ToString().Contains("</TEXT>"))
return;
var xml = new XmlDocument();
try
{
string xmlText = sb.ToString();
xml.LoadXml(xmlText);
foreach (XmlNode node in xml.SelectNodes("//annotation"))
{
try
{
XmlNode textNode = node.SelectSingleNode("TEXT");
XmlNodeList anchoredRegions = node.SelectNodes("segment/movingRegion/anchoredRegion");
if (textNode != null && anchoredRegions.Count == 2)
{
string startTime = anchoredRegions[0].Attributes["t"].Value;
string endTime = anchoredRegions[1].Attributes["t"].Value;
var p = new Paragraph();
p.StartTime = DecodeTimeCode(startTime);
p.EndTime = DecodeTimeCode(endTime);
p.Text = textNode.InnerText;
subtitle.Paragraphs.Add(p);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
_errorCount++;
}
}
subtitle.Sort(Enums.SubtitleSortCriteria.StartTime); // force order by start time
subtitle.Renumber(1);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
_errorCount = 1;
return;
}
}
private TimeCode DecodeTimeCode(string time)
{
string[] arr = time.Split(".:".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
int hours = int.Parse(arr[0]);
int minutes = int.Parse(arr[1]);
int seconds = int.Parse(arr[2]);
int milliseconds = int.Parse(arr[3]);
TimeSpan ts = new TimeSpan(0, hours, minutes, seconds, milliseconds);
return new TimeCode(ts);
}
private string EncodeTime(TimeCode timeCode)
{
//0:01:08.0
return string.Format("{0}:{1:00}:{2:00}.{3}", timeCode.Hours, timeCode.Minutes, timeCode.Seconds, timeCode.Milliseconds);
}
}
}