<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>OUseful.Info, the blog... &#187; Twitter Powered Subtitles for Conference Audio/Videos on Youtube</title>
	<atom:link href="http://blog.ouseful.info/2009/03/08/twitter-powered-subtitles-for-conference-audiovideos-on-youtube/feed/?withoutcomments=1" rel="self" type="application/rss+xml" />
	<link>http://blog.ouseful.info</link>
	<description>Trying to find useful things to do with emerging technologies in open education</description>
	<lastBuildDate>Fri, 24 May 2013 12:19:26 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='blog.ouseful.info' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>OUseful.Info, the blog... &#187; Twitter Powered Subtitles for Conference Audio/Videos on Youtube</title>
		<link>http://blog.ouseful.info</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://blog.ouseful.info/osd.xml" title="OUseful.Info, the blog..." />
	<atom:link rel='hub' href='http://blog.ouseful.info/?pushpress=hub'/>
		<item>
		<title>Twitter Powered Subtitles for Conference Audio/Videos on Youtube</title>
		<link>http://blog.ouseful.info/2009/03/08/twitter-powered-subtitles-for-conference-audiovideos-on-youtube/</link>
		<comments>http://blog.ouseful.info/2009/03/08/twitter-powered-subtitles-for-conference-audiovideos-on-youtube/#comments</comments>
		<pubDate>Sun, 08 Mar 2009 16:04:58 +0000</pubDate>
		<dc:creator>Tony Hirst</dc:creator>
				<category><![CDATA[Tinkering]]></category>
		<category><![CDATA[Visualisation]]></category>
		<category><![CDATA[captions]]></category>
		<category><![CDATA[subtitles]]></category>
		<category><![CDATA[Twitter]]></category>
		<category><![CDATA[youtube]]></category>

		<guid isPermaLink="false">http://ouseful.wordpress.com/?p=1400</guid>
		<description><![CDATA[Last week, I wrote a post on hHow people Tweeted Through Carter on “Delivering Digital Britain” at NESTA, where I created a slideshow of tweets posted in response to the NESTA Delivering Britain event/video stream and used them to illustrate the audio recording of Lord Carter&#8217;s presentation. Chatting to @liamgh last week, I mentioned how [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.ouseful.info&#038;blog=325417&#038;post=1400&#038;subd=ouseful&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Last week, I wrote a post on h<a href="http://ouseful.wordpress.com/2009/03/03/tweetmash-how-people-tweeted-through-carter-on-delivering-digital-britain-at-nesta/">How people Tweeted Through Carter on “Delivering Digital Britain” at NESTA</a>, where I created a slideshow of tweets posted in response to the NESTA Delivering Britain event/video stream and used them to illustrate the audio recording of Lord Carter&#8217;s presentation.</p>
<p><a href="http://www.flickr.com/photos/psychemedia/3337530057/" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3356/3337530057_ef1f8dab11.jpg" width="373" height="141"></a></p>
<p>Chatting to <a href="http://twitter.com/liamgh">@liamgh</a> last week, I mentioned how i was stumped for an easy way to do this. He suggested creating a subtitles feed, and then uploading it to Youtube, along with the audio recording (doh!).</p>
<p>So &#8211; here&#8217;s a <a href="http://www.youtube.com/watch?v=tBmFzF8szpo&amp;cc_load_policy=1">proof of concept</a> result of doing just that:</p>
<span class='embed-youtube' style='text-align:center; display: block;'><iframe class='youtube-player' type='text/html' width='700' height='424' src='http://www.youtube.com/embed/tBmFzF8szpo?version=3&#038;rel=1&#038;fs=1&#038;showsearch=0&#038;showinfo=1&#038;iv_load_policy=1&#038;wmode=transparent' frameborder='0'></iframe></span>
<p>(Actually, I&#8217;m not sure that subtitles/closed captions work in the embedded movies, so you may have to click through: <a href="http://www.youtube.com/watch?v=tBmFzF8szpo">proof of concept use of Tweets as subtitles: <em>Carter at &#8216;Delivering Digital Britain&#8217;, part 1</em></a>. <em>NB it is possible force embedded videos to show captions, as <a href="http://www.google.com/support/youtube/bin/answer.py?hl=en&amp;answer=140174"><br />
Learn More: Showing captions by default</a> describes. Simply add <strong>&amp;cc_load_policy=1 </strong> to the embed code; so e.g. in WordPress, you would use something like <strong>&#91;youtube=http://www.youtube.com/watch?v=tBmFzF8szpo&amp;cc_load_policy=1 &#93;</strong>.</em>)</p>
<p>And here&#8217;s how I did it&#8230;</p>
<p>The first thing to do was check out how Youtube handled subtitles: <a href="http://help.youtube.com/support/youtube/bin/answer.py?answer=100077">Getting Started: Adding / Editing captions</a>.</p>
<p>The trick is to upload a textfile with lines that look something like this:<br />
<tt>0:03:14.159<br />
Text shown at 3 min 14.159 sec for an undefined length of time.</p>
<p>0:02:20.250,0:02:23.8<br />
Text shown at 2 min 20.25 sec, until 2 min 23.8 sec</tt></p>
<p>Secondly &#8211; getting the list of tweets hashtagged with <em>#carter</em> over the period Lord Carter was speaking (i.e. the period covered by the video). For the original proof of concept, I used the tweets from the spreadsheet of scraped tweets that <a href="http://twitter.com/benosteen">@benosteen</a> grabbed for me, though it later occurred to me I could get the tweets direct from a Twitter search feed (as I&#8217;ll show in a  minute).</p>
<p><a href="http://spreadsheets.google.com/ccc?key=p1rHUqg4g420HKin0HWQzLg&amp;hl=en_GB" title="Scraped tweet timestamp by psychemedia, on Flickr"><img src="http://farm4.static.flickr.com/3580/3338285452_12c3156394_o.png" width="417" height="249" alt="Scraped tweet timestamp" /></a></p>
<p>The question now was how to get the timecode required for the subtitles file from the timestamp associated with each tweet. Note here that the timecode is the elapsed time from the start of the video. The solution I came up with was to convert the timestamps to universal time (i.e. seconds since midnight on January 1st  1970) and then find the universal time equivalent of the first tweet subtitle; subtracting this time from the universal time of all the other tweets would give the number of seconds elapsed from the first tweet, which I could convert to the timecode format.</p>
<p>As to how I got the universal time values &#8211; I used a Yahoo pipe (<a href="http://pipes.yahoo.com/pipes/pipe.info?_id=Dq_DpygL3hGV7mFEAVYZ7A">Twitter CSV to timeingsd</a>):</p>
<p><a href="http://pipes.yahoo.com/pipes/pipe.info?_id=Dq_DpygL3hGV7mFEAVYZ7A" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3351/3338310318_334889f2ac.jpg" width="500" height="364"></a></p>
<p>At this point, it&#8217;s probably worth pointing out that I didn&#8217;t actually need to call on @benosteen&#8217;s tweetscraper &#8211; I could just use the Twitter search API (i.e. the Twitter advanced search feed output) to grab the tweets. How so? Like this:</p>
<p><a href="http://search.twitter.com/search?q=&amp;ands=&amp;phrase=&amp;ors=&amp;nots=&amp;tag=carter&amp;lang=all&amp;from=&amp;to=&amp;ref=&amp;near=&amp;within=15&amp;units=mi&amp;since=2009-02-24&amp;until=2009-02-24&amp;source=&amp;rpp=50" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3381/3338327834_05a12ddfc9.jpg" width="426" height="500"></a></p>
<p>Looking at the results of this query, we see the timing is a little off &#8211; we actually need results from 8.30am, the actual time of the event:</p>
<p><a href="http://www.flickr.com/photos/psychemedia/3338331714/" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3402/3338331714_78a6e60755.jpg" width="500" height="242"></a></p>
<p>Which is where this comes into play &#8211; searching for &#8220;older&#8221; results:</p>
<p><a href="http://www.flickr.com/photos/psychemedia/3337504685/" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3611/3337504685_115467a9e3.jpg" width="500" height="99"></a></p>
<p>If you click on &#8220;Older&#8221; you&#8217;ll notice a new argument is introduced into the search results page URL &#8211; <em>&amp;page=</em>:</p>
<p><a href="http://www.flickr.com/photos/psychemedia/3337507521/" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3413/3337507521_dacb798b65.jpg" width="388" height="63"></a></p>
<p>&#8230;which means that by selecting appropriate values for <em>rpp=</em> and <em>page=</em> we can tunnel in on the results covering from a particular time by looking at &#8220;older&#8221; results pages, and grabbing the URL for the page of results covering the time period we want:</p>
<p><a href="http://search.twitter.com/search?max_id=1247074821&amp;page=4&amp;rpp=100&amp;q=+%23carter+since%3A2009-02-24+until%3A2009-02-24" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3558/3337518405_e674c836f7.jpg" width="470" height="381"></a></p>
<p>Then we can grab the feed:</p>
<p><a href="http://www.flickr.com/photos/psychemedia/3337521771/" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3658/3337521771_3e7134b499.jpg" width="364" height="136"></a></p>
<p>which gives us something like this:<br />
<tt><a href="http://search.twitter.com/search.atom?q=+%23carter+since%3A2009-02-24+until%3A2009-02-24" rel="nofollow">http://search.twitter.com/search.atom?q=+%23carter+since%3A2009-02-24+until%3A2009-02-24</a></tt></p>
<p>which we hack a little to get to the right period:</p>
<p><em><a href="http://search.twitter.com/search.atom?q=+%23carter+since%3A2009-02-24+until%3A2009-02-24" rel="nofollow">http://search.twitter.com/search.atom?q=+%23carter+since%3A2009-02-24+until%3A2009-02-24</a><strong>&amp;page=4&amp;rpp=100</strong></em></p>
<p>(For more info on the Twitter search API, see the <a href="http://apiwiki.twitter.com/Search+API+Documentation">Twitter Search API Documentation wiki</a>.)</p>
<p><em>NB while I&#8217;m at it, note that there&#8217;s a corollary hack here that might<br />
come in useful somewhere, or somewhen, else &#8211; getting a Twitter search feed into a Google spreadsheet (so we can, for example,process it as a CSV file published from the spreadsheet):</em></p>
<p><tt>=ImportFeed("http://search.twitter.com/search.atom?rpp=100&amp;page=4&amp;q=+%23carter+since%3A2009-02-24+until%3A2009-02-24")</tt></p>
<p><a href="http://www.flickr.com/photos/psychemedia/3338269676/" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3652/3338269676_47d058dfb5.jpg" width="500" height="293"></a></p>
<p><em>That is:</em></p>
<p><a href="http://www.flickr.com/photos/psychemedia/3338272218/" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3642/3338272218_4d405a122b.jpg" width="500" height="105"></a></p>
<p>Okay &#8211; back to the main thread &#8211; and a tweak to the pipe to let us ingest the feed, rather than the spreadsheet CSV:</p>
<p><a href="http://pipes.yahoo.com/pipes/pipe.info?_id=8a37dd62f9acda9fe65614a8666803e4" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3638/3338383344_95e7ae6d20.jpg" width="500" height="421"></a></p>
<p>Just by the by, we can add a search front end to the pipe if we want:</p>
<p><a href="http://pipes.yahoo.com/pipes/pipe.info?_id=8a37dd62f9acda9fe65614a8666803e4" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3299/3338396482_4134573cbc.jpg" width="500" height="393"></a></p>
<p>and construct the Twitter search API URI accordingly:</p>
<p><a href="http://pipes.yahoo.com/pipes/pipe.info?_id=8a37dd62f9acda9fe65614a8666803e4" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3653/3338392780_6166048779.jpg" width="500" height="394"></a></p>
<p>(The date formatter converts the search date to the format required by the Twitter search API; it was constructed according to <a href="http://uk.php.net/strftime">PHP: strftime</a> principles.)</p>
<p>Ok &#8211; so let&#8217;s recap where we&#8217;re at &#8211; we&#8217;ve now got a pipe that will give us universal timecoded tweets (that&#8217;s not so far for such a long post to here, is it?!) If we take the JSON feed from the pipe into an HMTL page, we can write a little handler that will produce the subtitle file from it:</p>
<p><a href="http://www.flickr.com/photos/psychemedia/3338406536/" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3378/3338406536_8aa258edba.jpg" width="500" height="213"></a></p>
<p>Here&#8217;s the code to grab the pipe&#8217;s JSON output into an HTML file:</p>
<p><tt>	var pipeUrl="http://pipes.yahoo.com/pipes/pipe.run?_id=Dq_DpygL3hGV7mFEAVYZ7A&amp;aqs";</p>
<p>    function ousefulLoadPipe(url){</p>
<p>	var d=document;<br />
	var s=d.createElement('script');<br />
	s.type='text/javascript';</p>
<p>	var pipeJSON=url+"&amp;_render=json&amp;_callback=parseJSON";<br />
	s.src=pipeJSON;<br />
	d.body.appendChild(s);<br />
    }<br />
    ousefulLoadPipe(pipeUrl);<br />
</tt></p>
<p>Here&#8217;s the JSON handler:</p>
<p><tt>    function parseJSON(json_data){<br />
    	var caption; var timestamp=0; var mintime=json_data.value.items[0]['datebuilder'].utime;<br />
    	for (var i=0; itimestamp) mintime=timestamp;<br />
    	}</p>
<p>    	for (var j=json_data.value.items.length-1; j&gt;=0; j--) {<br />
    		caption=json_data.value.items[j]['title'];<br />
    		timestamp=1*json_data.value.items[j]['datebuilder'].utime;<br />
    		if (j&gt;0) timeEnd=(1*json_data.value.items[j-1]['datebuilder'].utime)-3; else timeEnd=10+1*json_data.value.items[j]['datebuilder'].utime;<br />
    		if (timeEnd&lt;timestamp) timeEnd=timestamp+2;<br />
    		timecode=getTimeCode(timestamp-mintime);<br />
    		timeEnd=getTimeCode(timeEnd-mintime);<br />
    		var subtitle=timecode+","+timeEnd+"<br />"+caption+"&lt;br/&gt;&lt;br/&gt;";<br />
    		document.write(subtitle);<br />
    	}<br />
    }</tt></p>
<p>Here&#8217;s the timecode formatter:</p>
<p><tt>//String formatter from: <a href="http://forums.devshed.com/javascript-development-115/convert-seconds-to-minutes-seconds-386816.html" rel="nofollow">http://forums.devshed.com/javascript-development-115/convert-seconds-to-minutes-seconds-386816.html</a><br />
String.prototype.pad = function(l, s){<br />
return (l -= this.length) &gt; 0 ? (s = new Array(Math.ceil(l / s.length) + 1).join(s)).substr(0, s.length) + this + s.substr(0, l - s.length) : this; };</p>
<p>	function getTimeCode(seconds){<br />
		var timecode="00:"+(Math.floor(seconds / 60)).toFixed().pad(2, "0") + ":" + (seconds % 60).toFixed().pad(2, "0")+".0";<br />
		return timecode;<br />
	}</tt></p>
<p>(I generated the timecodes in part using a string formatter from <a href="http://forums.devshed.com/javascript-development-115/convert-seconds-to-minutes-seconds-386816.html">http://forums.devshed.com/javascript-development-115/convert-seconds-to-minutes-seconds-386816.html</a>.)</p>
<p>Copy and paste the output into a text file and save it with the <em>.sub</em> suffix, to give a file which can then be uploaded to Youtube.</p>
<p>So that&#8217;s the subtitle file &#8211; how about getting the audio into Youtube? I&#8217;d already grabbed an audio recording of Carter&#8217;s presentation using Audacity (wiring the &#8220;headphones out&#8221; to the &#8220;microphone in&#8221; on my laptop and playing the recording from  the NESTA site), so I just clipped the first 10 minutes (I think Youtube limits videos to 10 mins?) and saved the file as a <em>wav</em> file, then imported it into iMovie (thinking I might want to add some images, e.g. from photos of the event on flickr). This crib &#8211; <a href="http://www.frommyexperience.com/imovie-video-settings-youtube.php">iMovie Settings for Upload to YouTube</a> &#8211; gave me the settings I needed to export the audio/video from my old copy of iMovie to a file format I could upload to Youtube (I think more recent versions of iMovie support a &#8220;Share to Youtube&#8221; option?).</p>
<p>I then uploaded this file, along with the subtitles file:</p>
<p><a href="http://www.youtube.com/watch?v=tBmFzF8szpo&amp;cc_load_policy=1" title="Photo Sharing"><img src="http://farm4.static.flickr.com/3554/3337601713_b9795e1b67.jpg" width="500" height="330"></a></p>
<p>So there we have it: Twitter subtitle/annotations (pulled from a Twitter search feed) to the first part of Lord Carter&#8217;s presentation at Delivering Digital Britain&#8230;</p>
<p>PS Also on the Twitter front, O&#8217;Reilly have started watching Twitter for links to interesting stories, or into particular debates: <a href="http://broadcast.oreilly.com/2009/03/twitscan-the-debate-over-open.html">Twitscan: The Debate over &#8220;Open Core&#8221;</a>.</p>
<p>Chatting to <a href="http://twitter.com/cheslincoln">@cheslincoln</a> the other night, we got into a discussion about whether or not Twitter could be used to support a meaningful discussion or conversation, given the immediacy/short lived nature of tweets and the limited character count. I argued that by linking out to posts to support claims in tweets, &#8220;hyper-discussions&#8221; <em>were</em> possible. By mining &#8220;attention trends&#8221; (a term I got from misreading a <a href="http://twitter.com/paulwalk/status/1292804920">tweet of Paul Walk&#8217;s</a> that scaffold a conversation, it&#8217;s possible to create a summary post of a conversation, or argument, like the O&#8217;Reilly one?</p>
<p>See also this post from Paul Walk: <a href="http://blog.paulwalk.net/2009/03/06/anything-you-quote-from-twitter-is-always-out-of-context/">Anything you quote from Twitter is always out of context</a>.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/ouseful.wordpress.com/1400/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/ouseful.wordpress.com/1400/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.ouseful.info&#038;blog=325417&#038;post=1400&#038;subd=ouseful&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://blog.ouseful.info/2009/03/08/twitter-powered-subtitles-for-conference-audiovideos-on-youtube/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/abbd9f90565ce9ae4d065d93a81d8c03?s=96&#38;d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">Tony Hirst</media:title>
		</media:content>

		<media:content url="http://farm4.static.flickr.com/3356/3337530057_ef1f8dab11.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3580/3338285452_12c3156394_o.png" medium="image">
			<media:title type="html">Scraped tweet timestamp</media:title>
		</media:content>

		<media:content url="http://farm4.static.flickr.com/3351/3338310318_334889f2ac.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3381/3338327834_05a12ddfc9.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3402/3338331714_78a6e60755.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3611/3337504685_115467a9e3.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3413/3337507521_dacb798b65.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3558/3337518405_e674c836f7.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3658/3337521771_3e7134b499.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3652/3338269676_47d058dfb5.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3642/3338272218_4d405a122b.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3638/3338383344_95e7ae6d20.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3299/3338396482_4134573cbc.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3653/3338392780_6166048779.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3378/3338406536_8aa258edba.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3554/3337601713_b9795e1b67.jpg" medium="image" />
	</item>
	</channel>
</rss>
