<?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>Mike Knoop</title>
	<atom:link href="http://mikeknoop.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://mikeknoop.com/blog</link>
	<description>Founder, Web Developer, and Mechanical Engineer</description>
	<lastBuildDate>Fri, 12 Apr 2013 00:25:23 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>jQuery 1.9.1 and Yuglify / UglifyJS</title>
		<link>http://mikeknoop.com/blog/jquery-1-9-1-and-yuglify-uglifyjs/</link>
		<comments>http://mikeknoop.com/blog/jquery-1-9-1-and-yuglify-uglifyjs/#comments</comments>
		<pubDate>Sat, 06 Apr 2013 07:25:05 +0000</pubDate>
		<dc:creator>mikeknoop</dc:creator>
				<category><![CDATA[All Posts]]></category>
		<category><![CDATA[Hacking]]></category>

		<guid isPermaLink="false">http://mikeknoop.com/blog/?p=258</guid>
		<description><![CDATA[Here is a fun bug we ran into in production today at Zapier. As most large scale sites do, we have an asset pipeline. This asset pipeline takes our static assets (compiled Javascript, compiled CSS, and frontend HTML templates), minifies &#8230; <a href="http://mikeknoop.com/blog/jquery-1-9-1-and-yuglify-uglifyjs/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Here is a <del>fun</del> bug we ran into in production today at <a href="https://zapier.com/z/C2/">Zapier</a>. As most large scale sites do, we have an asset pipeline. This asset pipeline takes our static assets (compiled Javascript, compiled CSS, and frontend HTML templates), minifies them, combines them all together into a few files, then ships them up to Amazon S3.</p>
<p>We received a report that the site wasn&#8217;t &#8220;loading&#8221; on Firefox. Since we use Backbone for most of our internal application pages, that means the user was stuck on our fallback loading GIF animation and has run into a Javascript runtime error on page load. Again, Firefox only on this bug (Chrome and IE were strangely fine). Here is the exception I saw thrown for every AJAX request that was fired off:</p>
<p><code>o is not a function</code></p>
<p>Keep in mind this is minified code so <code>o</code> might vary depending on what <a href="https://github.com/mishoo/UglifyJS">UglifyJS</a> decided to use as a replacement variable. This error occurred whether I ran production jQuery 1.9.1 (minified already) or development jQuery 1.9.1 (unminified) through our asset pipeline.</p>
<p>I was able to narrow down the cause to some default setting in <a href="https://github.com/yui/yuglify">Yuglify</a> (a lightweight wrapper around <a href="https://github.com/mishoo/UglifyJS">UglifyJS 1.x</a>). I bumped our UglifyJS version to the latest 2.x series but discovered that Yuglify was incompatible with this series. The final result was to remove Yuglify completely and tell Django Pipeline to use <a href="https://github.com/mishoo/UglifyJS2">UglifyJS 2.x</a> and CSSMIN directly. Here is what my final settings.py file looked like:</p>
<pre><code>##################################
#### FRONTEND DJANGO PIPELINE ####
##################################

# npm install -g cssmin
PIPELINE_CSS_COMPRESSOR = 'pipeline.compressors.cssmin.CSSMinCompressor'
PIPELINE_CSSMIN_BINARY = '/usr/bin/env cssmin'
PIPELINE_CSSMIN_ARGUMENTS = ''

# npm install -g uglify-js
PIPELINE_JS_COMPRESSOR = 'pipeline.compressors.uglifyjs.UglifyJSCompressor'
PIPELINE_UGLIFYJS_BINARY = '/usr/bin/env uglifyjs'
PIPELINE_UGLIFYJS_ARGUMENTS = ''
</code></pre>
<p>And viola! Once all our Javascript assets ran through Uglify 2.x directly, we didn&#8217;t see any problems. I suspect this is because of either:</p>
<ol>
<li>a better internal compression scheme in the UglifyJS 2.x series or</li>
<li>UglifyJS 2.x is no longer minifying variable names given the default settings.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://mikeknoop.com/blog/jquery-1-9-1-and-yuglify-uglifyjs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A Year in Frontend Development at Zapier</title>
		<link>http://mikeknoop.com/blog/a-year-in-frontend-dev/</link>
		<comments>http://mikeknoop.com/blog/a-year-in-frontend-dev/#comments</comments>
		<pubDate>Wed, 20 Mar 2013 08:29:53 +0000</pubDate>
		<dc:creator>mikeknoop</dc:creator>
				<category><![CDATA[All Posts]]></category>

		<guid isPermaLink="false">http://mikeknoop.com/blog/?p=183</guid>
		<description><![CDATA[Prior to October 2011, I hadn&#8217;t written more than a handful of lines of Javascript. I had done my fair share of HTML and CSS work, but always to support some PHP-driven backend engine. Early on, my co-founder Bryan Helmig &#8230; <a href="http://mikeknoop.com/blog/a-year-in-frontend-dev/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Prior to October 2011, I hadn&#8217;t written more than a handful of lines of Javascript. I had done my fair share of HTML and CSS work, but always to support some PHP-driven backend engine. Early on, my co-founder Bryan Helmig and I decided to split <a href="https://zapier.com/">Zapier</a> into two parts: the backend and the frontend, which would communicate over an internal JSON REST API. Given the complex nature of <a href="https://zapier.com/">Zapier</a>, it seemed elegant to architect the service this way. Bryan focused on writing our backend framework in Django and Python and I decided to foray into some exploratory frontend tech.</p>
<p>The frontend stack which <a href="https://zapier.com/">Zapier</a> ran on for twelve months comprised of <a href="http://coffeescript.org/">Coffeescript</a>, <a href="http://backbonejs.org/">Backbone</a>, <a href="http://requirejs.org/">RequireJS</a>, <a href="http://mustache.github.com/">Mustache</a>, and <a href="http://sass-lang.com/">Sass</a>.</p>
<p>The decision to use this stack was a bit arbitrary. Coffeescript and Backbone were really hot and I thought they&#8217;d be worth learning. Other pieces came out of suggestions and early research. The initial demo for Startup Weekend was written in pure Javascript and CSS so the transition to these relatively new pieces of tech had immediate tangible benefits for my sanity.</p>
<p>Some of those decisions were good, some bad. Let&#8217;s take a look at each piece&#8230;</p>
<hr />
<h1><a href="http://coffeescript.org/">Coffeescript</a></h1>
<p>Coffeescript is a little language that compiles into Javascript. It adds syntactic sugar and new grammars to writing pure Javascript. I would recommend it immediately to anyone who has done more than a weekend&#8217;s work in pure Javascript. A common theme I&#8217;ve heard is &#8220;Begin with Javascript, and once you fully understand it, you can start using Coffeescript&#8221;. I disagree with this. I think even relative beginners can reap the rewards of Coffeescript (I certainly did). You don&#8217;t need to be a &#8220;rockstar ninja&#8221; to benefit from simpler syntax and fewer gotchas. Two things I would miss if I didn&#8217;t have Coffeescript:</p>
<h2>1. The existential operator</h2>
<pre><code>?
</code></pre>
<p>The existential operator is a shorthand syntax you can use to absorb and test for <code>null</code> or <code>undefined</code> values. Typing is Javascript is very loose and it is a common pitfall to incorrectly assume a certain falsy type (like an empty string or zero). On top of that, the operator enables you to avoid undefined TypeErrors, like this one:</p>
<pre><code>&gt;&gt; blog = {}
&gt;&gt; alert(blog.author.name)
TypeError: Cannot read property 'author' of undefined
</code></pre>
<p>Using Coffeescript we can write it like this, to absorb the null value and avoid a runtime error:</p>
<pre><code>&gt;&gt; blog = {}
&gt;&gt; alert(blog.author?.name)
undefined
</code></pre>
<p>The operator also enables you to quickly and correctly test for existence of keys in objects:</p>
<pre><code>&gt;&gt; blog = {author: 'Mike Knoop'}
&gt;&gt; alert(blog.author?)
true
&gt;&gt; alert(blog.posts?)
false
</code></pre>
<p>There are <strong>1130 instances</strong> of the existential operator in our Coffeescript codebase at <a href="https://zapier.com/">Zapier</a> which is a testament to how useful one character can be.</p>
<h2>2. Lower effort for well-styled code</h2>
<p>My other favorite part of Coffeescript isn&#8217;t a single feature. But rather the style it enables you to write in. Here&#8217;s a method I pulled from our frontend codebase:</p>
<p><script src="https://gist.github.com/mikeknoop/5184343.js"></script></p>
<p>&#8230; and the corresponding Javascript:</p>
<p><script src="https://gist.github.com/mikeknoop/5184929.js"></script></p>
<p>To me, there is a lot of mental overhead to parsing pure Javascript compared to Coffeescript. I&#8217;ll accept that one can write clean, readable Javascript (just go check out the <a href="http://backbonejs.org/docs/backbone.html">Backbone.js source code</a>) but you have to put in a lot more effort to do so. At minimum, there is a larger learning curve to writing well-styled Javascript over well-styled Coffeescript. For a startup product in active development, the efficiency and speed versus maintainability tradeoff is worth a lot.</p>
<hr />
<h1><a href="http://backbonejs.org/">Backbone</a></h1>
<p>Backbone is a frontend &#8220;MVC&#8221; organization framework. If you&#8217;re familiar with Python web frameworks, Backbone is like <a href="http://flask.pocoo.org/">Flask</a>, not Django. The spirit of the framework is similar. You&#8217;ll be rolling your own patterns and might even be overriding built in behavior for large projects. Because of this, Backbone tends to have a shallow learning but steep mastery curve. In my opinion, the mastery curve is steeper than than other frontend frameworks like Ember.js and Angular.js who impose more strict conventions (and thus, less options, less trial and error). It&#8217;s incredibly easy to whip up a quick Backbone-driven app or site but even easier to fall into common traps as you grow and scale.</p>
<p>Decent patterns are the hardest things to pick up when learning to write sane Backbone-driven applications. One year after implementing Backbone in production at <a href="https://zapier.com/">Zapier</a>, I re-wrote our entire frontend to avoid some common pitfalls and implement several patterns I learned over the previous twelve months. Even so, I am surprised how stable our site was during that one year learning period &#8212; a testament that &#8220;okay&#8221; Backbone apps can be written even if you have no idea what you&#8217;re doing.</p>
<p>Here are a few takeaways from my experience writing and designing the Zapier frontend and subsequent re-write to fix all my mistakes:</p>
<h2>1. Do you like creating conventions? You&#8217;ll love Backbone</h2>
<p>Part of the challenge of Backbone (for me) is that hardly anything is strictly imposed. Views are left entirely up to the implmentor. The bootstrapping process is flexible. Model storages can by interchanged on the fly. I mentioned it above, but if you like <a href="http://flask.pocoo.org/">Flask</a> (python) or <a href="http://www.sinatrarb.com/">Sinatra</a> (ruby), I suspect you&#8217;ll like Backbone. This is entirely up to taste and preference. I like the allowed creativity, but others prefer more guidelines.</p>
<p>The other consideration is team size. I am a single maintainer so I have the flexibility at this point to throw away patterns and re-implement them. This much harder to do with multiple contributors.</p>
<h2>2. Don&#8217;t create crazy inheritance layers on top of Backbone</h2>
<p>It is tempting to do this and it bit me pretty hard. You could probably also go overboard with compositions, but I think it&#8217;s harder to do so. I originally used several layers of models and collections to do things like:</p>
<ul>
<li>Adding pagination to our collections</li>
<li>Interfacing with special RPC API endpoints</li>
<li>Creating singleton models and collections</li>
</ul>
<p>In most cases it was a better decision to break the pieces into their own models or collections and composite the final object together. A nice pattern for doing this is Underscore&#8217;s <code>extend</code> method:</p>
<p><script src="https://gist.github.com/mikeknoop/5185280.js"></script></p>
<p>I still use inheritance for a few things and it has it&#8217;s place. I find myself preferring composition these days, though, mostly to avoid subtle ordering bugs around calling <code>super</code> that seem to creep in when you use inheritance heavily.</p>
<h2>3. Use many controllers and make them more than just dumb entry points</h2>
<p>Our original app had a single controller. It contained every frontend route and was pretty much a dumb pipe between a route and instantiating a top level view, nothing more. It looked very much like a <code>urls.py</code> from Django-land.</p>
<p>The downside to this approach for a larger mutli-page application was that I started to have many of these specialized top level views. More and more &#8220;loading&#8221; logic wound up in these specialized views and began to leak downward into child views. The concept of a &#8220;view&#8221; started bleed together and it was hard to keep everything straight.</p>
<p>Now there exists a separate controller for each section of our application. A single controller handles the model initialization and view rendering for its section of the app. This is extremely clean compared to what I was doing and enabled me to implement a really lightweight convention (read: copy-paste template) for new controllers that wanted to ensure models existed before the rest of the page rendered.</p>
<h2>4. Zombie-views, Race conditions, Event-callback hell (and more to come)</h2>
<p>Dedicated posts on these topics are in my future. Keep an eye on <a href="http://mikeknoop.com/blog/feed/">my RSS feed</a> for more.</p>
<hr />
<h1><a href="http://sass-lang.com/">Sass</a></h1>
<p><a href="http://sass-lang.com/">Sass</a> is an intermediary language for CSS which lets your write CSS using nested rules, mixins, math, and variables. I have really nothing to complain about with Sass. You should use it (or a similar CSS pre-parser).</p>
<p>One cool trick I discovered pretty late into my learning curve is that you can <code>@extend</code> existing CSS classes into other CSS classes. It looks like this:</p>
<p><script src="https://gist.github.com/mikeknoop/5185406.js"></script></p>
<hr />
<h1><a href="http://requirejs.org/">RequireJS</a> (&amp; AMD)</h1>
<p>This one is pretty negative. Let me start with what is good. RequireJS has really solid documentation. The maintainers are active and willing to help. Many popular libraries include native AMD support now and there is a fallback shim you can implement directly with RequireJS if AMD support isn&#8217;t available in your favorite library.</p>
<p>Now, the bad. Put it simply: RequireJS is <strong>way too complex</strong>. The promise of RequireJS (and by extension the AMD module format) is that it will improve the speed and quality of your code. After twelve months with RequireJS, I can say this was not the case.</p>
<p>I estimate I put in between fifty and one hundred hours learning, using, debugging, deploying, and optimizing RequireJS over the course of one year. I seriously tried to make it work. <a href="https://github.com/zapier/coffee-script">I even hacked the Coffeescript compiler to add a nice syntax layer for RequireJS</a>. But there are so many drawbacks to using it and no gains over alternatives. Some of the reasons why I no longer use it:</p>
<ul>
<li>My Backbone view files were, on average, ~400 characters and 7 lines longer</li>
<li>Every file had a useless extra leading indentation</li>
<li>You&#8217;re still boiling everything down to a single file for production, we didn&#8217;t see any performance gains</li>
<li>Refactoring is harder. You have to change paths for every affected RequireJS dependency in every affected file</li>
<li>Does this look fun to maintain for <strong>every single modular file you have</strong>?</li>
</ul>
<p><script src="https://gist.github.com/mikeknoop/5185478.js"></script></p>
<p>Modularization is a great idea. Backbone encourages this format and it&#8217;s a huge organizational win to split all your files up. The real problem is I couldn&#8217;t obtain any tangible benefits after using it for twelve months in production. We&#8217;re now using a super-simple asset pipeline (<a href="https://github.com/cyberdelia/django-pipeline">Django Pipeline</a>; look for Jammit if you&#8217;re on Rails) and by comparison, I can&#8217;t believe how much effort I put in to make RequireJS work to achieve practically the same thing (frontend asset compilation, minification, and deployment).</p>
<p>If a reasonably smart fellow can&#8217;t figure out any benefit from your software after a year, something is broken. If you&#8217;re smart enough to figure out and master RequireJS while keeping your sanity &#8212; all the power to you. I will happily admit that I was not smart enough to do so.</p>
<hr />
<h1><a href="http://sass-lang.com/">Mustache</a> and <a href="http://handlebarsjs.com/">Handlebars</a></h1>
<p>Some members of the community take a passionate stance against Handlebars. I have used both in production and don&#8217;t really have a preference between the two. Here&#8217;s the comparison:</p>
<h2><a href="http://sass-lang.com/">Mustache</a></h2>
<p>A simple logic-less templating language. It has plugins available for practically every programming language and is really easy to learn. Mustache-style templating is so simple and flexible it can power both our frontend HTML templates and our live Zap previews! The syntax is so simple I didn&#8217;t really pick up any extra tricks in production beyond just reading the documentation and implementing it. And I think that is a powerful statement. It really is as simple as it sounds.</p>
<h2><a href="http://handlebarsjs.com/">Handlebars</a></h2>
<p>Handlebars is almost a drop-in replacement for Mustache (you&#8217;ll have to replace your if/else/then blocks). The big thing Handlebars gives you over Mustache is the ability to implement custom helpers. Normally in Mustache if you needed to implement some special rendering logic, you would pass in a Javascript function which performed the logic and returned a string. This is similar to Handlebars custom helpers, except custom helpers can be re-used globally.</p>
<p>In Handlebars, you can register a custom helper method like this:</p>
<p><script src="https://gist.github.com/mikeknoop/5185647.js"></script></p>
<p>And you can then use it in your template file like this:</p>
<p><script src="https://gist.github.com/mikeknoop/5185657.js"></script></p>
<p>The gotcha with Handlebars is you&#8217;re tempted to re-implement Jinja2-style template yourself. I recommend not doing that, keep it simple. Use it only for repeatable helpers you find yourself needing across template files. Another trick is deciding whether to pass the real Backbone object into the template or pass a JSON representation of the object.</p>
<p>I tend to prefer the JSON representation but there are certain helpers I&#8217;ve written that expect Backbone objects so they can call methods on them. To compromise, you could pass in the raw Backbone object and access the JSON as <code>myObject.attributes</code> inside the template.</p>
<p>The jury is still out for me on Mustache versus Handlebars.</p>
<hr />
<h1>Final thoughts</h1>
<p>I&#8217;ll likely deep dive into many of the above topics in further blog posts so <a href="http://mikeknoop.com/blog/feed/">stay tuned to my RSS</a> for more on that front. I am really happy with where we&#8217;re at today, frontend tech wise, at <a href="https://zapier.com/">Zapier</a>. We have a really solid foundation for growing and building the interface going forward. There is still a lot to learn, of course. I&#8217;d love to discuss anything mentioned or alluded to in this post. Drop me a line directly or start up a comment thread below.</p>
]]></content:encoded>
			<wfw:commentRss>http://mikeknoop.com/blog/a-year-in-frontend-dev/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>#FF4A00</title>
		<link>http://mikeknoop.com/blog/ff4a00/</link>
		<comments>http://mikeknoop.com/blog/ff4a00/#comments</comments>
		<pubDate>Mon, 11 Mar 2013 11:20:23 +0000</pubDate>
		<dc:creator>mikeknoop</dc:creator>
				<category><![CDATA[All Posts]]></category>
		<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://mikeknoop.com/blog/?p=146</guid>
		<description><![CDATA[Wade, Bryan, Dan and I just sat down at Museao in Columbia, Missouri, fall 2011. The room is a large storage garage, distinct from the rest of the building through its unfinished concrete floor and painted cinder-block walls. We&#8217;re looking &#8230; <a href="http://mikeknoop.com/blog/ff4a00/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Wade, Bryan, Dan and I just sat down at <a href="http://www.museao.com">Museao</a> in Columbia, Missouri, fall 2011. The room is a large storage garage, distinct from the rest of the building through its unfinished concrete floor and painted cinder-block walls. We&#8217;re looking for power strips, beer, and a whiteboard &#8212; gearing up for the next forty-eight hours at <a href="http://columbia.startupweekend.org/">Columbia&#8217;s inaugural Startup Weekend</a>.</p>
<p><img src="http://mikeknoop.com/blog/wp-content/uploads/2013/03/Z26xG.png" alt="Table in garage at Startup Weekend" /></p>
<p>&#8220;Well, we&#8217;re gonna need to pick a better name.&#8221;</p>
<p>At this point &#8220;API Mixer&#8221; is little more than <a href="http://mikeknoop.com/blog/wp-content/uploads/2013/03/c29186faff891284ba8bf9075af0b738.png">a chat conversation</a> and two-minute pitch.</p>
<p>&#8220;I agree. Let&#8217;s see, things that are fast, snappy&#8230; How about Snapier?&#8221;</p>
<p>&#8220;It even has &#8216;API&#8217; in it!&#8221;</p>
<p>And so it was, <a href="http://techcrunch.com/2013/01/26/resolving-co-founder-disputes/">for the next four months at least</a>. When your startup is less than an hour old it&#8217;s easy to get distracted with decisions that seem like they&#8217;ll make or break your your company. In retrospect I&#8217;ve learned that you grow into the early decisions you&#8217;ve made. I&#8217;ve also learned not to sweat any single decision. There are thousands of them ahead of you.</p>
<p>Over the next two days we&#8217;d go on to create a landing page (&#8220;should we make our own or prefab?&#8221;), collect a few dozen email addresses (&#8220;how often should we email them?&#8221;), and build a working drag and drop demo that could connect two SaaS APIs live on stage (&#8220;which apps should pick? what will the demo UI look like?&#8221; &#8230; )</p>
<p>All this would kick start our journey onto a beta product <a href="http://mikeknoop.com/blog/wp-content/uploads/2013/03/GKWpV.png">with real paying customers</a>. From there we&#8217;d apply (twice) and attend the <a href="http://mikeknoop.com/blog/wp-content/uploads/2013/03/meUhU.png">Y Combinator Summer 2012 class</a>, <a href="https://zapier.com/blog/2012/06/20/zippity-zappity-zapier-launches-publicly/">launch a public product in June</a>, and grow to a team of six by March 2013.</p>
<p>What was then Snapier, <a href="https://zapier.com">now Zapier</a>, is nothing but an accumulation of all those past decisions come to fruition. Call it your company culture, if you will. When I look back at a few of our decisions, some feel like they were made on auto-pilot. Yet, I know  each and every one had its own context, points, and counter-points. I look back fondly at the early days with eyes wide open &#8212; and look forward with them open even wider, hoping that the culmination of all the decisions I make tomorrow will equal a sum greater than it&#8217;s parts for the next day, and the next.</p>
<p>But before any of that, we&#8217;re all back at the table in Columbia, Missouri and we have one more small decision to make before kicking off our sprint.</p>
<p>&#8220;What color should we pick?&#8221;</p>
<p>&#8220;Let&#8217;s just pick one randomly and go with it.&#8221;</p>
<p>&#8220;How about&#8230; #FF4A00?&#8221;</p>
<p>&#8220;Done.&#8221;</p>
<p><a href="https://zapier.com/z/C2/">
<p><img src="http://mikeknoop.com/blog/wp-content/uploads/2013/03/white.png" alt="Zapier Logo #FF4A00" /></p>
<p></a></p>
]]></content:encoded>
			<wfw:commentRss>http://mikeknoop.com/blog/ff4a00/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Dusting off your Blog? Don&#8217;t Forget About Easy SEO Wins</title>
		<link>http://mikeknoop.com/blog/dont-forget-about-easy-seo-wins/</link>
		<comments>http://mikeknoop.com/blog/dont-forget-about-easy-seo-wins/#comments</comments>
		<pubDate>Thu, 07 Mar 2013 05:40:43 +0000</pubDate>
		<dc:creator>mikeknoop</dc:creator>
				<category><![CDATA[All Posts]]></category>

		<guid isPermaLink="false">http://mikeknoop.com/blog/?p=127</guid>
		<description><![CDATA[Even if your blog only gets 10 hits a day, there are plenty of reasons to keep it in good condition, search wise, if you&#8217;d like to see your traffic grow. We&#8217;re talking about making your site or blog more &#8230; <a href="http://mikeknoop.com/blog/dont-forget-about-easy-seo-wins/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Even if your blog only gets 10 hits a day, there are plenty of reasons to keep it in good condition, search wise, if you&#8217;d like to see your traffic grow. We&#8217;re talking about making your site or blog more visible to searches engines like Google and co.</p>
<h2>1. HTML Titles and Descriptions</h2>
<p>Generally, hosted blogs (like wordpress.com and tumblr.com) do a pretty good job about this (<a href="http://wadefoster.net/post/43633476838/three-quick-wins-for-boosting-tumblr-search-traffic">not always, though</a>). If you&#8217;re throwing together your own blog from scratch or hosting your own blogging software, it&#8217;s worth double checking you got your HTML header title and description tags correct.</p>
<p>Generally, it&#8217;s best to have the title and description match the content of the page and vary from page to page. Search engines are more likely to show it than the same content that never changes. The title should be short, less than 10 words whereas a good description reads more like a sentence.</p>
<p>In blog terms, the title can match the title of the post (with some prefix or suffix describing the blog) and the description can match an excerpt of the post. Check out the example I&#8217;m running on this blog below.</p>
<p><script src="https://gist.github.com/mikeknoop/5105427.js"></script></p>
<h2>2. Google Webmaster Tools</h2>
<p>This is a <a href="https://www.google.com/webmasters/tools/home?hl=en">must-use tool</a> (from Google itself!) if you&#8217;re rolling your own site. It&#8217;ll verify you as the owner of the site, suggest ways to improve it for speed and search, and even email you when there are outstanding problems (like available WordPress updates).</p>
<p>Google Webmaster Tools are also really useful when migrating from one domain to another, such as going from a hosted blog to your own domain.</p>
<h2>3. Google Authorship</h2>
<p>A new trend that is growing in popularity is linking your content to your Google+ profile through Authorship. This is a simple way to tell Google who is writing what content.</p>
<p><a href="https://plus.google.com/authorship">You can get started here</a>.</p>
<p>You&#8217;ll need to verify and link your Google+ profile to your site via an anchor tag:</p>
<pre><code>&lt;a href="https://plus.google.com/u/1/{{id}}?rel=author"&gt;Google+&lt;/a&gt;
</code></pre>
<p>And be sure to replace <strong>{{id}}</strong> with your own Google+ ID from your profile. Pretty soon you&#8217;ll start to see your blog posts appearing in Google results like this:</p>
<p><img src="http://mikeknoop.com/blog/wp-content/uploads/2013/03/ucrLRQT.png" alt="enter image description here" /></p>
<h2>4. Share Liberally on Social Media</h2>
<p>After putting all the effort into writing great content, don&#8217;t forget to share it! Not only will it help grow your audience (through re-shares) but you&#8217;ll also feel more motivated with inbound feedback.</p>
<p>I&#8217;ll thank my co-founder, Wade, for <a href="http://wadefoster.net/post/43633476838/three-quick-wins-for-boosting-tumblr-search-traffic">reminding me</a> about some of these dead-simple todos.</p>
]]></content:encoded>
			<wfw:commentRss>http://mikeknoop.com/blog/dont-forget-about-easy-seo-wins/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Simple Hacker News Blog Comments</title>
		<link>http://mikeknoop.com/blog/simple-hacker-news-blog-comments/</link>
		<comments>http://mikeknoop.com/blog/simple-hacker-news-blog-comments/#comments</comments>
		<pubDate>Wed, 06 Mar 2013 05:14:22 +0000</pubDate>
		<dc:creator>mikeknoop</dc:creator>
				<category><![CDATA[All Posts]]></category>
		<category><![CDATA[Hacking]]></category>

		<guid isPermaLink="false">http://mikeknoop.com/blog/?p=111</guid>
		<description><![CDATA[Lately I thought my old Disqus comment blocks were getting tacky since they took up a good chunk of UI and were mostly empty. I decided to outsource my commenting to Hacker News to let the discussion progress there since &#8230; <a href="http://mikeknoop.com/blog/simple-hacker-news-blog-comments/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Lately I thought my old Disqus comment blocks were getting tacky since they took up a good chunk of UI and were mostly empty. I decided to outsource my commenting to Hacker News to let the discussion progress there since that&#8217;s a community I am actively involved in.</p>
<p>There <a href="https://github.com/igrigorik/hackernews-button">are a</a> <a href="http://hnlike.com/">few Hacker News</a> <a href="http://www.saicharan.in/blog/2009/07/12/hn-submit-button/">&#8220;like&#8221; buttons</a> out there but they all lose one critical element for small blogs: notifications. Most commenting systems will notify you when a new comment occurs but we lose that moving to a primitive (by comparison) site like Hacker News.</p>
<p>So how to address this? With a little bit of rigging using <a href="http://webscript.io">Webscript.io</a> and <a href="https://zapier.com">Zapier</a>, of course! The end result is a simple textual call to action at the bottom of each post which submits the story to Hacker News and sends me an email notification each time the link is clicked &#8212; so that I can follow up comment to anyone in my admittedly small audience.</p>
<p>The comment call-to-action is a link with an anchor tag that looks like this:</p>
<p><code>http://zapier.webscript.io/click?next=http%3A%2F%2Fnews.ycombinator.com%2Fsubmitlink%3Fu%3Dhttp%253A%252F%252Fmikeknoop.com%252Fblog%252Fprlibs-a-columbia-mo-startup-weekend-project%252F%26t%3DPRLibs%252C%2Ba%2BColumbia%2BMO%2BStartup%2BWeekend%2BProject&#038;zid=54Xj</code></p>
<p>Let&#8217;s dissect what is going on when the user clicks the above link.</p>
<p>1. The user clicks the link and sends a request to our Webscript hosted on <a href="webscript.io">webscript.io</a>.</p>
<p>2. The link includes a `next` URL (the Hacker News-specific submit link) and a `zid` which I talk more about below.</p>
<p>3. The webscript will send a webhook POST request to <a href="https://zapier.com">Zapier </a>to trigger a Zap. My triggered Zap sends me an Gmail notification.</p>
<p>4. The webscript will tell the user&#8217;s browser to redirect to the `next` URL via a `302` HTTP redirect.</p>
<p>You can use my Zap template which <a href="https://zapier.com/zapbook/webhook/gmail/7702/blog-hn-comment-link-click-notifications/">I&#8217;ve created here</a>. In step 2, take note of the last few characters in your webhook URL.</p>
<p><a href="http://mikeknoop.com/blog/wp-content/uploads/2013/03/6u8kcUv.png"><img src="http://mikeknoop.com/blog/wp-content/uploads/2013/03/6u8kcUv-300x103.png" alt="Zapier Step 2" width="300" height="103" class="alignleft size-medium wp-image-114" /></a></p>
<p>In my example above, the special hook identifier is <strong>5F3p</strong>. Since I am using WordPress, I can inject some PHP code into the main WordPress post loop after the content.</p>
<p>I also recommend setting up a filter on your Zap to exclude clicks from popular search engine bots, you can do this in Step 4 when setting up your Zap:</p>
<p><a href="http://mikeknoop.com/blog/wp-content/uploads/2013/03/GCYX5W2.png"><img src="http://mikeknoop.com/blog/wp-content/uploads/2013/03/GCYX5W2-300x72.png" alt="GCYX5W2" width="300" height="72" class="alignleft size-medium wp-image-124" /></a></p>
<p><script src="https://gist.github.com/mikeknoop/5096819.js"></script></p>
<p>Replace `$code` with your from above. If you&#8217;re not using PHP, you&#8217;ll need to follow the above pattern to properly URL encode each &#8220;layer&#8221; (there are two: one for the blog URL and title, and one for the Hacker News submit URL).</p>
<p>This will use a hosted webscript on a paid account so it won&#8217;t disappear in a few days. You&#8217;re free to use our webscript as it doesn&#8217;t do any special. If you&#8217;d prefer to host it yourself, here is the Lua code it is running:</p>
<p><script src="https://gist.github.com/mikeknoop/5096877.js"></script></p>
<p>Once it is set up properly you&#8217;ll begin to receive email notifications when folks click through your comment links. You can see this live and in action at the bottom of the post!</p>
]]></content:encoded>
			<wfw:commentRss>http://mikeknoop.com/blog/simple-hacker-news-blog-comments/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>PRLibs, a Columbia MO Startup Weekend Project</title>
		<link>http://mikeknoop.com/blog/prlibs-a-columbia-mo-startup-weekend-project/</link>
		<comments>http://mikeknoop.com/blog/prlibs-a-columbia-mo-startup-weekend-project/#comments</comments>
		<pubDate>Tue, 02 Oct 2012 00:43:00 +0000</pubDate>
		<dc:creator>mikeknoop</dc:creator>
				<category><![CDATA[All Posts]]></category>
		<category><![CDATA[Current Events]]></category>
		<category><![CDATA[Hacking]]></category>
		<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://knoopgroup.com/blog/?p=98</guid>
		<description><![CDATA[I recently attended Columbia MO&#8217;s second annual Startup Weekend event. Recall that Zapier, the startup I co-founded, formed at the same event last year. Wade Foster and I decided we wanted to build something again just for fun. So we &#8230; <a href="http://mikeknoop.com/blog/prlibs-a-columbia-mo-startup-weekend-project/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>I recently attended Columbia MO&#8217;s second annual <a href="http://columbia.startupweekend.org/">Startup Weekend event</a>. Recall that <a href="https://zapier.com" title="Zapier, Sync your Web Apps">Zapier</a>, the startup I co-founded, formed at the same event last year.</p>
<p><a href="https://twitter.com/WadeFoster">Wade Foster</a> and I decided we wanted to build something again just for fun. So we came up with a gag startup idea, <a href="http://prlibs.com">PRLibs</a>, a madlib-style startup press release generator.</p>
<p>The final incarnation of the product lets you enter in a few details about your startup along with a URL. We will crawl the URL for words about your startup and attemtpt to fill-in (madlib stlye) a press release. The final release is then auto-posted it our <a href="http://prlibs.posterous.com">Posterous blog</a>.</p>
<p>I decided I would build this using a tech stack I had never used before. The backend is a <a href="http://flask.pocoo.org/">Flask </a> server, sitting behind <a href="http://nginx.org/">Nginx</a> reverse-proxied over <a href="http://projects.unbit.it/uwsgi/">uWSGI</a>. <a href="http://www.readncode.com/blog/Deploying-Flask-with-nginx-uWSGI-and-Supervisor/">This blog post</a> was amazing and got me a working &#8220;Hello World&#8221; on the first try! I was a little surprised how easy it was to glue all the pieces together.</p>
<p>On the product side, I used <a href="http://docs.python-requests.org/en/latest/">Python Requests</a>, <a href="http://lxml.de/">LXML</a>, and the <a href="http://nltk.org/">Python NLTK</a> to parse a user-submitted URL for words and tag them by part of speech. The words are then fed into pre-defined sentences to form the final press release.</p>
<p>We used a Zap, Webhook -> Gmail, via <a href="http://zapier.com">Zapier</a> to auto-post the final press release to our <a href="http://prlibs.posterous.com">Posterous </a>blog.</p>
<p>Oh, did I mention this entire project is <a href="https://github.com/mikeknoop/prlibs">open source and available on Github</a>?</p>
<p>In the end we came out of Startup Weekend with a functional and hilarious product (partly because the part of speech tagging system wasn&#8217;t very accurate&#8230;). So how&#8217;d we do? Here is our traffic graph from the last two days:</p>
<p><img alt="PRLibs Traffic Graph" src="http://i.imgur.com/f02lK.png" title="PRLibs Traffic Graph" style="max-width: 500px;"></p>
<p>Wade delivered the perfect gag-pitch on Sunday and really lifted the mood of the room after three hours of presentations. One judge gave us the title of &#8220;worst startup of all time&#8221;, in other words, we nailed it.</p>
]]></content:encoded>
			<wfw:commentRss>http://mikeknoop.com/blog/prlibs-a-columbia-mo-startup-weekend-project/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Android Live Webcam Wallpaper</title>
		<link>http://mikeknoop.com/blog/mizzou-webcams/</link>
		<comments>http://mikeknoop.com/blog/mizzou-webcams/#comments</comments>
		<pubDate>Mon, 26 Mar 2012 21:51:05 +0000</pubDate>
		<dc:creator>mikeknoop</dc:creator>
				<category><![CDATA[All Posts]]></category>

		<guid isPermaLink="false">http://knoopgroup.com/blog/?p=93</guid>
		<description><![CDATA[One cool app I recently found for my Android phone: WebLiveWallpaper which lets you set any webcam as the background live wallpaper. Scenery always makes a nice live wallpaper, and my university (Mizzou) has quite a few to choose from. &#8230; <a href="http://mikeknoop.com/blog/mizzou-webcams/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>One cool app I recently found for my Android phone: <a href="https://play.google.com/store/apps/details?id=com.dngames.websitelivewallpaper&#038;hl=en">WebLiveWallpaper </a>which lets you set any webcam as the background live wallpaper.</p>
<p>Scenery always makes a nice live wallpaper, and my university (Mizzou) has quite a few to choose from.</p>
<p>Here is a list (as of March 2012) of known webcams at the University of Missouri (Mizzou).</p>
<p><strong>Overlook of Student Center</strong><br />
[<a href="http://128.206.113.97/axis-cgi/jpg/image.cgi?resolution=640x480">Static Image</a>] [<a href="http://128.206.113.97/axis-cgi/mjpg/video.cgi?resolution=640x480">Video</a>]<br />
<img src="http://128.206.113.97/axis-cgi/jpg/image.cgi?resolution=640x480" alt="" /></p>
<p><strong>Inside Student Center</strong><br />
[<a href="http://128.206.113.106/axis-cgi/jpg/image.cgi?resolution=640x480">Static Image</a>] [<a href="http://128.206.113.106/axis-cgi/mjpg/video.cgi?resolution=640x480">Video</a>]<br />
<img src="http://128.206.113.106/axis-cgi/jpg/image.cgi?resolution=640x480" alt="" /></p>
<p><strong>MU Bookstore</strong><br />
[<a href="http://128.206.113.98/axis-cgi/jpg/image.cgi?resolution=640x480">Static Image</a>] [<a href="http://128.206.113.98/axis-cgi/mjpg/video.cgi?resolution=640x480">Video</a>]<br />
<img src="http://128.206.113.98/axis-cgi/jpg/image.cgi?resolution=640x480" alt="" /></p>
<p><strong>Stankowski Field</strong><br />
[<a href="http://128.206.113.99/axis-cgi/jpg/image.cgi?resolution=640x480">Static Image</a>] [<a href="http://128.206.113.99/axis-cgi/mjpg/video.cgi?resolution=640x480">Video</a>]<br />
<img src="http://128.206.113.99/axis-cgi/jpg/image.cgi?resolution=640x480" alt="" /></p>
<p><strong>Overlook of J-School</strong><br />
[<a href="http://128.206.143.98/axis-cgi/jpg/image.cgi?resolution=640x480">Static Image</a>] [<a href="http://128.206.143.98/axis-cgi/mjpg/video.cgi?resolution=640x480">Video</a>]<br />
<img src="http://128.206.143.98/axis-cgi/jpg/image.cgi?resolution=640x480" alt="" /></p>
]]></content:encoded>
			<wfw:commentRss>http://mikeknoop.com/blog/mizzou-webcams/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SPL (Nerf) Optimization (MAE Capstone Fall 2011)</title>
		<link>http://mikeknoop.com/blog/soft-projectile-launcher/</link>
		<comments>http://mikeknoop.com/blog/soft-projectile-launcher/#comments</comments>
		<pubDate>Tue, 13 Dec 2011 00:04:52 +0000</pubDate>
		<dc:creator>mikeknoop</dc:creator>
				<category><![CDATA[All Posts]]></category>

		<guid isPermaLink="false">http://knoopgroup.com/blog/?p=76</guid>
		<description><![CDATA[For our senior Mechanical Engineering capstone, Aaron Wagner and myself set out to investigate the potential positive flight characteristic effects of adding rotation to Nerf darts. You can check out all of our highspeed footage here. You can download our &#8230; <a href="http://mikeknoop.com/blog/soft-projectile-launcher/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>For our senior Mechanical Engineering capstone, Aaron Wagner and myself set out to investigate the potential positive flight characteristic effects of adding rotation to Nerf darts.</p>
<p><a href="http://knoopgroup.com/blog/wp-content/uploads/2011/12/finale.png"><img src="http://knoopgroup.com/blog/wp-content/uploads/2011/12/finale-300x238.png" alt="" title="Final Design" width="300" height="238" class="alignleft size-medium wp-image-87" /></a></p>
<p><a href="http://www.youtube.com/playlist?list=PL0FF1657C0B08FAB8">You can check out all of our highspeed footage here</a>.</p>
<p><a href="http://knoopgroup.com/upload/MAE4980CapstonePresentation.pptx">You can download our final presentation here</a>.</p>
<p><a href="http://knoopgroup.com/upload/MAE4980CapstoneReport.docx">Our final report can be downloaded here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://mikeknoop.com/blog/soft-projectile-launcher/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Manually Closing Individual jGrowl Notifications</title>
		<link>http://mikeknoop.com/blog/manually-closing-jgrowl-notifications/</link>
		<comments>http://mikeknoop.com/blog/manually-closing-jgrowl-notifications/#comments</comments>
		<pubDate>Wed, 07 Dec 2011 08:53:10 +0000</pubDate>
		<dc:creator>mikeknoop</dc:creator>
				<category><![CDATA[All Posts]]></category>
		<category><![CDATA[Hacking]]></category>

		<guid isPermaLink="false">http://knoopgroup.com/blog/?p=78</guid>
		<description><![CDATA[jGrowl is a nice little library to show simple JavaScript notifications. Unfortunately, by default, notifications can only be closed manually, on a timer, or never. There are a few solutions on StackOverflow to close all notifications but not individual notifications. &#8230; <a href="http://mikeknoop.com/blog/manually-closing-jgrowl-notifications/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><a href="http://stanlemon.net/24#options">jGrowl </a> is a nice little library to show simple JavaScript notifications. Unfortunately, by default, notifications can only be closed manually, on a timer, or never. There are a few solutions on <a href="http://stackoverflow.com/questions/2295769/how-to-close-a-jgrowl-manually">StackOverflow </a> to close all notifications but not individual notifications. For example, a &#8220;loading&#8230;&#8221; notification which closes when the action is complete.</p>
<p>Fortunately, there is a simple method to close individual notifications manually. Check out this CoffeeScript:</p>
<pre>
testGrowl = ->
 close = (e) ->
  console.log 'close'
  $(e).find('.jGrowl-close').trigger('click')
 ao = (e) ->
  setTimeout(
   -> close(e)
   1500)
 $.jGrowl("Hello world!", { sticky: true, afterOpen: ao })
 setTimeout testGrowl, 100
 setTimeout testGrowl, 1000
</pre>
<p>Or translating into pure JavaScript:</p>
<pre>    
testGrowl = function() {
 var ao, close;
 close = function(e) {
  console.log('close');
  return $(e).find('.jGrowl-close').trigger('click');
 };
 ao = function(e) {
   return setTimeout(function() {
   return close(e);
  }, 1500);
 };
 return $.jGrowl("Hello world!", {
   sticky: true,
   afterOpen: ao
  });
 };
setTimeout(testGrowl, 100);
setTimeout(testGrowl, 1000);
</pre></p>
]]></content:encoded>
			<wfw:commentRss>http://mikeknoop.com/blog/manually-closing-jgrowl-notifications/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Pioneer DVR Harddrive Recovery Tools</title>
		<link>http://mikeknoop.com/blog/pioneer-dvr-harddrive-recovery-tools/</link>
		<comments>http://mikeknoop.com/blog/pioneer-dvr-harddrive-recovery-tools/#comments</comments>
		<pubDate>Sat, 19 Nov 2011 10:53:52 +0000</pubDate>
		<dc:creator>mikeknoop</dc:creator>
				<category><![CDATA[Hacking]]></category>

		<guid isPermaLink="false">http://knoopgroup.com/blog/?p=71</guid>
		<description><![CDATA[If you&#8217;ve found this post then you&#8217;re likely already aware of the failure-prone nature of Pioneer DVR systems. There are many forums discussing how to improve stability but, again, if you&#8217;ve found this post then it is likely already too &#8230; <a href="http://mikeknoop.com/blog/pioneer-dvr-harddrive-recovery-tools/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>If you&#8217;ve found this post then you&#8217;re likely already aware of the failure-prone nature of Pioneer DVR systems. There are many forums discussing how to improve stability but, again, if you&#8217;ve found this post then it is likely already too late for those discussions. Common errors indicating drive corruption include &#8220;HDD ERR&#8221;, &#8220;HDD Error&#8221;, &#8220;HD ERR&#8221;, etc. when booting up the device. I personally had this happen to me with a Pioneer DVR-633H and the this post may or may not be helpful if you do not have that system.</p>
<p>So, down to the nitty-gritty. You have a failed/corrupted Pioneer Harddrive and you want to recover the contents of the drive (ie, the videos). First off, do <strong>not</strong> re-initialize the drive. If you&#8217;ve done this, it is probably too late. My understanding is that this will format the drive and the existing media will be destroyed. I have created a few Python scripts to extract the video contents of the drive and helper tools to re-assemble the contents but I will warn you, it is time intensive and not a &#8220;1-click&#8221; solution. Recovery of a 160GB drive took approximately 10 hours to extract the raw medial files and 10 hours of manual labor to combine. There are varying stages of corruption and I insist you try the following solutions before resorting to my tools:</p>
<p><strong><a href="http://forum.videohelp.com/threads/290541-HDD-Data-Recovery-from-Pioneer-DVR-530H-S-failed-hard-drive?p=1774865&#038;viewfull=1#post1774865">Play button may still work</a></strong></p>
<blockquote><p>You should be able to copy all the files to DVD-RWs.<br />
The &#8220;Disc Navigator&#8221; is inaccessible but the &#8220;Play&#8221; button should be working, i.e. you still can watch &#038; copy all titles! To navigate between titles, press &#8220;Play&#8221; to start playing a title, then use the &#8220;Prev&#8221;/&#8221;Next&#8221; buttons or the numbers to enter the title number you want to jump to (as instructed in the manual). Insert a blank DVD-RW (VR mode) and simply press &#8220;One Touch Copy&#8221; when playing a title. Once &#8220;Hi-Speed Copy&#8221; appears on screen, you can press &#8220;Stop&#8221; (then press &#8220;Display&#8221; to watch the copying process).</p></blockquote>
<p><strong><a href="http://forum.videohelp.com/threads/304426-Upgrading-drive-in-Pioneer-DVR-633H-S?p=1947494&#038;viewfull=1#post1947494">Hard resest</a></strong></p>
<blockquote><p>try the hardware reset : hold down the stop button (under the disc tray) and press the power button. The recorder will reboot, and you&#8217;ll need to reset the clock, the MN speeds, and other initial options. Once this is done, try doing a low-level TVGOS reset to clear possible corruption:</p></blockquote>
<p>For more reading, see <a href="http://forum.videohelp.com/threads/304426-Upgrading-drive-in-Pioneer-DVR-633H-S">here </a>and <a href="http://forum.videohelp.com/threads/290541-HDD-Data-Recovery-from-Pioneer-DVR-530H-S-failed-hard-drive">here</a>.</p>
<p>So, none of the above worked? I will introduce a procedure to recover the contents of the drive. This is a last-resort tutorial! You may never be able to get your DVR to recognize this harddrive again. Don&#8217;t expect your DVR to work flawlessly once you recover the contents and replace the drive (though mine did).</p>
<p><strong>1. Remove harddrive from DVR</strong><br />
The first step is to remove the physical harddrive from the device. It is a fairly simple procedure, <a href="http://www.pioneerfaq.info/filer/DVR-630/replacing_HDD_X30H.pdf">refer to this guide</a> if you&#8217;d like some guidance.</p>
<p><strong>2. Connect the drive to your desktop/laptop</strong><br />
You&#8217;ll need to purchase either an <a href="http://www.amazon.com/3-5-IDE-HDD-Enclosure-Black/dp/B0018NEQFI">IDE enclosure</a> or <a href="http://www.amazon.com/USB-Cable-Adapter-2-5-Inch-3-5-Inch/dp/B000I0VIYE">IDE-to-USB adapter</a>. Hook your drive up to your machine and drivers should be automatically installed in most modern Operating Systems (they are standard USB harddrive drivers).</p>
<p><strong>3. Create a drive image</strong><br />
Next you need to backup the entire contents of the drive to an image file. You can use a tool such as <a href="http://www.ufsexplorer.com/download_stdr.php">UFS Explorer</a> or similar to do this. Any tool which can create a harddrive backup image should work. This took about 10 hours for my 160GB drive. It works best to have a separate external drive to which you are writing recovery files, such as a 1TB external USB harddrive. This may be very slow and your harddrive may make loud clicking noises at the beginning. I believe this had to do with the drive corruption in my case.</p>
<p><strong>4. Install Python</strong><br />
If you haven&#8217;t already, install a copy of Python so you can run my scripts. <a href="http://www.python.org/getit/">Python can be found here</a>.</p>
<p><strong>5. Download Pioneer DVR Harddrive Recovery Tools</strong><br />
Now hop on over to <a href="https://github.com/mikeknoop/Pioneer-DVR-Filesystem-Tools">Github</a> and download my three recovery tools. They are Python scripts. Each serves a separate purpose:</p>
<ol>
<li><strong>Extract.py</strong> &#8211; Extracts MPEG sequences from a Pioneer drive image</li>
<li><strong>Combine.py</strong> &#8211; A command line to combine multiple MPEG files together</li>
<li><strong>Split.py</strong> &#8211; A tool to manually split a file at a specific byte location</li>
</ol>
<p><strong>6. Extract the media files from your drive image using extract.py</strong><br />
Open extract.py and modify to the user-variables at the top of the file. Explanations of each variable can also be found in this file. Once done, open up a command-line and begin running the file: <code>python extract.py</code>. This will begin extracting each media file from the drive image to the location variable you defined. They are numbered sequentially and are each MPEG files. My drive extraction created approximately 2000 files. This extraction process took about 10 hours as well. You can begin step 7 while this script is running.</p>
<p><strong>7. Open, identify, and combine like-media files</strong><br />
This is the manual-labor step. Roughly speaking, your media files will be laid out on the drive in an sequential fashion. However, beginnings and ends of logical recordings may be interwoven. You&#8217;ll notice this as your inspect the output files. You can view the MPEG streams with Windows Media Player, VLC, and many others. Additionally, the DVR likes to record tiny short repeat-segments all over the drive. In general, any file less than 1000KB is a repeat and can immediately be deleted but you should verify this. Use combine.py to help you combine files into a single MPEG file. There are a few variables you should define within this file, too. Read the usage docs within the script for more details. I also created a split.py script to help you split an MPEG file at a specific byte location. Extract.py is not perfect and sometimes misses the split between two recordings. You may need to manually split files with the script to account for this. Again, read the docs within split.py for more details.</p>
<p><strong>8. Post-processing</strong><br />
Each of your resulting combined MPEG files are &#8220;broken&#8221; in the sense that the headers don&#8217;t properly list the length and other details about the recording. While not necessary, you can search for one of the many available &#8220;MPEG Fixers&#8221; to clean up the rough ends.</p>
<p><strong>Final Thoughts</strong><br />
Recovering these drives is a pain. If your drive is bootable in the DVR, I recommend manually recording the videos to either another device or your desktop/laptop using an <a href="http://www.amazon.com/EasyCAP-DC60-Creator-Capture-High-quality/dp/B002H3BSCM">analog-to-USB adapter</a>. If you do choose to attempt to recover your drive using my tools, I wish you luck and let me know how it turns out.</p>
]]></content:encoded>
			<wfw:commentRss>http://mikeknoop.com/blog/pioneer-dvr-harddrive-recovery-tools/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
