۱۳۹۰/۰۵/۱۳

تبديل زيرنويس‌هاي يوتيوب به فايل srt


تعدادي از ويديوهاي يوتيوب، خصوصا مواردي كه از Google videos به يوتيوب منتقل شده‌اند، داراي زير نويس هستند. اين زير نويس هم فرمت جالبي دارد:
<?xml version="1.0" encoding="UTF-8"?>
<transcript>
<text start="23.49" dur="5.97">
&amp;gt;&amp;gt;Commentator: We have here today, Steve with
us whom I met not too long ago at something
</text>
...
</transcript>
و از هر لحاظ بهتر است از فرمت srt متداول (خصوصا از لحاظ قابليت بهتر parse آن) ولي ... اين فرمت xml ايي، متداول نيست و بهتر است جهت استفاده از آن در برنامه‌هاي پخش كننده فايل‌هاي ويديويي، به srt تبديل شود.
ابتدا شايد بپرسيد كه اين‌ها را چگونه مي‌توان دريافت كرد. روش كار به صورت زير است:
http://video.google.com/timedtext?hl=en&v=Id&lang=en

در قسمت‌هاي hl و lang ، زبان مورد نظر قرار مي‌گيرد و قسمت v همان id معروف ويديوي يوتيوب مورد نظر است. براي مثال اگر لينك ويديوي ما http://www.youtube.com/watch?v=-PA-buwI3q4 باشد، لينك زيرنويس انگليسي هماهنگ با آن http://video.google.com/timedtext?hl=en&v=-PA-buwI3q4&lang=en خواهد بود.

البته زمان‌هاي اين فايل xml يك نكته‌ي خاص گوگلي دارد!
اگر عدد 3.4 را مشاهده كرديد يعني سه ثانيه و 400 ميلي ثانيه
اگر عدد 3.04 را مشاهده كرديد يعني سه ثانيه و 40 ميلي ثانيه
به عبارتي با قسمت اعشاري آن بايد به صورت رشته برخورد كنيد. اگر طول آن يك بود، دو صفر و اگر طول آن 2 بود، 1 صفر در جلوي آن بايد قرار گيرد تا قابل استفاده شود.

كلاس تهيه شده زير كار تبديل Xml گوگل را به Srt انجام مي‌دهد:
using System;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml.Linq;

namespace YtSubs
{
public class GoogleXmlToSrt
{
public string ConvertXmlToSrt(string url)
{
var transcript = loadTranscript(url);

var srtBuf = new StringBuilder();
var lineNumber = 0;

foreach (var item in transcript.Root.Elements("text"))
{
lineNumber++;
srtBuf.Append(lineNumber.ToString());

var timeLine = getTimeLine(item);
srtBuf.AppendFormat("{0}{1} --> {2}{3}", Environment.NewLine, timeLine.Item1, timeLine.Item2, Environment.NewLine);

var msg = decodeText(item);
srtBuf.AppendLine(msg + Environment.NewLine);
}
return srtBuf.ToString();
}

private static XDocument loadTranscript(string url)
{
var transcript = XDocument.Load(url);

if (transcript.Root == null)
throw new InvalidOperationException("couldn't load received data.");
return transcript;
}

private static string decodeText(XElement text)
{
var textValue = text.Value.Split('\n');
var msg = textValue.Aggregate(
string.Empty, (current, line) =>
current + (HttpUtility.HtmlDecode(line.Trim()) + Environment.NewLine)
);
return msg.Trim();
}

private static int fractionalPart(string data)
{
var idx = data.IndexOf(".");
if (idx == -1) return 0;
var fractionalPart = data.Substring(idx + 1);
if (fractionalPart.Length == 1) //3.4 --> 3.400
return int.Parse(fractionalPart + "00");

if (fractionalPart.Length == 2) //3.04 --> 3.040
return int.Parse(fractionalPart + "0");

return int.Parse(fractionalPart.Substring(0, 3));
}

private Tuple<string, string> getTimeLine(XElement text)
{
var startTs = getStartTs(text);
TimeSpan endTs = getEndTs(text, startTs);
return new Tuple<string, string>(timeSpanToString(startTs), timeSpanToString(endTs));
}

private static TimeSpan getStartTs(XElement text)
{
var startData = text.Attribute("start");
if (startData == null)
throw new InvalidOperationException("This is not a valid subtitle file.");

var start = startData.Value;
var startTs = new TimeSpan(0, 0, 0, (int)Math.Truncate(double.Parse(start)), fractionalPart(start));
return startTs;
}

private static TimeSpan getEndTs(XElement text, TimeSpan startTs)
{
TimeSpan endTs;
var durData = text.Attribute("dur");
if (durData == null)
{
endTs = startTs + new TimeSpan(0, 0, 0, 3, 0);
}
else
{
var dur = durData.Value;
var durTs = new TimeSpan(0, 0, 0, (int)Math.Truncate(double.Parse(dur)), fractionalPart(dur));
endTs = startTs + durTs;
}
return endTs;
}

private static string timeSpanToString(TimeSpan lineTs)
{
return string.Format("{0}:{1}:{2},{3}",
lineTs.Hours.ToString("D2"),
lineTs.Minutes.ToString("D2"),
lineTs.Seconds.ToString("D2"),
lineTs.Milliseconds.ToString("D3"));
}
}
}
و يك نمونه از استفاده آن مي‌تواند به شكل زير باشد:
using System.IO;

namespace YtSubs
{
class Program
{
static void Main(string[] args)
{
var url = "http://video.google.com/timedtext?hl=en&v=-PA-buwI3q4&lang=en";
//var url = "subtitle.xml";
var srtBuf = new GoogleXmlToSrt().ConvertXmlToSrt(url);
File.WriteAllText("-PA-buwI3q4.srt", srtBuf);
}
}
}


مطالب تكميلي:
همه چیز درباره زیرنویس ویدئوهای گوگل