<?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/"
	>

<channel>
	<title>TechnoBabel &#187; itunes</title>
	<atom:link href="http://www.fritscher.ch/blog/tag/itunes/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.fritscher.ch/blog</link>
	<description>Discoveries from the Virtual World</description>
	<lastBuildDate>Sat, 04 Jun 2011 20:59:16 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Browsing iTunesU without intalling iTunes</title>
		<link>http://www.fritscher.ch/blog/2009/05/13/browsing-itunesu-without-intalling-itunes/</link>
		<comments>http://www.fritscher.ch/blog/2009/05/13/browsing-itunesu-without-intalling-itunes/#comments</comments>
		<pubDate>Wed, 13 May 2009 20:51:59 +0000</pubDate>
		<dc:creator>Boris Fritscher</dc:creator>
				<category><![CDATA[Computer Programming]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[groovy]]></category>
		<category><![CDATA[itunes]]></category>
		<category><![CDATA[itunesU]]></category>
		<category><![CDATA[proxy]]></category>
		<category><![CDATA[StreamingMarkupBuilder]]></category>
		<category><![CDATA[XmlSlurper]]></category>

		<guid isPermaLink="false">http://www.fritscher.ch/blog/?p=82</guid>
		<description><![CDATA[Updated version of the script On some pages the location-bar information is available (third-level pages), but 1st and 2nd level pages do not have a location-bar. Even in iTunes it has been removed in favor of a location-bar managed by &#8230; <a href="http://www.fritscher.ch/blog/2009/05/13/browsing-itunesu-without-intalling-itunes/">Continue reading <span class="meta-nav">&#8594;</span></a>
No related posts.]]></description>
			<content:encoded><![CDATA[<h3>Updated version of the script</h3>
<p>On some pages the location-bar information is available (third-level pages), but 1st and 2nd level pages do not have a location-bar. Even in iTunes it has been removed in favor of a location-bar managed by iTunes. I suppose this is done in order to make use of a cached version of the page and speed up loading. Having no location-bar at 2nd level pages is an awkward design choice from Apple. Therefore location-bar has to be managed by the script and 3th level pages need also to be re-adapted to be displayed correctly.</p>
<p>Fixed version can be tested here: <a href="http://1.latest.unil-podcast.appspot.com/index.groovy?handle=">http://1.latest.unil-podcast.appspot.com/index.groovy?handle=</a><em>[itunesU url]</em></p>
<p>the new source files are available here:<br />
complete source code at github <a href="http://github.com/bfritscher/www-itunesu">http://github.com/bfritscher/www-itunesu</a></p>
<p><a href="http://www.fritscher.ch/blog/wp-content/uploads/2009/05/index_fixed.groovy">index_fixed.groovy</a> &#8211; new version with navigation fixed<br />
<a href="http://www.fritscher.ch/blog/wp-content/uploads/2009/05/proxy.groovy">proxy.groovy</a> &#8211; proxy class because iTunes checks user-agent to link to some video files behind a redirect.</p>
<h3>The problem</h3>
<p>More and more universities publish courses as podcasts on iTunesU and neglect to give a direct access to their content. This hinders sharing of knowledge, because of the obligatory Apple’s iTunes application needed to access iTunesU.</p>
<h3>The solution</h3>
<p>Emulating the iTunes client: the script can be tested at</p>
<p><a href="http://www2.unil.ch/itunesu/index.groovy?handle=">http://www2.unil.ch/itunesu/index.groovy?handle=</a><em>[itunesU url]</em></p>
<p>It only needs the url which would open in iTunes and converts it to a displayable webpage.</p>
<p>For example for mit.edu: <a href="http://deimos.apple.com/WebObjects/Core.woa/Browsev2/mit.edu">http://deimos.apple.com/WebObjects/Core.woa/Browsev2/mit.edu</a></p>
<p><a href="http://www2.unil.ch/itunesu/index.groovy?handle=http://deimos.apple.com/WebObjects/Core.woa/Browsev2/mit.edu">http://www2.unil.ch/itunesu/index.groovy?handle=http://deimos.apple.com/WebObjects/Core.woa/Browsev2/mit.edu</a></p>

<a href='http://www.fritscher.ch/blog/2009/05/13/browsing-itunesu-without-intalling-itunes/itunesu-mit/' title='itunesu-mit'><img width="150" height="150" src="http://www.fritscher.ch/blog/wp-content/uploads/2009/05/itunesu-mit-150x150.jpg" class="attachment-thumbnail" alt="itunesu-mit" title="itunesu-mit" /></a>
<a href='http://www.fritscher.ch/blog/2009/05/13/browsing-itunesu-without-intalling-itunes/itunesu-unil/' title='itunesu-unil'><img width="150" height="150" src="http://www.fritscher.ch/blog/wp-content/uploads/2009/05/itunesu-unil-150x150.jpg" class="attachment-thumbnail" alt="itunesu-unil" title="itunesu-unil" /></a>

<h3>Source download</h3>
<p><a href="http://www.fritscher.ch/blog/wp-content/uploads/2009/05/index.groovy">index.groovy version 1</a> iTunesU xml to html groovy script</p>
<h4>Why groovy?</h4>
<p>Having experience with Java and <a href="http://grails.org/">Grails</a> development using <a href="http://groovy.codehaus.org/">Groovy</a> especially due to its powerful closures, xml navigation and html building was a obvious choice. Also this project started as a Google AppEngine prototype, but hit the no-user-agent change limitation.</p>
<h4>Limitations</h4>
<p>Of the three types of layout used in iTunes U only the two column standard university page view and detailed course view (track list) are supported. The iTunes main site listing and main page three column layout is not supported.</p>
<h3>Solution explained</h3>
<p>iTunes uses a custom xml page to receive and render its content. This file can be access when switching the user-agent to “iTunes”.<br />
Switching user agent is easy under Firefox with the <a title="user-agent switcher add-on" href="https://addons.mozilla.org/en-US/firefox/addon/59">user-agent switcher add-on</a><br />
Part of the xml file</p>
<pre class="brush: xml; title: ; notranslate">
&lt;FontStyleSet setName=&quot;basic22&quot; normalStyle=&quot;helvetica22&quot;
	linkStyle=&quot;link&quot; linkPressedStyle=&quot;linkPressed&quot; linkRolloverStyle=&quot;linkRollover&quot; /&gt;
&lt;FontStyleSet setName=&quot;normal40&quot; normalStyle=&quot;lucida40&quot;
	linkStyle=&quot;link&quot; linkPressedStyle=&quot;linkPressed&quot; linkRolloverStyle=&quot;linkRollover&quot; /&gt;
&lt;FontStyleSet setName=&quot;normal40Bold&quot; normalStyle=&quot;lucida40Bold&quot;
	linkStyle=&quot;link&quot; linkPressedStyle=&quot;linkPressed&quot; linkRolloverStyle=&quot;linkRollover&quot; /&gt;
&lt;MatrixView rightInset=&quot;0&quot; bottomInset=&quot;0&quot; leftInset=&quot;0&quot;
	topInset=&quot;0&quot; rowFormat=&quot;100%,*&quot; viewName=&quot;Form&quot;&gt;
	&lt;View rightInset=&quot;0&quot; bottomInset=&quot;0&quot; leftInset=&quot;0&quot; topInset=&quot;0&quot;
		minWidth=&quot;948&quot; minHeight=&quot;400&quot;&gt;
		&lt;VBoxView&gt;
			&lt;!-- Layer 1 = Banner Image --&gt;
			&lt;!-- BEGIN ReplaceableImageXMLView --&gt;
			&lt;View alt=&quot;&quot; height=&quot;400&quot; width=&quot;600&quot;&gt;
				&lt;PictureView proportional=&quot;1&quot; verticalAlignment=&quot;top&quot;
					shouldBeVisible=&quot;true&quot; shadowHeight=&quot;0&quot;
					alt=&quot;MIT offers a selection of video and audio from several of our groundbreaking projects: MIT OpenCourseWare, MIT World, MIT TechTV, and Visualizing Cultures.&quot;
					shadowWidth=&quot;0&quot; height=&quot;400&quot; width=&quot;600&quot;
					url=&quot;http://deimos3.apple.com/indigo//2c/c5/f3/93/2cc5f393018ecb9b1a3991063e10376b093cd1918f43b3847af32a8a9ba35103-1502158938.jpg&quot;
					addShadowSizes=&quot;false&quot; /&gt;
			&lt;/View&gt;
			&lt;!-- END ReplaceableImageXMLView --&gt;
		&lt;/VBoxView&gt;
		&lt;VBoxView&gt;
			&lt;!-- Layer 2 = link box area --&gt;
			&lt;MatrixView leftInset=&quot;0&quot; rightInset=&quot;0&quot; topInset=&quot;25&quot;
				columnFormat=&quot;65%,25,35%&quot;&gt;
				&lt;!--
					This ensures we leave enough room for the banner image in the 3
					column view
				--&gt;
				&lt;View minHeight=&quot;400&quot; /&gt;
				&lt;View /&gt;
				&lt;VBoxView rightInset=&quot;25&quot; bottomInset=&quot;0&quot; leftInset=&quot;0&quot;
					topInset=&quot;0&quot;&gt;
					&lt;VBoxView&gt;
						&lt;VBoxView leftInset=&quot;0&quot; rightInset=&quot;0&quot; topInset=&quot;0&quot;
							bottomInset=&quot;0&quot;&gt;
							&lt;!-- BEGIN description box--&gt;
							&lt;!-- END description box--&gt;
							&lt;!-- START ListBoxStack --&gt;
							&lt;View&gt;
								&lt;FontStyle name=&quot;outlineTitleFontStyle&quot; color=&quot;ffffff&quot; /&gt;
								&lt;FontStyle name=&quot;outlineTextFontStyle&quot; color=&quot;ffffff&quot; /&gt;
								&lt;!-- BEGIN RoundedBox --&gt;
								&lt;View rightInset=&quot;0&quot; bottomInset=&quot;0&quot; leftInset=&quot;0&quot;
									topInset=&quot;0&quot;&gt;
									&lt;Test value=&quot;7.0.0&quot; comparison=&quot;greater or equal&quot;
										property=&quot;iTunes version&quot;&gt;
										&lt;!-- BEGIN MaskedView --&gt;
										&lt;View rightInset=&quot;0&quot; bottomInset=&quot;0&quot; leftInset=&quot;0&quot;
											topInset=&quot;0&quot;&gt;
											&lt;PictureButtonView rightInset=&quot;0&quot; topInset=&quot;0&quot;
												bottomInset=&quot;0&quot; leftInset=&quot;0&quot; alt=&quot;&quot; color=&quot;rgba(0,0,0,0.75)&quot;
												mask=&quot;http://deimos3.apple.com/rsrc/Images/masks/rounded_box.png&quot;
												cap=&quot;4&quot; /&gt;

										&lt;/View&gt;
									&lt;/Test&gt;
								&lt;/View&gt;
							&lt;/View&gt;
						&lt;/VBoxView&gt;
					&lt;/VBoxView&gt;
				&lt;/VBoxView&gt;
			&lt;/MatrixView&gt;
		&lt;/VBoxView&gt;
	&lt;/View&gt;
&lt;/MatrixView&gt;
</pre>
<p>The xml file is quite verbose and it takes some time to identify the interesting content.<br />
The hard part of this project was to identify the right way to identify and select the wanted data from the xml tree.</p>
<p>Following are code snippets of the source file with their most important or interesting aspects explained.</p>
<p>First task of the script is to retrieve the xml page by settings the right user-agent [3] and then use the powerful groovy xml parser api [13] to have an easily navigable xml object.</p>
<pre class="brush: groovy; highlight: [3,13]; title: ; notranslate">
def xml
def conn = new URL(handle).openConnection()
conn.setRequestProperty ( &quot;User-Agent&quot;, &quot;iTunes/8.1&quot; )
def putBackTogether = new StringBuffer()
def r = new InputStreamReader ( conn.getInputStream(), &quot;UTF-8&quot; )
char [  ]  cb = new char [ 2048 ]
int amtRead = r.read ( cb )
while  ( amtRead  &gt;  0 )   {
	putBackTogether.append ( cb, 0, amtRead )
	amtRead = r.read ( cb )
}
xml = putBackTogether.toString()
xml = new XmlSlurper().parseText(xml)
</pre>
<p>To work around default namespace bugs when recreating a xml string from an xml object the Markupbuilder has to be set to the apple namespace[6].</p>
<pre class="brush: groovy; highlight: [6]; title: ; notranslate">
import groovy.xml.StreamingMarkupBuilder
def getXml(item){
	def outputBuilder = new StreamingMarkupBuilder()
	outputBuilder.encoding = &quot;UTF-8&quot;
	String result = outputBuilder.bind{
		mkp.declareNamespace(&quot;&quot;:&quot;http://www.apple.com/itms/&quot;)
		mkp.yield item
	}
}
</pre>
<p>Example of the powerful way to generate html code with the groovy MarkupBuilder.</p>
<pre class="brush: groovy; highlight: [2]; title: ; notranslate">
import groovy.xml.StreamingMarkupBuilder
html.html(xmlns:&quot;http://www.w3.org/1999/xhtml&quot;,lang:&quot;en&quot;,'xml:lang':&quot;en&quot;) {
	head {
		title xml.Path.PathElement.collect{it.'@displayName'}.join(&quot; &gt; &quot;)
		meta('http-equiv':&quot;X-UA-Compatible&quot;, content:&quot;IE=8&quot;)
		meta('http-equiv':&quot;content-type&quot;,content:&quot;application/xhtml+xml; charset=UTF-8&quot;)
		//...
</pre>
<p>Identifying the type of layout the xml file is meant to build is done by looking at a reflection attribute from the first image found in the file.</p>
<pre class="brush: groovy; title: ; notranslate">
def topimg = xml.'**'.find{ it.name() == &quot;PictureView&quot;}
def reflect = topimg?.'@reflection'==1 //true = course page
</pre>
<p>Getting page colors from FontStyle tags</p>
<pre class="brush: groovy; title: ; notranslate">
def titleFontStyle = xml.'**'.find{it.name() == &quot;FontStyle&quot; &amp;&amp; it.'@name' == &quot;normalTitleFontStyle&quot;}?.'@color'
if(titleFontStyle == null){
	titleFontStyle = xml.'**'.find{it.name() == &quot;FontStyle&quot; &amp;&amp; it.'@name' == &quot;outlineTitleFontStyle&quot;}?.'@color'
}
</pre>
<p>Some more examples of the groovy MarkupBuilder: especially interesting is the mkp.yieldUnescaped [10] method to allow usage of html otherwise automatically converted symbols like &#038;.</p>
<pre class="brush: groovy; highlight: [10]; title: ; notranslate">
body(style:&quot;background: #${xml.'*'.find{ it.'@backColor' != ''}.'@backColor'}&quot;){
	div(class:&quot;container&quot;){
		div(class:&quot;span-24 last nav&quot;){
			def list = xml.Path.PathElement
			def last = list.size()-1
			list.eachWithIndex{ pe,i -&gt;
				if(i == 0){
					a(class:&quot;first&quot;,href:&quot;index.groovy?handle=${pe.text().trim()}&quot;){
						span(){
							mkp.yieldUnescaped &quot;&amp;nbsp;&quot;
						}
					}
				}else{
					a(class:&quot;${i == last ? 'last' : ''}&quot;,href:&quot;index.groovy?handle=${pe.text().trim()}&quot;, pe.'@displayName')
				}
			}
		}
	}
}
</pre>
<p>Being able to identify the right element is sometimes a bit tricky, especially that not all pages seem to follow the same structure rules.</p>
<pre class="brush: groovy; title: ; notranslate">
div(class:&quot;description&quot;){
	xml.ScrollView.View.MatrixView.View?.'**'.find{it?.name()== &quot;VBoxView&quot; &amp;&amp; it?.parent()?.name()== &quot;VBoxView&quot; &amp;&amp; it?.parent()?.parent()?.name()== &quot;VBoxView&quot; &amp;&amp; it?.parent()?.parent()?.parent()?.name()== &quot;VBoxView&quot;}?.'*'.findAll{ it?.name() == &quot;TextView&quot;}.each{ text -&gt; p text }
	mkp.yieldUnescaped &quot;&amp;nbsp;&quot;
}
</pre>
<p>Usage of the <a href="http://code.google.com/p/xmlwise/">xmlwise</a> library[15] to parse the standard plist part of the xml containing the track list .</p>
<pre class="brush: groovy; highlight: [15]; title: ; notranslate">
import xmlwise.*
div(class:&quot;span-24 last tracklist&quot;){
	table(cellspacing:&quot;1&quot;, class:&quot;tablesorter&quot;){
		thead{
			tr{
				th &quot;&quot;
				th &quot;Name&quot;
				th &quot;Time&quot;
				th &quot;Artist&quot;
				th &quot;Release Date&quot;
				th &quot;&quot;
			}
		}
		tbody{
			def plist = Plist.fromXml(getXml(xml.TrackList.plist))
			plist.items.eachWithIndex{ track, i -&gt;
				int seconds = (track[&quot;duration&quot;] /1000)
				int minutes = seconds % 3600
				int hours = (seconds - minutes) / 3600
				seconds = minutes % 60
				minutes = (minutes - seconds) / 60

				tr(class:i % 2 == 0 ? &quot;even&quot; : &quot;odd&quot;){
					td track[&quot;rank&quot;]
					td track[&quot;songName&quot;]
					td ((hours &gt; 0 ? String.format(&quot;%02d&quot;,hours)  + &quot;:&quot; : &quot;&quot;)+ String.format(&quot;%02d&quot;,minutes) + &quot;:&quot; + String.format(&quot;%02d&quot;,seconds))
					td track[&quot;artistName&quot;]
					td track[&quot;releaseDate&quot;][0..9]
					td{
						a(href:track[&quot;previewURL&quot;],rel:&quot;lightbox[set 480 380]&quot;,title:track[&quot;songName&quot;], &quot;view&quot;)
						mkp.yieldUnescaped(&quot;&amp;nbsp;&quot;)
						a(href:track[&quot;previewURL&quot;],title:track[&quot;songName&quot;], &quot;download&quot;)
					}
				}
			}
		}
	}
}
</pre>
<h3>Resources used for html design</h3>
<ul>
<li><a href="http://www.blueprintcss.org/">http://www.blueprintcss.org/</a></li>
<li><a href="http://jquery.com/">http://jquery.com/</a> (thankfully, compatible with mootools)</li>
<li><a href="http://www.digitalia.be/software/reflectionjs-for-jquery">http://www.digitalia.be/software/reflectionjs-for-jquery</a></li>
<li><a href="http://mootools.net/">http://mootools.net/</a> (needed for mediabox advanced)</li>
<li><a href="http://iaian7.com/webcode/mediaboxAdvanced">http://iaian7.com/webcode/mediaboxAdvanced</a></li>
<li><a href="http://tablesorter.com/docs/">http://tablesorter.com/docs/</a></li>
<li><a href="http://comparenetworks.com/developers/jqueryplugins/jbreadcrumb.html">http://comparenetworks.com/developers/jqueryplugins/jbreadcrumb.html</a> (only used images)</li>
</ul>
<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://www.fritscher.ch/blog/2009/05/13/browsing-itunesu-without-intalling-itunes/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
	</channel>
</rss>

