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

<channel>
	<title>Ken Keiter</title>
	<atom:link href="http://blog.kenkeiter.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.kenkeiter.com</link>
	<description>Just another WordPress weblog</description>
	<pubDate>Wed, 10 Jun 2009 06:35:55 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Twitter as a Public Utility</title>
		<link>http://blog.kenkeiter.com/2009/03/07/twitter-as-a-public-utility/</link>
		<comments>http://blog.kenkeiter.com/2009/03/07/twitter-as-a-public-utility/#comments</comments>
		<pubDate>Sun, 08 Mar 2009 00:47:32 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.kenkeiter.com/?p=241</guid>
		<description><![CDATA[Is Twitter's architecture agile enough to get out of its own way?]]></description>
			<content:encoded><![CDATA[<p><img src="http://blog.kenkeiter.com/wp-content/uploads/2009/03/twitter_fail_whale-300x225.png" alt="Epic Fail Whale" style="float: right; width: 300px; height: 255px; margin: 0px 0px 10px 10px;" /></p>
<h2>The Fail Whale</h2>
<p>
The popular micro-status service Twitter has a huge problem: itself. With the  explosion of startups and sites dedicated to aggregating Twitter, the Rails-based monolith has been feeling the pain architecturally as it struggles to keep up with its (frankly appalling) 100-API-request-per-hour limit.
</p>
<p>
As a micro-industry grows around it, Twitter is subject to evermore-creative uses such as real-time emergency broadcasting and the aid of disaster recovery, with the assumption that its architecture is strong enough to disseminate critical information in real-time, reliably. This is simply not the case, and serves as a perfect segue into our next question: can lives be trusted to Twitter?
</p>
<h2>And there was Light</h2>
<p>
When Twitter was first created, I doubt that the designers ever thought that it would evolve to become what it has today, nor do I think that they really understand what they&#8217;ve created, and the responsibilities they have to their creation. Today, over three million messages will be posted to Twitter. Yes, the majority of them may be stupid (my personal favorite example: &#8220;I&#8217;m eating a salad.&#8221; &#8230; &#8220;Who gives a fuck!?&#8221;) but there will be a great deal of them that affect the way people live, from meeting arrangements to flood warnings. What happens if the messages don&#8217;t get where they&#8217;re supposed to?
</p>
<p>
It is absolutely critical for Twitter to realize that it is no longer a fancy-free garden-salad-variety website &#8212; Twitter is now a public utility, and in my opinion it is time that they accept that responsibility and start behaving as such.
</p>
<h2>Walking the Talk</h2>
<p>
Twitter has a long way to go if it is to become a utility. Their basic goals should be:</p>
<ol class="steps">
<li><strong>Run from Ruby and Rails</strong> - I love Ruby. I think it is the best language out there, but it and (especially) Rails are not the tools for this job. Ruby&#8217;s interpreter is significantly slower than those of many other languages, and Rails was designed for the agile development crowd. Twitter: You aren&#8217;t really adding new features anymore, therefore you don&#8217;t need the framework that was designed for that subset of rapid development.
<li><strong>99.9999% uptime (the real definition, not your definition)</strong> - First of all, you&#8217;re lying about your uptime. I see the fail-whale on a regular basis. In fact, we&#8217;ve become rather good friends. His name is Mitch. Not Mitchell. He doesn&#8217;t like that. We sit around and drink Mojitos on Friday nights, and play racquetball on Sundays. That&#8217;s how often I freaking see him. If your definition of &#8220;uptime&#8221; applies to your homepage only, then yes, you are up the 99.98% that you claim. People rely on you &#8212; it&#8217;s time to rethink your topology/architecture. Rewrite the whole goddamn thing. Just do it. Don&#8217;t argue.
<li><strong>Be API-centric</strong> - According to Twitter co-founder Biz Stone, your API traffic is an order of magnitude larger than the browser traffic to your site. If that&#8217;s true, then why the hell am I limited to 100 requests an hour? Your savior is going to be the industry you create around yourself, NOT advertising or anything like that. The little industry you&#8217;re creating demands data, so give it to them &#8212; as much as they can handle.</li>
<li><strong>Ensure predictable delivery speed</strong> - I&#8217;m not saying it has to be instantaneous (although it could easily be) &#8212; I&#8217;m just saying that the variable wait time is a problem because you give the impression that it&#8217;s instantaneous. You&#8217;ve effectively become the world&#8217;s most delayed IM service.</li>
</ol>
<h2>Competition Fail</h2>
<p>
I don&#8217;t really know much about Twitter&#8217;s competitors, but I can tell you that they&#8217;ve already lost. FriendFeed and Identica are wonderful services, but they don&#8217;t appeal to the masses. They appeal to the geeks &#8212; and on Twitter the masses are no longer the geeks.
</p>
<p>
That said, I have a ton of respect for Identica. The idea and execution are great, but the marketing required to make it mainstream just isn&#8217;t there. This is one of those annoying illogical cases where it really is almost all in the name!
</p>
<h2>Conclusion</h2>
<p>
Twitter&#8217;s greatest success will come when it realizes that the best companies do one thing, and do it very well. They should be done adding features, and instead focus on redeveloping the entire system and monetizing it. They need to begin charging your biggest API users, and they need to do it fast. Most of all, Twitter must remember its responsibility to its users, and know that someday someone&#8217;s life may rest in their hands.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.kenkeiter.com/2009/03/07/twitter-as-a-public-utility/feed/</wfw:commentRss>
		</item>
		<item>
		<title>NewsInnovation Barcamp Liveblog and Recap.</title>
		<link>http://blog.kenkeiter.com/2009/02/21/newsinnovation-barcamp-liveblog-and-recap/</link>
		<comments>http://blog.kenkeiter.com/2009/02/21/newsinnovation-barcamp-liveblog-and-recap/#comments</comments>
		<pubDate>Sat, 21 Feb 2009 19:02:19 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.kenkeiter.com/?p=208</guid>
		<description><![CDATA[NewsInnovation Barcamp in Portland, OR - February 21, 2009]]></description>
			<content:encoded><![CDATA[<p><img src="http://blog.kenkeiter.com/wp-content/uploads/2009/02/ni-pdx.jpg" alt="ni-pdx logo" style="float: right; margin: 0px 0px 10px 10px;" /></p>
<h2>Yep, it&#8217;s live!</h2>
<p>
Watch streaming online at: <a href="http://www.mogulus.com/newsinnovation_portland">http://www.mogulus.com/newsinnovation_portland</a> The conference is currently going on. You can tune in at the link above to watch and chat. You can also find me on twitter for tiny updates at: <a href="http://twitter.com/kenkeiter/">http://twitter.com/kenkeiter/</a>
</p>
<h2>Live Content</h2>
<p>
<strong>11:00 AM:</strong> We&#8217;re currently discussing bullet-points from our individual discussions on the news business models. Emphasis includes: new data visualization tools, collaboration with the audience, requiring better technology skills in journalists. We&#8217;ve also noticed that production in the real-world and production in print face the challenge of a lack of timeline on the web. One of the concerns is that people who are producing for the web lose their scope - we need virtual newsrooms? Journalists need the back-and-forth between peers to gather information? Many disagreements about this. Should consumers provide the primary critique!
</p>
<p>
<strong>11:09 AM:</strong> Consumer driven media versus media outlets as gatekeepers &#8212; deciding what stories the public sees. Big topic of contention.
</p>
<p>
<strong>11:15 AM:</strong> What is the threshold between when consumers want content to be forced to them versus when they gather it themselves. Someone made the point that human interest stories are dugg down? First thing I&#8217;ve ever heard about that&#8230; How does geographic location affect human interest stories.
</p>
<p>
<strong>11:19 AM:</strong> Let&#8217;s take a break!
</p>
<p>
<strong>12:02 PM:</strong> Talking about marketing and community. How do we generate community and what role should community play in news. Empower users to spread content for you. Talking about EveryBlock. User generated content is advertising &#8212; seems to be the concern here. Is it journalism? Or is is just geo-twitter? Static information versus dynamic information. What is the definition of journalism? Is it the provision of data (no matter how small) to empower your community? Or is it stories that are vetted and go through a real process. CRM in news! Managing the relationship with consumers that GENERATE media. This may be an entirely new application&#8230; Consumers that generate media are critical in an industry that is being killed by teh interwebz.
</p>
<p>
<strong>12:58 PM:</strong> Talked about <a href="http://www.mapwith.us/">http://www.mapwith.us/</a> briefly. We&#8217;re talking about geo-relevance for stories and micro-stories. How to tackle social burnout and get people to continue producing content.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.kenkeiter.com/2009/02/21/newsinnovation-barcamp-liveblog-and-recap/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Roll your own DVR with Python and PyAVC</title>
		<link>http://blog.kenkeiter.com/2009/01/29/write-your-own-dvr-with-pyavc/</link>
		<comments>http://blog.kenkeiter.com/2009/01/29/write-your-own-dvr-with-pyavc/#comments</comments>
		<pubDate>Thu, 29 Jan 2009 23:54:20 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.kenkeiter.com/?p=39</guid>
		<description><![CDATA[Wondering why your cable box has that Firewire (IEEE1394) port? It's so you can use my new PyAVC module and your OS X machine to roll your own DVR.]]></description>
			<content:encoded><![CDATA[<h2>Soo.. I see you&#8217;ve made a DVR.</h2>
<p><img src="http://blog.kenkeiter.com/wp-content/uploads/2009/01/snaketvlrg.jpg" alt="PyAVC" style="float: right;" /></p>
<p>
Let&#8217;s face it: TV is inconvenient. Don&#8217;t you hate it when you&#8217;re rushing home to catch LOST and you spill your coffee all over your lap, scalding yourself in an area that was only meant to be treated nicely, while simultaneously ruining all possibility of hanky-panky with that cute (read: slightly geeky) girl (or guy)? That <strike>was painful</strike>.. never happened to me. Anyhoo, with the release of the PyAVC module, you Python developers out there can cross another fear off your list: the trade of LOST for loins! If you&#8217;ve got a Mac and a cable box with a Firewire (IEE1394) port you can roll your own simple DVR in Python.
</p>
<h2>A <strike>comcastic</strike> fantastic discovery.</h2>
<p>
As a resident of Portland (Oregon) my cable provider is Comcast.
</p>
<p>
No, it&#8217;s not Comcastic in any possible way, shape, or form. It never will be. Even the well-meaning gay man that initially coined the phrase &#8220;comcastic&#8221; while sipping his sixth appletini at &#8220;the club&#8221; is currently crying in a corner somewhere, wishing that &#8212; even through the haze of his Asprin/NyQuil-induced coma &#8212; he&#8217;d kept his mouth shut in that marketing meeting the next day.
</p>
<p>
Ignoring the disparities in service quality and plummeting level of perceived &#8220;comcasticness&#8221;, I was delighted to find that, upon moving to a new apartment, Comcast provided me with an awesome new Motorola DCH-3200 cable box &#8212; a huge improvement over any of their previous models. As I unpacked my new cable box, I took a glance across the back and discovered not only one, but <strong>two</strong> Firewire ports! I anxiously found a cable, plugged it into my Macbook Pro and&#8230; nothing happened. No compatible applications. The equivalent of technological blue balls. After a brief bout of sobbing on the phone with <a href="http://www.youtube.com/watch?v=4nIUcRJX9-o">my bff Jill</a> and the consumption of a gallon of ice cream on the couch while watching <a href="http://www.imdb.com/title/tt0088128/">Sixteen Candles</a>, I began to google.
</p>
<p>
I discovered that my box speaks AVC (Advanced Video Coding) and is actually streaming MPEG-2 TS-encapsulated 1080i video over that Firewire cable! I quickly set to work finding out how to use the protocol under OS X and was delighted to find that Apple provided example code in their <a href="https://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?bundleID=20298">Firewire SDK</a>. I wrote an Objective-C framework that provides an object-oriented interface to the device (or a set of devices) and wrapped it in Python (thanks, <a href="http://pyobjc.sourceforge.net/">pyobjc</a>).
</p>
<h2>Get it. Got it? Good.</h2>
<p>
At this point, if you haven&#8217;t already downloaded PyAVC from the <a href="http://blog.kenkeiter.com/projects/">projects page</a>, you should do so now. Alternately, you can download the latest version from <a href="http://cdn.blog.kenkeiter.com/packages/pyavc_latest.dmg">http://cdn.blog.kenkeiter.com/packages/pyavc_latest.dmg</a>. It&#8217;s tiny (much like my bff Jill) so you shouldn&#8217;t have long to wait.
</p>
<p>
Once you have it, you can go ahead and run the installer package that it contains. Please note that it will request an administrator password along the way. This is so it can do two things: install the necessary frameworks in <code>/Library/Frameworks</code>, and run the PyAVC <code>setup.py</code> script.
</p>
<h2>You said you were going to&#8230; fondle your sweaters.</h2>
<p>
If there&#8217;s anything that the movies have taught me (especially <a href="http://www.imdb.com/title/tt0243655/">Wet Hot American Summer</a> &#8212; an 80&#8217;s themed film set at a summer camp), it is that life is full of little misunderstandings. Some really weird, awkward shit&#8230; but mostly little misunderstandings. Therefore, I like to document everything carefully before someone gets the wrong impression.
</p>
<p>
The PyAVC module (imported as <code>avc</code>) is very simple to use. It contains three relevant classes: <code>AVCCaptureInterface</code>, <code>AVCCaptureDevice</code>, and <code>AVCCaptureException</code>. <code>AVCCaptureInterface</code> instances act as factories for <code>AVCCaptureDevice</code>s. You begin by creating an <code>AVCCaptureInterface</code> instance, and accessing it&#8217;s <code>devices</code> property:</p>
<div class="terminal">
kenkeiter_mbp:~ kkeiter$ python<br />
Python 2.5.1 (r251:54863, Apr 15 2008, 22:57:26)<br />
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin<br />
Type &#8220;help&#8221;, &#8220;copyright&#8221;, &#8220;credits&#8221; or &#8220;license&#8221; for more information.<br />
>>> import avc<br />
>>> interface = avc.AVCCaptureInterface()<br />
>>> interface.devices<br />
[&lt;AVCCaptureDevice 0x2210fffe49c061 (MOTOROLA:DCH-3200) - kDeviceReady&gt;]
</div>
<p>The resulting <code>list</code> is comprised of <code>AVCCaptureDevice</code> instances, upon which you can call the following methods:</p>
<pre class="ascii_diagram">
AVCCaptureDevice
    |- tune_channel(self, channel, channel_minor = None)
    |- begin_recording(self, file_handle)
    |- finish_recording(self)
</pre>
<p>and access the following properties:</p>
<pre class="ascii_diagram">
AVCCaptureDevice
    |- @property name
    |- @property vendor
    |- @property guid
    |- @property deviceState
</pre>
<p>If for some reason something breaks, an <code>AVCCaptureException</code> will be raised. If it&#8217;s my fault, it&#8217;ll raise an <code>AVCCaptureException</code>. If it&#8217;s your fault&#8230; you&#8217;re on your own.
</p>
<p>
For more detailed documentation, you can visit the <a href="http://blog.kenkeiter.com/projects/pyavc/">PyAVC API page</a> or run <code>import avc; help(avc.AVCCaptureInterface)</code> or <code>import avc; help(avc.AVCCaptureDevice)</code> in your Python interactive interpreter for more information.
</p>
<p>
So, let&#8217;s begin with a simple example: recording ten seconds of video from the first device on whatever channel the cable box is currently tuned to:</p>
<pre name="code" class="python">
from time import sleep
from avc import AVCCaptureInterface

interface = AVCCaptureInterface() # create a new AVC interface
my_device = interface.devices[0] # select the first attached device

fh = open('/Users/yourusername/TestVideo.m2t', 'wb') # open the output file
my_device.begin_recording(fh) # begin recording to the output file

sleep(10) # wait for 10 seconds (begin_recording() is a non-blocking method)

my_device.finish_recording() # close it all up.
fh.close()
</pre>
<p>Oh my God, that was so freakin easy! You should now be able to play your newly-created MPEG2-TS file using something like <a href="http://www.videolan.org/vlc/download-macosx.html">VLC Player for OS X</a>.
</p>
<p>
Since PyAVC acts as a wrapper for a Cocoa/Objective-C++ framework, I figured I&#8217;d tell you a little bit about how that all works, as well &#8212; feel free to skip over this (most people won&#8217;t care about this part). The framework (installed under <code>/Libraries/Frameworks/AVCCapture.framework</code>) uses Apple&#8217;s AVCVideoService framework to enumerate, connect to, and stream from AVC devices. The main class, <code>CaptureController</code>, is a singleton that handles detection of AVC devices, and wraps them in an <code>AVCDeviceContainer</code> instance so that they can be handled properly by Python/PyObjC. The AVCDeviceContainer abstracts away a ton of ugly code so that AVCDevices can be managed using simple methods such as <code>- (void) beginRecordingWithFileHandle:(FILE *)theFH</code>.</p>
<h2>Kick it up a notch!</h2>
<p>
Ok, so ten-second video clips are getting boring and your channel-flipping finger is starting to weaken. Let&#8217;s add a little more flexibility and make it so that we can a) call this from the terminal, b) change the channel, and c) record for a set duration. We want our command line syntax to look something like this:</p>
<div class="terminal">
kenkeiter_mbp:~ kkeiter$ dvr -o ~/Desktop/Lost_s5e01_HD1080i.m2t -c 702 -d 2:00:00
</div>
<p>The <code>-o</code> (or <code>--output</code>) parameter should allow us to provide a destination to record to, the <code>-c</code> (or <code>--channel</code>) parameter should allow us to specify a channel (or leave the parameter out to record on the current channel) and the <code>-d</code> (or <code>--duration</code>) parameter should allow us to specify a duration to record for (in hh:mm:ss format).
</p>
<p>
Let&#8217;s get coding! First of all, we need a clean, simple way to parse input (paths, switches) from the command line. We&#8217;ll use the bundled <code>optparse</code> module because it is both flexible and fast. If you haven&#8217;t checked out <a href="http://docs.python.org/library/optparse.html"><code>optparse</code></a> yet, I <a href="http://blog.doughellmann.com/2007/08/pymotw-optparse.html">highly recommend it</a>. We&#8217;ll also be using the <a href="http://docs.python.org/library/sys.html"><code>sys</code></a>, <a href="http://docs.python.org/library/os.html"><code>os</code></a>, <a href="http://docs.python.org/library/time.html"><code>time</code></a>, and <a href="http://blog.kenkeiter.com/projects/pyavc/api/"><code>avc</code></a> modules. It&#8217;s like a module party &#8212; and you&#8217;re invited!</p>
<pre name="code" class="python">
#!/usr/bin/python

import sys
import os
import time
import optparse
import avc

def parse_duration_to_seconds(d):
    """
    Parse a human-readable duration in the format hh:mm:ss
    to seconds. Accepts: hh:mm:ss, mm:ss, ss
    """
    particles = [int(p) for p in d.split(':')]
    particles.reverse()
    if len(particles) > 3:
        raise ValueError('Too many colons in duration.')
    return sum([p * pow(60, i) for i, p in enumerate(particles)])

def main():
    # parse command line options
    parser = optparse.OptionParser()
    parser.add_option('-o', '--output', dest="output_path", default="~/Desktop/video.m2t")
    parser.add_option('-c', '--channel', dest="channel", default=0, type="int")
    parser.add_option('-d', '--duration', dest="duration", default="00:01:00")
    options, remainder = parser.parse_args() # parse command line arguments

    try:
        # initialize the capture interface and select the first device
        interface = avc.AVCCaptureInterface()
        my_device = interface.devices[0]

        # if we defined a channel to record to (other than 0), tune to it
        if options.channel != 0:
            my_device.tune_channel(options.channel)

        # open an output file for recording and begin doing so
        fh = open(os.path.expanduser(options.output_path), 'wb')
        my_device.begin_recording(fh) # begin recording

        # loop while updating a status until we've recorded for the desired duration
        total_time = parse_duration_to_seconds(options.duration)
        elapsed_time = 0
        while 1:
            # we'll make ourselves a status line!
            sys.stdout.write('\rRecording Ch. %d - Elapsed: %ds, Remaining: %ds%s' % \
                (options.channel, elapsed_time, total_time - elapsed_time, ' ' * 8))
            sys.stdout.flush()
            time.sleep(1)
            elapsed_time += 1
            if(elapsed_time >= total_time):
                break

        # clean up
        fh.close()
        sys.stdout.write('\rRecording complete -> %s\n\n' % options.output_path)

    except avc.AVCCaptureException, e:
        print 'An error occurred:', e.message

if __name__ == '__main__':
    main() # if we're calling from the command line, then run it.
</pre>
<p>Now we have a pretty little script to start a recording! If you&#8217;ve got Unix&#8217;s <a href="http://www.macosxhints.com/article.php?story=20050921214411382">&#8216;at&#8217; command enabled</a> you can now schedule recordings by running something like this:</p>
<div class="terminal">
kenkeiter_mbp:~ kkeiter$ at 8:00pm today&lt;ENTER&gt;<br />
python dvr.py -o ~/Desktop/Lost_s5e03_HD1080i.m2t -c 702 -d 1:00:00<br />
&lt;CTRL+D&gt;
</div>
</p>
<h2>It&#8217;s all a matter of expansion and contraction&#8230;</h2>
<p>Following the theme of movie-based lessons, we learn from <a href="http://www.imdb.com/title/tt0253798/">Out Cold</a>&#8217;s flawlessly executed hot-tub/genital -separation that it&#8217;s all &#8220;a matter of expansion and contraction&#8221;. We also learn that the Eskimos have nine different words for &#8220;hell mooch stuck in a hot tub&#8221;.
</p>
<p>
As you use PyAVC, you&#8217;ll discover that the files it outputs are gigantic. Two hours of 1080i MPEG2 TS-encapsulated video is around 12 GB. Therefore, you&#8217;ll most likely want to transcode your video! The versatile <a href="http://www.videolan.org/vlc/">VLC Media Player</a> has wonderful transcoding facilities, and a helpful community. Assuming you have <a href="http://www.videolan.org/vlc/download-macosx.html">VLC Player</a> installed, you can use a command like this:</p>
<div class="terminal">
kenkeiter_mbp:~ kkeiter$ /Applications/VLC.app/Contents/MacOS/clivlc -I dummy -v &#8220;/path/to/mpeg2tsvideo.m2t&#8221; :sout=&#8221;#transcode{vcodec=mp4v,\<br />
vb=4096,acodec=mp4a,ab=128,scale=1,channels=2,deinterlace,audio-sync}:standard{access=file,mux=mp4,\<br />
url=/mp4/output/path.mp4}&#8221; vlc:quit
</div>
<p>to transcode your video into a more comfortable MPEG4 format.</p>
<h2>That&#8217;s great. Give me a goddamn GUI.</h2>
<p>
I&#8217;ve been working on a FrontRow DVR appliance for this for a while and am almost satisfied with what I&#8217;ve come up with &#8212; however I&#8217;m not yet prepared to release it (needs to be refactored before I&#8217;ll put my name on it). In the meantime, you&#8217;ll be happy to know that functionality in PyAVC is implemented in a minimally-blocking way, and is natively threaded. This means that you can do any number of things including: using Django to build a guide-viewer + scheduler + kitchen sink, or using something like wxpython or PyObjC to build a Cocoa GUI.
</p>
<h2>Ideas and Resources for Improvement</h2>
<p>
To get you started with building a EPG (Electronic Program Guide) DVR, you should check out <a href="http://tmsdatadirect.com/docs/tv/">TMSDataDirect</a>. They have an awesome SOAP API that you can access for a nominal fee (I think I paid around $20 for the year). The data is provided in XTVD format (XML based), and (of course) there&#8217;s a Python package called <a href="http://www.timeheart.net/xtvd-tools/">xdtv-tools</a> to parse it.
</p>
<p>
Also, keep in mind that if you have multiple cable boxes you can record multiple streams simultaneously! Devices are accessed individually and the only limitation is Firewire bandwidth.
</p>
<h2>Disclaimers</h2>
<p>
I&#8217;m 99.99% sure that none of this will jack up your machine &#8212; that said, this is alpha software, so use it at your own risk. Also, if any part of this article was offensive to you, please bury your head in the sand and don&#8217;t visit my site. A lot of what I write is offensive, but it&#8217;s always meant in good fun! If you can&#8217;t understand that, you don&#8217;t belong here.
</p>
<h2>Special Thanks</h2>
<p>
The illustration for this article was created by the exceedingly dynamic <a href="http://jeffpatterson.tv">Jeff Patterson</a>. Look for a new illustration from a different artist for every major post.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.kenkeiter.com/2009/01/29/write-your-own-dvr-with-pyavc/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
