mirror of
https://github.com/SubtitleEdit/subtitleedit.git
synced 2024-11-22 11:12:36 +01:00
Added new subtitle format - thx Sebastian :)
This commit is contained in:
parent
12676977ac
commit
f70fa274e5
@ -1,4 +1,5 @@
|
||||
using Nikse.SubtitleEdit.Core;
|
||||
using System.IO.Compression;
|
||||
using Nikse.SubtitleEdit.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -145,6 +146,7 @@ namespace Nikse.SubtitleEdit.Logic.SubtitleFormats
|
||||
new VocapiaSplit(),
|
||||
new WebVTT(),
|
||||
new WebVTTFileWithLineNumber(),
|
||||
new Xif(),
|
||||
new YouTubeAnnotations(),
|
||||
new YouTubeSbv(),
|
||||
new YouTubeTranscript(),
|
||||
|
241
src/Logic/SubtitleFormats/Xif.cs
Normal file
241
src/Logic/SubtitleFormats/Xif.cs
Normal file
@ -0,0 +1,241 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Nikse.SubtitleEdit.Core;
|
||||
|
||||
namespace Nikse.SubtitleEdit.Logic.SubtitleFormats
|
||||
{
|
||||
class Xif : SubtitleFormat
|
||||
{
|
||||
public override string Extension
|
||||
{
|
||||
get { return ".xif"; }
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get { return "XIF"; }
|
||||
}
|
||||
|
||||
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 > _errorCount;
|
||||
}
|
||||
|
||||
public override string ToText(Subtitle subtitle, string title)
|
||||
{
|
||||
const string xmpTemplate = @"<XIF version='1.0' filename='file:///filename.xif'>
|
||||
<FileHeader>
|
||||
<OriginatorSoftware Name='FileConverter' Version='1.01'/>
|
||||
<GlobalFileInfo ProgrammeName='NAME' Format='Open' Language='English' StartTime='00:00:00:00' StopTime='00:00:00:00' TotalLength='00:00:00:00' ProgrammeTitle='TITLE' NumberOfCaptions='405' RevisionNumber='0' CountryIndex='0' CharacterCodeTable='0' MaxColumns='0' EpisodeNumber='0' BlinkOption='yes' CaptionService='0' CurrentVideo='2' Videosize='0' BaseOffset='0' TopOffset='0' DefaultSave='0' DisplaySafwWidth='576' SafeAreaWidthPercent='80' DisplaySafeHoriz='0' SafeAreaHorizPos='0' CurrentStyle='None' StyleDate='None' StyleTime='None' SAOLeft='72' SAORight='0' VideoStartTC='00:00:00:00' SpellLanguage='English' CGConfig='0'/>
|
||||
<ReadingSpeeds>
|
||||
<ReadingSpeed ID='0' WordsPerMinute='180' CharsPerMinute='720' CharsPerSecond='12' PTS='WPM'/>
|
||||
</ReadingSpeeds>
|
||||
<Canvases>
|
||||
<Canvas ID='0' Width='720' Height='576' SAWidth='576' SAHeight='460' SATop='57' SALeft='72'/>
|
||||
</Canvases>
|
||||
<FormattingInfo TabWidth='3'/>
|
||||
<TimingStandards>
|
||||
<TimingStandard ID='0' Type='SMPTE25'/>
|
||||
</TimingStandards>
|
||||
<Fonts>
|
||||
<Font ID='0' Type='True Type' Typeface='Courier New' Height='0' AstonFountSize='185' Ascent='268' Descent='97' IntLead='43'/>
|
||||
<Font ID='1' Type='True Type' Typeface='Courier New' Height='0' AstonFountSize='185' Ascent='268' Descent='97' IntLead='43'/>
|
||||
<Font ID='2' Type='True Type' Typeface='Hairy Helix' Height='0' AstonFountSize='12' Ascent='15' Descent='3' IntLead='2'/>
|
||||
<Font ID='3' Type='True Type' Typeface='Hairy Helix Italics' Height='0' AstonFountSize='12' Ascent='15' Descent='3' IntLead='2'/>
|
||||
<Font ID='4' Type='True Type' Typeface='Arial' Height='35' AstonFountSize='22' Ascent='28' Descent='7' IntLead='5'/>
|
||||
<Font ID='5' Type='True Type' Typeface='Arial Narrow' Height='35' AstonFountSize='25' Ascent='30' Descent='5' IntLead='0'/>
|
||||
<Font/>
|
||||
</Fonts>
|
||||
<Colours>
|
||||
<Colour ID='0' value='0x000000'/>
|
||||
<Colour ID='1' value='0xFF0000'/>
|
||||
<Colour ID='2' value='0x00FF00'/>
|
||||
<Colour ID='3' value='0xFFFF00'/>
|
||||
<Colour ID='4' value='0x0000FF'/>
|
||||
<Colour ID='5' value='0xFF00FF'/>
|
||||
<Colour ID='6' value='0x00FFFF'/>
|
||||
<Colour ID='7' value='0xFFFFFF'/>
|
||||
</Colours>
|
||||
<Threads>
|
||||
<Thread ID='0' CanvasID='0' TimingStandardID='0' ReadingSpeedID='0' Medium='Open' Name='Default'/>
|
||||
</Threads>
|
||||
<WordLists/>
|
||||
<TxStreams>
|
||||
<TxStream ID='0' Active='yes'/>
|
||||
</TxStreams>
|
||||
</FileHeader>
|
||||
<FileBody>
|
||||
<ProgrammeBlock NoOfAssociatedIDs='0'>
|
||||
<SceneBaseBlock NewsID='0' Position='0' NewsRoomInternalID='0'/>
|
||||
</ProgrammeBlock>
|
||||
<StoryBlock TimingType='Manual'>
|
||||
<SceneBaseBlock NewsID='0' Position='0' NewsRoomInternalID='0'/>
|
||||
</StoryBlock>
|
||||
|
||||
</FileBody>
|
||||
</XIF>";
|
||||
|
||||
const string paragraphTemplate = @"
|
||||
<ContentBlock TXStreamID='0' uid='B4EB0AE5-B82F-48ea-9FD4-C4194D004041'>
|
||||
<ThreadedObject ID='0' Type='BasicSubtitle' VPos='448'>
|
||||
<Note/>
|
||||
<TimingObject>
|
||||
<TimeIn value='10:00:03:06'/>
|
||||
<TimeOut value='10:00:05:09'/>
|
||||
</TimingObject>
|
||||
<Content Override='0' RaisedRowUp='0' RaisedRowDown='0' DirectionState='0' PositionState='1' ConfidenceValue='0'>
|
||||
<SubtitleText>
|
||||
<Paragraph Type='Open' Justification='Centre' LineLimit='15' CharactersPerRow='99' Alignment='None' Language='IDN' VideoLanguage='IDN' Xpercent='0.0' Ypercent='0.0'>
|
||||
<StartState Foreground='7' Background='0' Flash='No' Underline='No' Italic='No' Bold='No' Indent='0' SBOnOff='Yes' FontID='4'/>
|
||||
<Boxing Colour='0' Transparency='0' Type='None'/>
|
||||
<AntiAliasing Style='Default'/>
|
||||
<Row Number='1' JustificationOverride='Centre' Highlight='0'/>
|
||||
<ForegroundColour Colour='7'/>
|
||||
<Text>text1</Text>
|
||||
</Paragraph>
|
||||
</SubtitleText>
|
||||
</Content>
|
||||
</ThreadedObject>
|
||||
</ContentBlock>";
|
||||
|
||||
var xml = new XmlDocument();
|
||||
var lastTimeCode = new TimeCode(0);
|
||||
if (subtitle.Paragraphs.Count > 0)
|
||||
{
|
||||
lastTimeCode = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].StartTime;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
title = "Unknown";
|
||||
xml.LoadXml(xmpTemplate.Replace('\'', '"'));
|
||||
var globalFileInfoNode = xml.DocumentElement.SelectSingleNode("FileHeader/GlobalFileInfo");
|
||||
globalFileInfoNode.Attributes["ProgrammeName"].InnerText = title;
|
||||
globalFileInfoNode.Attributes["ProgrammeTitle"].InnerText = title;
|
||||
globalFileInfoNode.Attributes["StopTime"].InnerText = lastTimeCode.ToHHMMSSFF();
|
||||
globalFileInfoNode.Attributes["NumberOfCaptions"].InnerText = subtitle.Paragraphs.Count.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
|
||||
var fileBodyNode = xml.DocumentElement.SelectSingleNode("FileBody");
|
||||
foreach (Paragraph p in subtitle.Paragraphs)
|
||||
{
|
||||
XmlNode content = xml.CreateElement("ContentBlock");
|
||||
content.InnerXml = paragraphTemplate;
|
||||
content.SelectSingleNode("ContentBlock/ThreadedObject/TimingObject/TimeIn").InnerText = p.StartTime.ToHHMMSSFF();
|
||||
content.SelectSingleNode("ContentBlock/ThreadedObject/TimingObject/TimeOut").InnerText = p.EndTime.ToHHMMSSFF();
|
||||
|
||||
var paragraphNode = content.SelectSingleNode("ContentBlock/ThreadedObject/Content/SubtitleText/Paragraph");
|
||||
var lines = HtmlUtil.RemoveHtmlTags(p.Text, true).SplitToLines();
|
||||
for (int i = 1; i < lines.Length + 1; i++)
|
||||
{
|
||||
var rowNode = xml.CreateElement("Row");
|
||||
|
||||
var attrNumber = xml.CreateAttribute("Number");
|
||||
attrNumber.InnerText = i.ToString(CultureInfo.InvariantCulture);
|
||||
rowNode.Attributes.Append(attrNumber);
|
||||
|
||||
var attrJust = xml.CreateAttribute("JustificationOverride");
|
||||
attrJust.InnerText = "Centre";
|
||||
rowNode.Attributes.Append(attrJust);
|
||||
|
||||
var attrHighlight = xml.CreateAttribute("Highlight");
|
||||
attrHighlight.InnerText = "0";
|
||||
rowNode.Attributes.Append(attrHighlight);
|
||||
|
||||
paragraphNode.AppendChild(rowNode);
|
||||
}
|
||||
for (int index = 0; index < lines.Length; index++)
|
||||
{
|
||||
var line = lines[index];
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
var hardReturnNode = xml.CreateElement("HardReturn");
|
||||
paragraphNode.AppendChild(hardReturnNode);
|
||||
}
|
||||
|
||||
var foregroundColorNode = xml.CreateElement("ForegroundColour");
|
||||
var attrColor = xml.CreateAttribute("Colour");
|
||||
attrColor.InnerText = "7";
|
||||
foregroundColorNode.Attributes.Append(attrColor);
|
||||
paragraphNode.AppendChild(foregroundColorNode);
|
||||
|
||||
var textNode = xml.CreateElement("Text");
|
||||
textNode.InnerText = line;
|
||||
paragraphNode.AppendChild(textNode);
|
||||
|
||||
}
|
||||
fileBodyNode.AppendChild(content);
|
||||
}
|
||||
return ToUtf8XmlString(xml).Replace(" xmlns=\"\"", string.Empty);
|
||||
}
|
||||
|
||||
public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
|
||||
{
|
||||
_errorCount = 0;
|
||||
var sb = new StringBuilder();
|
||||
lines.ForEach(line => sb.AppendLine(line));
|
||||
var xmlAsText = sb.ToString().Trim();
|
||||
if (!xmlAsText.Contains("<XIF") || !xmlAsText.Contains("<SubtitleText"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var xml = new XmlDocument { XmlResolver = null };
|
||||
xml.LoadXml(xmlAsText);
|
||||
|
||||
foreach (XmlNode node in xml.DocumentElement.SelectNodes("FileBody/ContentBlock"))
|
||||
{
|
||||
try
|
||||
{
|
||||
var timeCodeIn = DecodeTimeCode(node.SelectSingleNode("ThreadedObject/TimingObject/TimeIn").Attributes["value"].InnerText);
|
||||
var timeCodeOut = DecodeTimeCode(node.SelectSingleNode("ThreadedObject/TimingObject/TimeOut").Attributes["value"].InnerText);
|
||||
sb.Clear();
|
||||
foreach (XmlNode paragraphNode in node.SelectSingleNode("ThreadedObject/Content/SubtitleText/Paragraph").ChildNodes)
|
||||
{
|
||||
if (paragraphNode.Name == "Text")
|
||||
sb.Append(" " + paragraphNode.InnerText);
|
||||
else if (paragraphNode.Name == "HardReturn")
|
||||
sb.AppendLine();
|
||||
}
|
||||
var p = new Paragraph(timeCodeIn, timeCodeOut, sb.ToString().Replace(" ", " ").Trim());
|
||||
subtitle.Paragraphs.Add(p);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(ex.Message);
|
||||
_errorCount++;
|
||||
}
|
||||
}
|
||||
subtitle.Renumber();
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
private static TimeCode DecodeTimeCode(string timeCode)
|
||||
{
|
||||
//00:00:07:12
|
||||
var parts = timeCode.Split(':');
|
||||
var hour = int.Parse(parts[0]);
|
||||
var minutes = int.Parse(parts[1]);
|
||||
var seconds = int.Parse(parts[2]);
|
||||
var frames = int.Parse(parts[3]);
|
||||
return new TimeCode(hour, minutes, seconds, FramesToMillisecondsMax999(frames));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1136,6 +1136,7 @@
|
||||
<Compile Include="Logic\SubtitleFormats\VocapiaSplit.cs" />
|
||||
<Compile Include="Logic\SubtitleFormats\WebVTT.cs" />
|
||||
<Compile Include="Logic\SubtitleFormats\Wsb.cs" />
|
||||
<Compile Include="Logic\SubtitleFormats\Xif.cs" />
|
||||
<Compile Include="Logic\SubtitleFormats\YouTubeAnnotations.cs" />
|
||||
<Compile Include="Logic\SubtitleFormats\YouTubeSbv.cs" />
|
||||
<Compile Include="Logic\SubtitleFormats\SubViewer10.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user