8
Nov
by Chris
Here is a translet that will transform Timed Text (TT) Authoring Format 1.0 to Microsoft Synchronized Accessible Media Interchange (SAMI). This one allows you to specify a TimeShift value in milliseconds to the transform. It also does better subtitle colouring.
function T-TtafToSmi{ param ( [xml]$inxml ,[int]$TimeShift = 0 ) BEGIN { . pslib:\xml\invoke-transform.ps1 [xml]$xslt = @' <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://www.w3.org/2006/10/ttaf1"
xmlns="http://www.w3.org/2006/10/ttaf1"
xmlns:ttp="http://www.w3.org/2006/10/ttaf1#parameter"
xmlns:tts="http://www.w3.org/2006/10/ttaf1#style"
xmlns:ttm="http://www.w3.org/2006/10/ttaf1#metadata"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:cjb="http://www.bayes.co.uk/script" version="1.0" exclude-result-prefixes="#default t ttp tts ttm msxsl cjb">
<xsl:param name="TimeShift" select="0" />
<xsl:output encoding="utf-8" indent="yes" method="xml" omit-xml-declaration="yes" />
<msxsl:script language="C#" implements-prefix="cjb">
public double getMilliseconds(string time)
{
return (string.IsNullOrEmpty(time) ? 0 : TimeSpan.Parse(time).TotalMilliseconds);
}
public string hyphenate(string str){
Text.StringBuilder sb = new Text.StringBuilder("", 100);
foreach (char c in str){
if (char.IsUpper(c)){sb.Append("-");sb.Append(char.ToLower(c));}
else{sb.Append(c);}}
return sb.ToString();
}
</msxsl:script>
<xsl:variable name="default-style">
<xsl:for-each select="/t:tt/t:head/t:styling/t:style[last()]">
<xsl:sort order="ascending" select="count(@tts:*)" />
<xsl:value-of select="@id" />
</xsl:for-each>
</xsl:variable>
<xsl:variable name="default-class">
<xsl:value-of select="/t:tt/@xml:lang" />cc
</xsl:variable>
<xsl:template match="t:tt">
<SAMI>
<HEAD>
<TITLE>
<xsl:value-of select="/t:tt/t:head/t:metadata/ttm:title" />
</TITLE>
<xsl:comment>
Copyright <xsl:value-of select="/t:tt/t:head/t:metadata/ttm:copyright" />
</xsl:comment>
<xsl:copy-of select="/t:tt/t:head/comment()" />
<STYLE>
<xsl:comment>
p {
text-align: center;
font-family: arial, sans-serif;
font-weight: normal;
color: white;
font-size: 16pt;
<xsl:apply-templates select="/t:tt/t:head/t:styling/t:style[@id=$default-style]/@tts:*" />
}
.<xsl:value-of select="$default-class" /> {
text-align: center;
font-family: arial, sans-serif;
font-weight: normal;
color: white;
font-size: 16pt;
<xsl:apply-templates select="/t:tt/t:head/t:styling/t:style[@id=$default-style]/@tts:*" />
Name: <xsl:value-of select="/t:tt/@xml:lang" /> Subtitles;
lang: <xsl:value-of select="/t:tt/@xml:lang" />;
SAMI_Type: CC;
}
</xsl:comment>
</STYLE>
</HEAD>
<xsl:apply-templates select="/t:tt/t:body" />
</SAMI>
</xsl:template>
<xsl:template match="t:body">
<BODY>
<xsl:apply-templates />
</BODY>
</xsl:template>
<xsl:template match="t:body/t:div">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="t:p">
<xsl:variable name="start" select="cjb:getMilliseconds(string(@begin)) + $TimeShift" />
<xsl:variable name="end" select="cjb:getMilliseconds(string(@end)) + $TimeShift" />
<xsl:variable name="following-start" select="cjb:getMilliseconds(string(following-sibling::t:p/@begin)) + $TimeShift" />
<Sync START="{$start}">
<p>
<xsl:if test="@style">
<xsl:attribute name="class">
<xsl:value-of select="$default-class" />
</xsl:attribute>
</xsl:if>
<span>
<xsl:if test="@style=$default-style and @tts:*">
<span>
<xsl:attribute name="style">
<xsl:apply-templates select="@tts:*" />
</xsl:attribute>
</span>
</xsl:if>
<xsl:if test="@style!=$default-style">
<xsl:attribute name="style">
<xsl:apply-templates select="/t:tt/t:head/t:styling/t:style[@id=current()/@style]/@tts:*|@tts:*" />
</xsl:attribute>
</xsl:if>
<xsl:apply-templates />
</span>
</p>
</Sync>
<xsl:if test="$end < ($following-start - 10)">
<xsl:text>
</xsl:text>
<Sync START="{$end}">
<p>
<nonbreakingspace />
</p>
</Sync>
</xsl:if>
</xsl:template>
<xsl:template match="@tts:*">
<xsl:value-of select="cjb:hyphenate(local-name())" />:<xsl:value-of select="." />;
</xsl:template>
<xsl:template match="@tts:fontFamily">
<xsl:value-of select="cjb:hyphenate(local-name())" />:<xsl:value-of select="cjb:hyphenate(.)" />;
</xsl:template>
<xsl:template match="@tts:fontSize">
<xsl:value-of select="cjb:hyphenate(local-name())" />:<xsl:value-of select="." /> pt;
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name(.)}">
<xsl:if test="@tts:*">
<xsl:attribute name="style">
<xsl:apply-templates select="@tts:*" />
</xsl:attribute>
</xsl:if>
<xsl:apply-templates select="@*[namespace-uri()!='http://www.w3.org/2006/10/ttaf1#style']" />
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="text()">
<xsl:copy />
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
'@ } PROCESS{ if ($_ -is [xml]){ $result = (invoke-transform -inxml $_ -inxsl $xslt -arguments @{TimeShift=$TimeShift}) $result = $result.replace(" xmlns=`"http://www.w3.org/2006/10/ttaf1`"", "") $result = $result.replace("<nonbreakingspace />", " ") $result } END{ if ($inxml -is [xml]){ $result = (invoke-transform -inxml $inxml -inxsl $xslt -arguments @{TimeShift=$TimeShift}) $result = $result.replace(" xmlns=`"http://www.w3.org/2006/10/ttaf1`"", "") $result = $result.replace("<nonbreakingspace />", " ") $result } }} The replaces on the $result string look like a bit of a hack but they do need to be there for reasons that are very long to explain.
It can be used like this
PS> . .\T-TtafToSmiM3.ps1 # dot source this file
PS> T-TtafToSmi [xml](gc ttaf.xml) -T 1000| sc -encoding ascii
or
PS> . .\T-TtafToSmiM3.ps1 # dot source this file
PS> [xml](gc .\ttaf.xml) | T-TtafToSmi -T 1000 | sc -encoding ascii
The -T 1000 will shift the subtitle forward by 1 second. A value of -T -1000 will shift the subtitle backward by 1 second.
Here is the code
T-TtafToSmiM3.zip (2.50 kb)
