using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Xml;
namespace Nikse.SubtitleEdit.Core.SubtitleFormats
{
public class FinalCutProXml14Text : SubtitleFormat
{
public double FrameRate { get; set; }
public override string Extension => ".fcpxml";
public override string Name => "Final Cut Pro Xml 1.4 Text";
public override string ToText(Subtitle subtitle, string title)
{
if (Configuration.Settings.General.CurrentFrameRate > 26)
{
FrameRate = 30;
}
else
{
FrameRate = 25;
}
string xmlStructure =
"" + Environment.NewLine +
"" + Environment.NewLine +
Environment.NewLine +
"" + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
"";
string xmlClipStructure =
"
" + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" THE NOISEMAKER" + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
" " + Environment.NewLine +
"";
var xml = new XmlDocument();
xml.LoadXml(xmlStructure);
XmlNode videoNode = xml.DocumentElement.SelectSingleNode("//project/sequence/spine/gap");
int number = 1;
foreach (Paragraph p in subtitle.Paragraphs)
{
XmlNode video = xml.CreateElement("video");
var trimmedTitle = new StringBuilder();
foreach (var ch in HtmlUtil.RemoveHtmlTags(p.Text, true))
{
if (CharUtils.IsEnglishAlphabet(ch) || char.IsDigit(ch))
{
trimmedTitle.Append(ch);
}
}
string temp = xmlClipStructure.Replace("[NUMBER]", number.ToString(CultureInfo.InvariantCulture)).Replace("[TITLEID]", trimmedTitle.ToString());
video.InnerXml = temp;
XmlNode generatorNode = video.SelectSingleNode("title");
if (IsNearleWholeNumber(p.StartTime.TotalSeconds))
{
generatorNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds) + "s";
}
else
{
generatorNode.Attributes["offset"].Value = FinalCutProXml15.GetFrameTime(p.StartTime);
}
if (IsNearleWholeNumber(p.Duration.TotalSeconds))
{
generatorNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds) + "s";
}
else
{
generatorNode.Attributes["duration"].Value = FinalCutProXml15.GetFrameTime(p.Duration);
}
if (IsNearleWholeNumber(p.StartTime.TotalSeconds))
{
generatorNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds) + "s";
}
else
{
generatorNode.Attributes["start"].Value = FinalCutProXml15.GetFrameTime(p.StartTime);
}
XmlNode param = video.SelectSingleNode("title/text/text-style");
param.InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
videoNode.AppendChild(generatorNode);
number++;
}
string xmlAsText = ToUtf8XmlString(xml);
xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml");
xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml");
return xmlAsText;
}
private static bool IsNearleWholeNumber(double number)
{
double rest = number - Convert.ToInt64(number);
return rest < 0.001;
}
public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
{
_errorCount = 0;
FrameRate = Configuration.Settings.General.CurrentFrameRate;
var sb = new StringBuilder();
lines.ForEach(line => sb.AppendLine(line));
string x = sb.ToString();
if (!x.Contains(""))
{
return;
}
var xml = new XmlDocument();
try
{
xml.LoadXml(x.Trim());
if (subtitle.Paragraphs.Count == 0)
{
var textNodes = xml.SelectNodes("//project/sequence/spine/title/text");
if (textNodes.Count == 0)
{
textNodes = xml.SelectNodes("//project/sequence/spine/gap/title/text");
}
foreach (XmlNode node in textNodes)
{
try
{
string text = node.ParentNode.InnerText;
var p = new Paragraph();
p.Text = text.Trim();
p.StartTime = DecodeTime(node.ParentNode.Attributes["offset"]);
p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(node.ParentNode.Attributes["duration"]).TotalMilliseconds;
bool add = true;
if (subtitle.Paragraphs.Count > 0)
{
var prev = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1];
if (prev.Text == p.Text && prev.StartTime.TotalMilliseconds == p.StartTime.TotalMilliseconds)
{
add = false;
}
}
if (add)
{
subtitle.Paragraphs.Add(p);
}
}
catch
{
_errorCount++;
}
}
}
subtitle.Renumber();
}
catch
{
_errorCount = 1;
}
}
private static TimeCode DecodeTime(XmlAttribute duration)
{
// 220220/60000s
if (duration != null)
{
var arr = duration.Value.TrimEnd('s').Split('/');
if (arr.Length == 2)
{
return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1]));
}
if (arr.Length == 1)
{
return TimeCode.FromSeconds(float.Parse(arr[0]));
}
}
return new TimeCode();
}
}
}