<?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>AlferSoft Blog &#187; Software Development</title>
	<atom:link href="http://www.alfersoft.com.ar/blog/category/dev/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.alfersoft.com.ar/blog</link>
	<description>Explaining this blog in a few words since 1999</description>
	<lastBuildDate>Wed, 01 Feb 2012 22:36:48 +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>jQuery Lightbox: Adjust big images to the window size</title>
		<link>http://www.alfersoft.com.ar/blog/2011/12/09/jquery-lightbox-adjust-big-images-to-the-window-size/</link>
		<comments>http://www.alfersoft.com.ar/blog/2011/12/09/jquery-lightbox-adjust-big-images-to-the-window-size/#comments</comments>
		<pubDate>Fri, 09 Dec 2011 12:21:27 +0000</pubDate>
		<dc:creator>fvicente</dc:creator>
				<category><![CDATA[Software Development]]></category>
		<category><![CDATA[customization]]></category>
		<category><![CDATA[image size]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[lightbox]]></category>
		<category><![CDATA[limit size]]></category>
		<category><![CDATA[photo size]]></category>
		<category><![CDATA[plugin]]></category>

		<guid isPermaLink="false">http://www.alfersoft.com.ar/blog/?p=350</guid>
		<description><![CDATA[jQuery lightBox is a great plugin inspired in the well-known Lightbox JS by Lokesh Dhakar. After starting using it I realized that things became messy with big images, so I made a small modification to limit the image size to the current browser window size. Also I added an option to hide the image information. [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/12/jquery-lightbox-alfersoft-mod.png"><img src="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/12/jquery-lightbox-alfersoft-mod-300x257.png" alt="" title="jQuery lightBox AlferSoft Mod" width="200" class="alignleft size-medium wp-image-358" /></a><a href="http://leandrovieira.com/projects/jquery/lightbox/" title="jQuery lightBox plugin" target="_blank">jQuery lightBox</a> is a great plugin inspired in the well-known <a href="http://www.huddletogether.com/projects/lightbox2/" title="LightBox JS" target="_blank">Lightbox JS</a> by Lokesh Dhakar.<br/><br />
After starting using it I realized that things became messy with big images, so I made a small modification to limit the image size to the current browser window size.<br/><br />
Also I added an option to hide the image information. The images used to navigate between images and close the preview are in English, so I modified the images to be more neutral (just icons)<br />
<span id="more-350"></span><br />
<strong>Code</strong><br />
Two new options were added:</p>
<pre class="brush: jscript; title: ; notranslate">
fitToWindow: true,		// option to fit preview to window size if the
                                // image is too big
showInfo:    true		// option to show or hide image info
</pre>
<p>To fit the preview to the window size, I&#8217;ve replaced the call to _resize_container_image_box() with _adjust_image_size().<br/><br />
Here is the definition of the new function:</p>
<pre class="brush: jscript; title: ; notranslate">
function _adjust_image_size(objImagePreloader) {
    // get image size
    var imgWidth = objImagePreloader.width;
    var imgHeight = objImagePreloader.height;

    var arrayPageSize = ___getPageSize();
    // calculate proportions
    var imageProportion = imgWidth / imgHeight;
    var winProportion = arrayPageSize[2] / arrayPageSize[3];
    if (imageProportion &gt; winProportion) {
        // calculate max width base on page width
        var maxWidth = arrayPageSize[2] - (settings.containerBorderSize * 2) - (arrayPageSize[2] / 10);
        var maxHeight = Math.round(maxWidth / imageProportion);
    } else {
        // calculate max height base on page height
        var maxHeight = arrayPageSize[3] - (settings.containerBorderSize * 2) - (arrayPageSize[3] / 10) - 40;
        var maxWidth = Math.round(maxHeight * imageProportion);
    }
    if (imgWidth &gt; maxWidth || imgHeight &gt; maxHeight) {
        imgWidth = maxWidth;
        imgHeight = maxHeight;
    }
    $('#lightbox-image').attr('width', imgWidth);
    $('#lightbox-image').attr('height', imgHeight);
    _resize_container_image_box(imgWidth, imgHeight);
};
</pre>
<p>Finally, to optionally hide or show the image info, just canged the following line:</p>
<pre class="brush: jscript; title: ; notranslate">
if ( settings.imageArray.length &gt; 1 ) {
</pre>
<p>to:</p>
<pre class="brush: jscript; title: ; notranslate">
if ( settings.showInfo &amp;&amp; (settings.imageArray.length &gt; 1) ) {
</pre>
<p><a href='http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/12/jquery-lightbox-0.5-alfersoft-mod.zip'>Download modified code here &#8211; jQuery lightBox 0.5 AlferSoft Mod</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.alfersoft.com.ar/blog/2011/12/09/jquery-lightbox-adjust-big-images-to-the-window-size/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Migrating GAE Blobstore to an HRD application</title>
		<link>http://www.alfersoft.com.ar/blog/2011/11/14/migrating-gae-blobstore-to-an-hrd-application/</link>
		<comments>http://www.alfersoft.com.ar/blog/2011/11/14/migrating-gae-blobstore-to-an-hrd-application/#comments</comments>
		<pubDate>Mon, 14 Nov 2011 03:09:40 +0000</pubDate>
		<dc:creator>fvicente</dc:creator>
				<category><![CDATA[Programming FAQ]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[app]]></category>
		<category><![CDATA[blobinfo]]></category>
		<category><![CDATA[blobstore]]></category>
		<category><![CDATA[engine]]></category>
		<category><![CDATA[GAE]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[high replication database]]></category>
		<category><![CDATA[HRD]]></category>
		<category><![CDATA[migrate]]></category>
		<category><![CDATA[migration]]></category>

		<guid isPermaLink="false">http://www.alfersoft.com.ar/blog/?p=296</guid>
		<description><![CDATA[If you are planning to migrate your Google App Engine application to the &#8220;new&#8221; High Replication Database scheme, then you probably know that the Google migration process won&#8217;t handle Blobstore files. Here you will find some python scripts that will help you to move the Blobstore files from the old application to the new one, [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/11/google-app-engine_logo_0111.png"><img src="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/11/google-app-engine_logo_0111.png" alt="Google App Engine" title="google app engine" width="150" height="150" class="alignleft size-full wp-image-301" /></a>If you are planning to migrate your Google App Engine application to the &#8220;new&#8221; High Replication Database scheme, then you probably know that the Google migration process won&#8217;t handle Blobstore files.</p>
<p>Here you will find some python scripts that will help you to move the Blobstore files from the old application to the new one, and correct all the references in the new database. As always, use at your own risk, don&#8217;t try to do anything without reading and understanding the scripts, otherwise you may loose your data permanently.</p>
<p><span id="more-296"></span></p>
<p>Some considerations:</p>
<ul>
<li>We did used these scripts in our very own application, so I can say it works. However, I had to edit the original scripts to make them more &#8220;generic&#8221;, remove my database references and names, etc.</li>
<li>I will try to describe the steps of the migration, but please don&#8217;t try to do anything before reading the whole article first.</li>
<li>This is not by any means intended to be a fully automatic migration, in fact, the steps needs to be manually executed one by one.</li>
<li><b>The scripts won&#8217;t migrate big files.</b> We have migrated files up to 40Mb without problems, but if you have files bigger than that, it will probably fail. For example we tried a 300Mb file unsuccessfully, the reason is that the file needs to be downloaded from the old app to the new, and there is a 1 minute timeout for the app requests, so if the download takes more than that it will fail.</li>
</ul>
<h5>How does the script works</h5>
<p>You will need to put the script in your applications, both the new (HRD) and the old one. It basically exposes four URLs and you will need to access two of them from the HRD application.<br />
By doing this you will download all the blob files from the old application to the new one, the script will create a model that keeps a map between the old and new references.<br />
The final step is to migrate the old references to the new one in the HRD databases.</p>
<h5>Prerequisites</h5>
<ul>
<li>All the references to blob files in your models must be of type <code>blobstore.BlobReferenceProperty</code>, if not you will need to adjust them first.</li>
<li>Migrate the databases from the old app to the new HRD app using the <a href="http://code.google.com/appengine/docs/adminconsole/applicationsettings.html#Migrate_from_Master/Slave_to_High_Replication_Datastore" title="Google App Engine migrate datastore">standard Google migration method</a>.</li>
</ul>
<h5>Steps</h5>
<ul>
<li>Add the URLs to your application</li>
</ul>
<pre>
<pre class="brush: python; title: ; notranslate">
def main():
    application = webapp.WSGIApplication(
          [
           # ... Your app URLs here ...
           # TODO - REMOVE THIS AFTER MIGRATION
           ('/mig/__getblob/(.+)', GetBlob),
           ('/mig/__getblobkeys/?', GetBlobKeys),
           ('/mig/__migrateblobs/?', MigrateBlobs),
           ('/mig/__migratereferences/?', MigrateBlobReferences),
          ], debug=True)
    run_wsgi_app(application)

if __name__ == '__main__':
  main()
</pre>
</pre>
<ul>
<li>Then add the migration code</li>
</ul>
<pre>
<pre class="brush: python; title: ; notranslate">
####
# TEMPORARY BLOBSTORE MIGRATION CODE
####

MODELS = (
    # add your (model, blobinfo_field_name) tuples here
    (&quot;myModel1&quot;, &quot;blobinfofield&quot;),
    (&quot;myModel2&quot;, &quot;BlobInfo&quot;),
)

class mig_BlobMig(db.Model):
    fname = db.StringProperty()
    blobinfo = blobstore.BlobReferenceProperty()
    origkey = db.StringProperty()

class MigrateBlobReferences(blobstore_handlers.BlobstoreDownloadHandler):
    '''Migrate blob info references'''
    def get(self):
        import models
        tb = dict([(str(blob.origkey), blob.blobinfo) for blob in mig_BlobMig.all()])
        for modelname, field in MODELS:
            to_put = []
            skipped = 0
            for obj in db.GqlQuery('SELECT * FROM %s'%modelname):
                oldkey = getattr(obj, field)
                if oldkey:
                    oldkey = str(oldkey.key())
                if oldkey in tb:
                    setattr(obj, field, tb[oldkey])
                    to_put.append(obj)
                else:
                    skipped += 1
            db.put(to_put)
            self.response.out.write(&quot;[%s] - Migrated %d references!&lt;br/&gt;Skipped: %d&lt;br/&gt;\n&quot;%(modelname, len(to_put), skipped))

class MigrateBlobs(blobstore_handlers.BlobstoreDownloadHandler):
    '''Migrate blobs from myoldapp to mynewapp'''
    def get(self):
        import time
        from google.appengine.api import files, urlfetch
        start_time = time.time()
        timed_out = False
        migrated_keys = set([str(blob.origkey) for blob in mig_BlobMig.all()])
        all_blobs = eval(urlfetch.fetch(&quot;https://myoldapp.appspot.com/mig/__getblobkeys&quot;, deadline=15.0).content)
        all_keys = set(all_blobs.keys())
        missing_keys = list(all_keys-migrated_keys)
        if not missing_keys:
            self.response.out.write(&quot;Nothing to migrate!&quot;)
            return
        # Download 20 blobs per round
        download_keys = missing_keys[:20]
        MAXSIZE = (10*1024*1024) # 10MB
        for origkey in download_keys:
            blob = all_blobs[origkey]
            url = &quot;https://myoldapp.appspot.com/mig/__getblob/%s&quot;%urllib.quote_plus(origkey)
            blob_path = files.blobstore.create(mime_type=blob[&quot;content_type&quot;], _blobinfo_uploaded_filename=blob[&quot;filename&quot;])
            fsize = int(blob[&quot;size&quot;])
            with files.open(blob_path, 'a') as f:
                for first_byte in range(0, fsize, MAXSIZE):
                    last_byte = (first_byte+MAXSIZE-1)
                    if last_byte&gt;=fsize: last_byte=(fsize-1)
                    bytes_range = &quot;bytes=%d-%d&quot;%(first_byte,last_byte)
                    logging.info(&quot;Downloading [%s] range [%s] key=[%s]&quot;%(blob[&quot;filename&quot;], bytes_range, origkey))
                    res = urlfetch.fetch(url, deadline=35.0, headers={&quot;Range&quot;: bytes_range})
                    f.write(res.content)
                    del res.content
                    del res
                    if (time.time()-start_time &gt; 40.0):
                        timed_out = True
                        break
            if timed_out:
                self.response.out.write(&quot;Stopped due to time limit!&lt;br/&gt;&quot;)
                break
            files.finalize(blob_path)
            blob_key = files.blobstore.get_blob_key(blob_path)
            blob_info = blobstore.BlobInfo.get(blob_key)
            mig_BlobMig(fname=blob[&quot;filename&quot;], blobinfo=blob_info, origkey=origkey).put()
            logging.info(&quot;Successfully downloaded [%s]!!!&quot;%(blob[&quot;filename&quot;]))
            self.response.out.write(&quot;Migrated %s&lt;br/&gt;&quot;%blob[&quot;filename&quot;])
        self.response.out.write(&quot;&lt;br/&gt;SUCCESS!&quot;)

class GetBlobKeys(blobstore_handlers.BlobstoreDownloadHandler):
    '''Retrieve all Blob Keys, as a JSON string'''
    def get(self):
        from simplejson.encoder import JSONEncoder
        blobs = {}
        for blob_info in blobstore.BlobInfo.all():
            blobs[str(blob_info.key())] = {
             &quot;filename&quot;:str(blob_info.filename),
             &quot;content_type&quot;:str(blob_info.content_type),
             &quot;size&quot;:int(blob_info.size),
             &quot;key&quot;:str(blob_info.key()),
            }
        self.response.headers[&quot;Content-type&quot;] = &quot;application/json&quot;
        self.response.out.write(JSONEncoder().encode(blobs))

class GetBlob(blobstore_handlers.BlobstoreDownloadHandler):
    '''Download a blob given its key'''
    def get(self, blobkey):
        blobkey = str(urllib.unquote(blobkey))
        blob_info = blobstore.BlobInfo.get(blobkey)
        if not blob_info:
            self.error(404)
            return
        self.response.headers[&quot;Content-type&quot;] = str(blob_info.content_type)
        self.send_blob(blob_info, save_as=True)

####
# END OF TEMPORARY BLOBSTORE MIGRATION CODE
####
</pre>
</pre>
<ul>
<li>Modify the <code>MODELS</code> variable adding the models and blob info fields that you need to update</li>
<li>Replace <code>myoldapp</code> URLs with the actual URL of your old application</li>
<li>Synchronize the code on both apps</li>
<li>Open a browser and point it to <code>http://mynewapp.appspot.com/mig/__migrateblobs</code>. Of course, <code>mynewapp</code> must be replaced with your actual HRD application name. This will download the blobs from the old app to the new one. Because there is a timeout limitation you will need to call it many times. <b>WARNING: You must make only one request at time, never call it from two browsers / tabs at the same time or you will mess the migration database!!!</b>. So this step must be carefully executed, one request at time and wait for it to finish before calling the next one. Repeat this until you see the message <code>Nothing to migrate!</code>. This step can be very slow depending on how many files you have in the blobstore and the size of them, it might take a long time to get all the files downloaded. Be patient!</li>
<li>Once all the files are copied to the new app, point the browser to <code>http://mynewapp.appspot.com/mig/__migratereferences</code>. Should be quick, and it needs to be executed only one time. However doesn&#8217;t harm to call it more than once.</li>
<li>The last step, remove the migration code and URLs and synchronize the code again. You can also remove the <code>mig_BlobMig</code> database.</li>
</ul>
<p>Good migration.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.alfersoft.com.ar/blog/2011/11/14/migrating-gae-blobstore-to-an-hrd-application/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Make something useful out of your DPF (Digital Photo Frame)</title>
		<link>http://www.alfersoft.com.ar/blog/2011/04/04/make-something-useful-out-of-your-dpf-digital-photo-frame/</link>
		<comments>http://www.alfersoft.com.ar/blog/2011/04/04/make-something-useful-out-of-your-dpf-digital-photo-frame/#comments</comments>
		<pubDate>Mon, 04 Apr 2011 16:52:05 +0000</pubDate>
		<dc:creator>fvicente</dc:creator>
				<category><![CDATA[Electronic Projects]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[circuit]]></category>
		<category><![CDATA[digital]]></category>
		<category><![CDATA[DIY]]></category>
		<category><![CDATA[dpf]]></category>
		<category><![CDATA[frame]]></category>
		<category><![CDATA[hack]]></category>
		<category><![CDATA[parallel]]></category>
		<category><![CDATA[photo]]></category>
		<category><![CDATA[serial]]></category>
		<category><![CDATA[switch]]></category>
		<category><![CDATA[USB]]></category>

		<guid isPermaLink="false">http://www.alfersoft.com.ar/blog/?p=221</guid>
		<description><![CDATA[We had this great idea of using an old digital photo frame from smartparts to display some useful reports at work. In this case, useful will mean that the reports (pictures in the frame) needs to be automatically updated, with no human intervention. Take a look to this post, you may find something useful for [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110401_171720.jpg"><img src="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110401_171720-150x150.jpg" alt="" title="IMG_20110401_171720" width="100" height="100" class="alignleft size-thumbnail wp-image-232" /></a>We had this great idea of using an old digital photo frame from <a href="http://smartpartsproducts.com/content/index.html">smartparts</a> to display some useful reports at work.<br />
In this case, useful will mean that the reports (pictures in the frame) needs to be automatically updated, with no human intervention.<br />
Take a look to this post, you may find something useful for your own DIY project!<br />
<span id="more-221"></span><br />
There are probably several ways to update the pictures dynamically, we have chosen the easiest one: connect it to an old Itona computer (we&#8217;ve plenty of these crappy machines 300Mhz processor, 512Mb of hard disk) running Ubuntu Linux and then make a script that will periodically (cron) gather the info we want to show from the Internet convert it to JPG images, and update the photo frame.</p>
<p><a href="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110404_105718.jpg"><img src="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110404_105718-225x300.jpg" alt="" title="IMG_20110404_105718" width="225" height="300" class="alignnone size-medium wp-image-225" /></a><br />
Too easy? Ok, a real ninja hacker solution would had been taking apart the photo frame, put a Linux on the nice ARC processor through the JTAG interface and then plug some Wi-Fi dongle and do Everything directly on the DPF itself. But that&#8217;s just dreaming, we don&#8217;t have experience nor time for that.</p>
<p>Now you may ask, why didn&#8217;t you plug a monitor on the machine and just display whatever you want directly on the monitor? and the first reason is, we don&#8217;t have spare LCD monitors for this purpose (in fact we have many CRT monitors that nobody uses, but that requires just too much space). The second reason is that we wanted to do something more exiting, like hacker things <img src='http://www.alfersoft.com.ar/blog/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> .</p>
<p>So we setup everything: we made the scripts to generate the images, installed Ubuntu Linux 10.4 on the Itona and actually we borrowed another 512mb (flash) hard disk form another Itona and put it in the secondary IDE to get a RAID configuration with 1GB of space, just enough for Ubuntu and our script. Then realized of a small problem, as soon as you plug the DPF on the USB port, it enters in &#8220;transfer mode&#8221; and stops displaying the images. No matter if you eject, unmount, safe remove, shut down the computer, etc. the photo frame wont show the pictures until you physically unplug the USB cable (cut off the +5V). Here is were our ingenious circuit enters in action. A <strong>USB interrupter controlled by the parallel port</strong> (I guess serial RS-232 would work fine also using one of the signal pins).<br />
After googling, I&#8217;ve found <a href="http://forum.allaboutcircuits.com/showthread.php?t=38544&#038;page=2">a circuit</a> that seemed usable for this purpose. Just a couple transistors and a few resistors is everything we need.<br />
<a href="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110331_185113.jpg"><img src="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110331_185113-300x225.jpg" alt="" title="IMG_20110331_185113" width="300" height="225" class="alignnone size-medium wp-image-226" /></a><br />
Crop the board and put it in a case&#8230;<br />
<a href="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110331_191457.jpg"><img src="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110331_191457-300x225.jpg" alt="" title="IMG_20110331_191457" width="300" height="225" class="alignnone size-medium wp-image-227" /></a><br />
<a href="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110331_191507.jpg"><img src="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110331_191507-300x225.jpg" alt="" title="IMG_20110331_191507" width="300" height="225" class="alignnone size-medium wp-image-228" /></a><br />
<a href="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110331_192915.jpg"><img src="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110331_192915-300x225.jpg" alt="" title="IMG_20110331_192915" width="300" height="225" class="alignnone size-medium wp-image-229" /></a><br />
Finally, the whole thing mounted:<br />
The Itona hidden behind the desk&#8230;<br />
<a href="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110404_103336.jpg"><img src="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110404_103336-225x300.jpg" alt="" title="IMG_20110404_103336" width="225" height="300" class="alignnone size-medium wp-image-230" /></a><br />
And the DPF showing the reports!! <img src='http://www.alfersoft.com.ar/blog/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /><br />
<a href="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110401_171707.jpg"><img src="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110401_171707-300x225.jpg" alt="" title="IMG_20110401_171707" width="300" height="225" class="alignnone size-medium wp-image-231" /></a><br />
<a href="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110401_171720.jpg"><img src="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/IMG_20110401_171720-300x225.jpg" alt="" title="IMG_20110401_171720" width="300" height="225" class="alignnone size-medium wp-image-232" /></a></p>
<p>For the circuit you will need:</p>
<ul>
<li>1k resistor (2)</li>
<li>10k resistor (2)</li>
<li>PNP transistor</li>
<li>NPN transistor</li>
<li>USB connectors, 1 to plug into the computer, the other whatever is better for your DPF</li>
<li>DB-25 male connector (if you decided to do it via parallel too)</li>
</ul>
<p><strong>IMPORTANT: following image was borrowed from http://forum.allaboutcircuits.com/showthread.php?t=38544&#038;page=2</strong><br />
<a href="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/5V-Switch.png"><img src="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/04/5V-Switch-300x216.png" alt="" title="5V-Switch" width="300" height="216" class="alignnone size-medium wp-image-238" /></a><br />
I&#8217;ve connected the control-in to the PIN #2 of the DB-25, and the PIN #20 to the ground.<br />
To turn the parallel port signal on/off I&#8217;ve made the following program in C (para.c):</p>
<pre>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include
<linux/ppdev.h>

static int pfd;

int main(int argc, char* argv[])
{
	unsigned char data=0;

	if (argc != 3) {
		printf("Usage:\n\tpara [port] [val]\n\nport: parallel port e.g. /dev/parport0\nval:  1 to activate all data bits in parallel port, or\n      0 to disable all data bits in parallel port\n\n");
		return 1;
	}
	if (argv[2][0] == '1') {
		data=0xff;
	}
	pfd = open(argv[1], O_RDWR);
	if (pfd < 0) {
		perror("Failed to open port");
		exit(0);
	}
	if ((ioctl(pfd, PPEXCL) < 0) || (ioctl(pfd, PPCLAIM) < 0)) {
		perror("Failed to lock port");
		close(pfd);
		exit(0);
	}
	printf("Setting %s data bits to: %.2X\n", argv[1], data);
	ioctl(pfd, PPWDATA, &#038;data);
	close(pfd);
	return 0;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.alfersoft.com.ar/blog/2011/04/04/make-something-useful-out-of-your-dpf-digital-photo-frame/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to fix the &#8220;federated domain&#8221; problem in emesene</title>
		<link>http://www.alfersoft.com.ar/blog/2011/03/30/how-to-fix-the-federated-domain-problem-in-emesene/</link>
		<comments>http://www.alfersoft.com.ar/blog/2011/03/30/how-to-fix-the-federated-domain-problem-in-emesene/#comments</comments>
		<pubDate>Wed, 30 Mar 2011 17:36:23 +0000</pubDate>
		<dc:creator>fvicente</dc:creator>
				<category><![CDATA[Linux FAQ]]></category>
		<category><![CDATA[Software Applications]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[bug]]></category>
		<category><![CDATA[domain]]></category>
		<category><![CDATA[emesene]]></category>
		<category><![CDATA[federated]]></category>
		<category><![CDATA[fix]]></category>
		<category><![CDATA[issue]]></category>
		<category><![CDATA[message]]></category>

		<guid isPermaLink="false">http://www.alfersoft.com.ar/blog/?p=215</guid>
		<description><![CDATA[If your emesene keeps displaying the annoying message &#8220;User could not be added: Email Domain is IM Federated Contact LiveID xxx@yyy.com is federated domain.&#8221; no matter if you accept or reject the user, then you probably want to apply this patch. This is not really a fix, but a workaround, is just a hack to [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/03/emesene-logo.png"><img src="http://www.alfersoft.com.ar/blog/wp-content/uploads/2011/03/emesene-logo.png" alt="" title="emesene-logo" width="96" height="96" class="alignleft size-full wp-image-217" /></a><br />
If your emesene keeps displaying the annoying message &#8220;User could not be added: Email Domain is IM Federated Contact LiveID xxx@yyy.com is federated domain.&#8221; no matter if you accept or reject the user, then you probably want to apply this patch.<br />
This is not really a fix, but a workaround, is just a hack to avoid displaying the error message if you really like emesene and you want to use it. If you don&#8217;t like this kind of ugly solutions, there are always other options like Pidgin, aMSN, etc. &#8212; in other words if you don&#8217;t like programming forget it, or wait for a new emesene version.<br />
<span id="more-215"></span><br />
Ok, so, since we have the source code (emesene was developed in python) the file we need to hack is /usr/share/emesene/Controller.py<br />
We just need to find the two lines that displays the messages, and bypass them if the user is our xxx@yyy.com. That would be (in the current version as 2011/03/30) lines 674 and 770.</p>
<p>in line 674 add:<br />
<code>if mail in ("xxx@yyy.com", ): continue</code></p>
<p>then in line 770 add:<br />
<code>if email in ("xxx@yyy.com", ): return</code></p>
<p>So the final Controller.py will look something like:</p>
<pre>
....

    def checkPending(self):
        '''Check for users pending to be added'''

        if self.msn is None:
            return False

        if self.addBuddy is None:
            self.addBuddy = dialog.AddBuddy(self)

        users = self.msn.checkPending()
        if len(users) > 0:
            for mail in users:
                if mail in ("xxx@yyy.com", ): continue
                nick = self.msn.getUserDisplayName(mail)
                self.addBuddy.append(nick, mail)
        return False

....

    def addNotification(self, msnp, command, tid, params, email, nick):
        '''this method is called when a user adds you'''

        if self.addBuddy is None:
            self.addBuddy = dialog.AddBuddy(self)
        if email in ("xxx@yyy.com", ): return
        self.addBuddy.append(nick, email)
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.alfersoft.com.ar/blog/2011/03/30/how-to-fix-the-federated-domain-problem-in-emesene/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Firefox add-on: UploadProgress</title>
		<link>http://www.alfersoft.com.ar/blog/2010/08/26/firefox-add-on-uploadprogress/</link>
		<comments>http://www.alfersoft.com.ar/blog/2010/08/26/firefox-add-on-uploadprogress/#comments</comments>
		<pubDate>Thu, 26 Aug 2010 22:37:48 +0000</pubDate>
		<dc:creator>fvicente</dc:creator>
				<category><![CDATA[Software Applications]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[add-on]]></category>
		<category><![CDATA[component]]></category>
		<category><![CDATA[firefox]]></category>
		<category><![CDATA[meter]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[progress]]></category>
		<category><![CDATA[upload]]></category>

		<guid isPermaLink="false">http://www.alfersoft.com.ar/blog/?p=145</guid>
		<description><![CDATA[This Firefox add-on adds a new option to the Tools menu called &#8220;Uploads&#8221; that displays a small window, similar to the downloads, but displaying only current uploads in progress. The uploads are automatically removed from the window after they finish. The idea is to have a way to know the progress of your file uploads [...]]]></description>
			<content:encoded><![CDATA[<p><img alt="UploadProgress add-on" src="http://www.alfersoft.com.ar/files/upload.png" title="UploadProgress add-on" class="alignleft" width="200" height="136" /> This Firefox add-on adds a new option to the Tools menu called &#8220;Uploads&#8221; that displays a small window, similar to the downloads, but displaying only current uploads in progress. The uploads are automatically removed from the window after they finish. The idea is to have a way to know the progress of your file uploads and an estimated remaining time to finish.</p>
<p>Useful for sites that does not shows the upload progress like youtube.</p>
<p>This is my first add-on, so if you find something wrong please let me know.<br />
I&#8217;ve submitted it to <a href="https://addons.mozilla.org/">AMO</a> <del datetime="2010-10-03T02:48:06+00:00">but since it takes time to get released to the general public, I&#8217;ve decided to put it here in our blog if you want to give it a try.<br />
</del><br />
<a href="https://addons.mozilla.org/en-US/firefox/addon/221510/">Download it here</a>, enjoy!</p>
<p><span id="more-145"></span><br />
Tested in Linux, MacOS X and Windows, this small add-on is 100% JavaScript, no dlls no native XPCOMs. I&#8217;ve read in some forums that once upon a time (2004) Firefox already included an upload progress indicator but since then till today is broken. Today other modern browsers like Chrome already provides an upload percent indicator.</p>
<p>Edit: version 0.3 now displaying a small progress bar in Firefox&#8217;s status-bar. Check it out!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.alfersoft.com.ar/blog/2010/08/26/firefox-add-on-uploadprogress/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>How to create two wired virtual serial ports on Linux?</title>
		<link>http://www.alfersoft.com.ar/blog/2010/02/19/how-to-create-two-wired-virtual-serial-ports-on-linux/</link>
		<comments>http://www.alfersoft.com.ar/blog/2010/02/19/how-to-create-two-wired-virtual-serial-ports-on-linux/#comments</comments>
		<pubDate>Fri, 19 Feb 2010 18:18:32 +0000</pubDate>
		<dc:creator>fvicente</dc:creator>
				<category><![CDATA[Linux FAQ]]></category>
		<category><![CDATA[Programming FAQ]]></category>
		<category><![CDATA[bridged]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[null-modem]]></category>
		<category><![CDATA[port]]></category>
		<category><![CDATA[rs-232]]></category>
		<category><![CDATA[serial]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[virtual]]></category>
		<category><![CDATA[wired]]></category>

		<guid isPermaLink="false">http://www.alfersoft.com.ar/blog/?p=134</guid>
		<description><![CDATA[To create two bridged virtual serial ports use the following command: socat -d -d pty,raw,echo=0 pty,raw,echo=0 The output will show you which are the virtual ports (or pseudo terminals) created, e.g.: 2010/02/19 16:16:33 socat[9662] N PTY is /dev/pts/3 2010/02/19 16:16:33 socat[9662] N PTY is /dev/pts/4 2010/02/19 16:16:33 socat[9662] N starting data transfer loop with FDs [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignnone" title="FAQ" src="http://www.alfersoft.com.ar/files/question.png" alt="" width="48" height="48" /></p>
<p>To create two bridged virtual serial ports use the following command:<br />
<code><br />
socat -d -d pty,raw,echo=0 pty,raw,echo=0<br />
</code></p>
<p>The output will show you which are the virtual ports (or pseudo terminals) created, e.g.:<br />
<code><br />
2010/02/19 16:16:33 socat[9662] N PTY is /dev/pts/3<br />
2010/02/19 16:16:33 socat[9662] N PTY is /dev/pts/4<br />
2010/02/19 16:16:33 socat[9662] N starting data transfer loop with FDs [3,3] and [5,5]<br />
</code></p>
<p>Note: if you are using Ubuntu and you do not have this command, try:<br />
<code><br />
sudo apt-get install socat<br />
</code></p>
<h6><a title="Lock" href="http://commons.wikimedia.org/wiki/File:Gnome-dialog-question.svg" target="_blank">Image source</a></h6>
]]></content:encoded>
			<wfw:commentRss>http://www.alfersoft.com.ar/blog/2010/02/19/how-to-create-two-wired-virtual-serial-ports-on-linux/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How do I compare two binary files on Linux?</title>
		<link>http://www.alfersoft.com.ar/blog/2010/02/19/how-do-i-compare-two-binary-files-on-linux/</link>
		<comments>http://www.alfersoft.com.ar/blog/2010/02/19/how-do-i-compare-two-binary-files-on-linux/#comments</comments>
		<pubDate>Fri, 19 Feb 2010 17:51:00 +0000</pubDate>
		<dc:creator>fvicente</dc:creator>
				<category><![CDATA[Linux FAQ]]></category>
		<category><![CDATA[Programming FAQ]]></category>
		<category><![CDATA[binaries]]></category>
		<category><![CDATA[binary]]></category>
		<category><![CDATA[compare]]></category>
		<category><![CDATA[diff]]></category>
		<category><![CDATA[hexdump]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[meld]]></category>

		<guid isPermaLink="false">http://www.alfersoft.com.ar/blog/?p=128</guid>
		<description><![CDATA[The easiest way I found is dumping the binaries into text files using hexdump and then comparing them with your favourite program (diff, Meld, etc.). E.g.: hexdump -C a.bin >a.txt hexdump -C b.bin >b.txt diff a.txt b.txt Image source]]></description>
			<content:encoded><![CDATA[<p><img class="alignnone" title="FAQ" src="http://www.alfersoft.com.ar/files/question.png" alt="" width="48" height="48" /></p>
<p>The easiest way I found is dumping the binaries into text files using hexdump and then comparing them with your favourite program (diff, Meld, etc.). E.g.:<br />
<code><br />
hexdump -C a.bin >a.txt<br />
hexdump -C b.bin >b.txt<br />
diff a.txt b.txt<br />
</code></p>
<h6><a title="Lock" href="http://commons.wikimedia.org/wiki/File:Gnome-dialog-question.svg" target="_blank">Image source</a></h6>
]]></content:encoded>
			<wfw:commentRss>http://www.alfersoft.com.ar/blog/2010/02/19/how-do-i-compare-two-binary-files-on-linux/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Extracting FLV meta tags with Python</title>
		<link>http://www.alfersoft.com.ar/blog/2010/02/05/extracting-flv-meta-tags-with-python/</link>
		<comments>http://www.alfersoft.com.ar/blog/2010/02/05/extracting-flv-meta-tags-with-python/#comments</comments>
		<pubDate>Sat, 06 Feb 2010 00:07:36 +0000</pubDate>
		<dc:creator>fvicente</dc:creator>
				<category><![CDATA[Programming FAQ]]></category>
		<category><![CDATA[flash]]></category>
		<category><![CDATA[flv]]></category>
		<category><![CDATA[metadata]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://www.alfersoft.com.ar/blog/?p=112</guid>
		<description><![CDATA[I&#8217;ve stolen this code from http://code.activestate.com/recipes/457406/ which in turn was stolen / ported from http://inlet-media.de/flvtool2 , made some small fixes in bugs related to the date conversion function, object and array unmarshaling, file name hardcoded, etc&#8230; If you want to learn more about the FLV format and metatags read Acrobat&#8217;s Video File Format Specification Version [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve stolen this code from <a href="http://code.activestate.com/recipes/457406/" target="_blank">http://code.activestate.com/recipes/457406/</a> which in turn was stolen / ported from <a rel="nofollow" href="http://inlet-media.de/flvtool2">http://inlet-media.de/flvtool2 </a>, made some small fixes in bugs related to the date conversion function, object and array unmarshaling, file name hardcoded, etc&#8230; If you want to learn more about the FLV format and metatags read Acrobat&#8217;s <a title="Video File Format Specification Version 10" href="http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf" target="_blank">Video File Format Specification Version 10</a><br />
<span id="more-112"></span></p>
<pre class="brush: python; title: ; notranslate">
from struct import unpack
from datetime import datetime

class FLVReader(dict):
    &quot;&quot;&quot;
    Reads metadata from FLV files
    &quot;&quot;&quot;

    # Tag types
    AUDIO = 8
    VIDEO = 9
    META = 18
    UNDEFINED = 0

    def __init__(self, filename):
        &quot;&quot;&quot;
        Pass the filename of an flv file and it will return a dictionary of meta
        data.
        &quot;&quot;&quot;
        # Lock on to the file
        self.file = open(filename, 'rb')
        self.signature = self.file.read(3)
        assert self.signature == 'FLV', 'Not an flv file'
        self.version = self.readbyte()
        self.typeFlags = self.readbyte()
        self.dataOffset = self.readint()
        extraDataLen = self.dataOffset - self.file.tell()
        self.extraData = self.file.read(extraDataLen)
        self.readtag()

    def readtag(self):
        unknown = self.readint()
        tagType = self.readbyte()
        dataSize = self.read24bit()
        timeStamp = self.read24bit()
        unknown = self.readint()
        if tagType == self.AUDIO:
            print &quot;Can't handle audio tags yet&quot;
        elif tagType == self.VIDEO:
            print &quot;Can't handle video tags yet&quot;
        elif tagType == self.META:
            endpos = self.file.tell() + dataSize
            self.event = self.readAMFData()
            metaData = self.readAMFData()
            # We got the meta data.
            # Our job is done.
            # We are complete
            self.update(metaData)
        elif tagType == self.UNDEFINED:
            print &quot;Can't handle undefined tags yet&quot;

    def readint(self):
      data = self.file.read(4)
      return unpack('&gt;I', data)[0]

    def readshort(self):
      data = self.file.read(2)
      return unpack('&gt;H', data)[0]

    def readbyte(self):
      data = self.file.read(1)
      return unpack('B', data)[0]

    def read24bit(self):
      b1, b2, b3 = unpack('3B', self.file.read(3))
      return (b1 &lt;&lt; 16) + (b2 &lt;&lt; 8 ) + b3

    def readAMFData(self, dataType=None):
        if dataType is None:
            dataType = self.readbyte()
        funcs = {
            0: self.readAMFDouble,
            1: self.readAMFBoolean,
            2: self.readAMFString,
            3: self.readAMFObject,
            8: self.readAMFMixedArray,
           10: self.readAMFArray,
           11: self.readAMFDate
        }
        func = funcs[dataType]
        if callable(func):
            return func()

    def readAMFDouble(self):
        return unpack('&gt;d', self.file.read(8))[0]

    def readAMFBoolean(self):
        return self.readbyte() == 1

    def readAMFString(self):
        size = self.readshort()
        return self.file.read(size)

    def readAMFObject(self):
        result = {}
        data = True
        while data:
            size = self.readshort()
            key = self.file.read(size)
            dataType = self.readbyte()
            if not key and dataType == 9:
                break
            data = self.readAMFData(dataType)
            result[key] = data
        return result

    def readAMFMixedArray(self):
        size = self.readint()
        result = {}
        i = 0
        while i &lt; size:
            key = self.readAMFString()
            dataType = self.readbyte()
            if not key and dataType == 9:
                break
            result[key] = self.readAMFData(dataType)
            i += 1
        return result

    def readAMFArray(self):
        size = self.readint()
        result = []
        i = 0
        while i &lt; size:
            result.append(self.readAMFData())
            i += 1
        return result

    def readAMFDate(self):
        date = self.readAMFDouble()/1000
        localoffset = self.readshort()
        return datetime.fromtimestamp(date)

if __name__ == '__main__':
    import sys
    from pprint import pprint
    if len(sys.argv) == 1:
        print 'Usage: %s filename [filename]...' % sys.argv[0]
        print 'Where filename is a .flv file'
        print 'eg. %s myfile.flv' % sys.argv[0]
    for fn in sys.argv[1:]:
        x = FLVReader(fn)
        pprint(x)
</pre>
<p>Download this code snippet from <a title="flv.py" href="http://www.alfersoft.com.ar/files/flv.py" target="_blank">here</a></p>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 55px; width: 1px; height: 1px;">from&amp;nbsp;struct&amp;nbsp;import&amp;nbsp;unpack<br />
from&amp;nbsp;datetime&amp;nbsp;import&amp;nbsp;datetime</p>
<p>class&amp;nbsp;FLVReader(dict):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#8221;"&#8221;<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Reads&amp;nbsp;metadata&amp;nbsp;from&amp;nbsp;FLV&amp;nbsp;files<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#8221;"&#8221;</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;Tag&amp;nbsp;types<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AUDIO&amp;nbsp;=&amp;nbsp;8<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;VIDEO&amp;nbsp;=&amp;nbsp;9<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;META&amp;nbsp;=&amp;nbsp;18<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UNDEFINED&amp;nbsp;=&amp;nbsp;0</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;__init__(self,&amp;nbsp;filename):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#8221;"&#8221;<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Pass&amp;nbsp;the&amp;nbsp;filename&amp;nbsp;of&amp;nbsp;an&amp;nbsp;flv&amp;nbsp;file&amp;nbsp;and&amp;nbsp;it&amp;nbsp;will&amp;nbsp;return&amp;nbsp;a&amp;nbsp;dictionary&amp;nbsp;of&amp;nbsp;meta<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data.<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#8221;"&#8221;<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;Lock&amp;nbsp;on&amp;nbsp;to&amp;nbsp;the&amp;nbsp;file<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.file&amp;nbsp;=&amp;nbsp;open(filename,&amp;nbsp;&#8217;rb&#8217;)<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.signature&amp;nbsp;=&amp;nbsp;self.file.read(3)<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;assert&amp;nbsp;self.signature&amp;nbsp;==&amp;nbsp;&#8217;FLV&#8217;,&amp;nbsp;&#8217;Not&amp;nbsp;an&amp;nbsp;flv&amp;nbsp;file&#8217;<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.version&amp;nbsp;=&amp;nbsp;self.readbyte()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.typeFlags&amp;nbsp;=&amp;nbsp;self.readbyte()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.dataOffset&amp;nbsp;=&amp;nbsp;self.readint()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;extraDataLen&amp;nbsp;=&amp;nbsp;self.dataOffset&amp;nbsp;-&amp;nbsp;self.file.tell()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.extraData&amp;nbsp;=&amp;nbsp;self.file.read(extraDataLen)<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.readtag()</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;readtag(self):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unknown&amp;nbsp;=&amp;nbsp;self.readint()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tagType&amp;nbsp;=&amp;nbsp;self.readbyte()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dataSize&amp;nbsp;=&amp;nbsp;self.read24bit()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;timeStamp&amp;nbsp;=&amp;nbsp;self.read24bit()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unknown&amp;nbsp;=&amp;nbsp;self.readint()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;tagType&amp;nbsp;==&amp;nbsp;self.AUDIO:<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print&amp;nbsp;&#8221;Can&#8217;t&amp;nbsp;handle&amp;nbsp;audio&amp;nbsp;tags&amp;nbsp;yet&#8221;<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif&amp;nbsp;tagType&amp;nbsp;==&amp;nbsp;self.VIDEO:<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print&amp;nbsp;&#8221;Can&#8217;t&amp;nbsp;handle&amp;nbsp;video&amp;nbsp;tags&amp;nbsp;yet&#8221;<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif&amp;nbsp;tagType&amp;nbsp;==&amp;nbsp;self.META:<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;endpos&amp;nbsp;=&amp;nbsp;self.file.tell()&amp;nbsp;+&amp;nbsp;dataSize<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.event&amp;nbsp;=&amp;nbsp;self.readAMFData()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;metaData&amp;nbsp;=&amp;nbsp;self.readAMFData()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;We&amp;nbsp;got&amp;nbsp;the&amp;nbsp;meta&amp;nbsp;data.<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;Our&amp;nbsp;job&amp;nbsp;is&amp;nbsp;done.<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;We&amp;nbsp;are&amp;nbsp;complete<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.update(metaData)<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif&amp;nbsp;tagType&amp;nbsp;==&amp;nbsp;self.UNDEFINED:<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print&amp;nbsp;&#8221;Can&#8217;t&amp;nbsp;handle&amp;nbsp;undefined&amp;nbsp;tags&amp;nbsp;yet&#8221;</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;readint(self):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data&amp;nbsp;=&amp;nbsp;self.file.read(4)<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;unpack(&#8216;&gt;I&#8217;,&amp;nbsp;data)[0]</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;readshort(self):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data&amp;nbsp;=&amp;nbsp;self.file.read(2)<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;unpack(&#8216;&gt;H&#8217;,&amp;nbsp;data)[0]</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;readbyte(self):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data&amp;nbsp;=&amp;nbsp;self.file.read(1)<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;unpack(&#8216;B&#8217;,&amp;nbsp;data)[0]</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;read24bit(self):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;b1,&amp;nbsp;b2,&amp;nbsp;b3&amp;nbsp;=&amp;nbsp;unpack(&#8217;3B&#8217;,&amp;nbsp;self.file.read(3))<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;(b1&amp;nbsp;&lt;&lt;&amp;nbsp;16)&amp;nbsp;+&amp;nbsp;(b2&amp;nbsp;&lt;&lt;&amp;nbsp;8)&amp;nbsp;+&amp;nbsp;b3</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;readAMFData(self,&amp;nbsp;dataType=None):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;dataType&amp;nbsp;is&amp;nbsp;None:<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dataType&amp;nbsp;=&amp;nbsp;self.readbyte()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;funcs&amp;nbsp;=&amp;nbsp;{<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0:&amp;nbsp;self.readAMFDouble,<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1:&amp;nbsp;self.readAMFBoolean,<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2:&amp;nbsp;self.readAMFString,<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3:&amp;nbsp;self.readAMFObject,<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;8:&amp;nbsp;self.readAMFMixedArray,<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;10:&amp;nbsp;self.readAMFArray,<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;11:&amp;nbsp;self.readAMFDate<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;func&amp;nbsp;=&amp;nbsp;funcs[dataType]<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;callable(func):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;func()</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;readAMFDouble(self):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;unpack(&#8216;&gt;d&#8217;,&amp;nbsp;self.file.read(8))[0]</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;readAMFBoolean(self):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;self.readbyte()&amp;nbsp;==&amp;nbsp;1</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;readAMFString(self):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;size&amp;nbsp;=&amp;nbsp;self.readshort()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;self.file.read(size)</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;readAMFObject(self):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result&amp;nbsp;=&amp;nbsp;{}<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data&amp;nbsp;=&amp;nbsp;True<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;data:<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;size&amp;nbsp;=&amp;nbsp;self.readshort()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;key&amp;nbsp;=&amp;nbsp;self.file.read(size)<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dataType&amp;nbsp;=&amp;nbsp;self.readbyte()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;key&amp;nbsp;and&amp;nbsp;dataType&amp;nbsp;==&amp;nbsp;9:<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data&amp;nbsp;=&amp;nbsp;self.readAMFData(dataType)<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result[key]&amp;nbsp;=&amp;nbsp;data<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;result</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;readAMFMixedArray(self):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;size&amp;nbsp;=&amp;nbsp;self.readint()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result&amp;nbsp;=&amp;nbsp;{}<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;i&amp;nbsp;=&amp;nbsp;0<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;i&amp;nbsp;&lt;&amp;nbsp;size:<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;key&amp;nbsp;=&amp;nbsp;self.readAMFString()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dataType&amp;nbsp;=&amp;nbsp;self.readbyte()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;key&amp;nbsp;and&amp;nbsp;dataType&amp;nbsp;==&amp;nbsp;9:<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result[key]&amp;nbsp;=&amp;nbsp;self.readAMFData(dataType)<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;i&amp;nbsp;+=&amp;nbsp;1<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;result</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;readAMFArray(self):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;size&amp;nbsp;=&amp;nbsp;self.readint()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result&amp;nbsp;=&amp;nbsp;[]<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;i&amp;nbsp;=&amp;nbsp;0<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;i&amp;nbsp;&lt;&amp;nbsp;size:<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result.append(self.readAMFData())<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;i&amp;nbsp;+=&amp;nbsp;1<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;result</p>
<p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;readAMFDate(self):<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;date&amp;nbsp;=&amp;nbsp;self.readAMFDouble()/1000<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;localoffset&amp;nbsp;=&amp;nbsp;self.readshort()<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;datetime.fromtimestamp(date)</p>
<p>if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&#8217;__main__&#8217;:<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;import&amp;nbsp;sys<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;from&amp;nbsp;pprint&amp;nbsp;import&amp;nbsp;pprint<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;len(sys.argv)&amp;nbsp;==&amp;nbsp;1:<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print&amp;nbsp;&#8217;Usage:&amp;nbsp;%s&amp;nbsp;filename&amp;nbsp;[filename]&#8230;&#8217;&amp;nbsp;%&amp;nbsp;sys.argv[0]<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print&amp;nbsp;&#8217;Where&amp;nbsp;filename&amp;nbsp;is&amp;nbsp;a&amp;nbsp;.flv&amp;nbsp;file&#8217;<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print&amp;nbsp;&#8217;eg.&amp;nbsp;%s&amp;nbsp;myfile.flv&#8217;&amp;nbsp;%&amp;nbsp;sys.argv[0]<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;fn&amp;nbsp;in&amp;nbsp;sys.argv[1:]:<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;x&amp;nbsp;=&amp;nbsp;FLVReader(fn)<br />
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pprint(x)</p></div>
]]></content:encoded>
			<wfw:commentRss>http://www.alfersoft.com.ar/blog/2010/02/05/extracting-flv-meta-tags-with-python/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>(HTML) Table cell not inheriting style</title>
		<link>http://www.alfersoft.com.ar/blog/2009/06/04/html-table-cell-not-inheriting-style/</link>
		<comments>http://www.alfersoft.com.ar/blog/2009/06/04/html-table-cell-not-inheriting-style/#comments</comments>
		<pubDate>Fri, 05 Jun 2009 00:24:27 +0000</pubDate>
		<dc:creator>fvicente</dc:creator>
				<category><![CDATA[Programming FAQ]]></category>

		<guid isPermaLink="false">http://www.alfersoft.com.ar/blog/?p=108</guid>
		<description><![CDATA[The goal of this new blog category called &#8220;Programming FAQ&#8221; is to publish short posts with day to day programming problems and their solutions, or at least provide a tip to find the proper solution to your problem. And today we start with this simple and stupid problem that can happen to anyone with poor [...]]]></description>
			<content:encoded><![CDATA[<p>The goal of this new blog category called &#8220;Programming FAQ&#8221; is to publish short posts with day to day programming problems and their solutions, or at least provide a tip to find the proper solution to your problem. And today we start with this simple and stupid problem that can happen to anyone with poor HTML experience like me. &#8220;My HTML table does not seems to inherit the style from a parent element&#8221;, here is the example:</p>
<pre>&lt;html&gt;
&lt;body&gt;
&lt;div style="color: blue;"&gt;
	&lt;table&gt;
	&lt;tbody&gt;
		&lt;tr&gt;
			&lt;td&gt;BLUE TEXT&lt;/td&gt;
		&lt;/tr&gt;
	&lt;/tbody&gt;
	&lt;/table&gt;
&lt;div&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>In the below example I would expect to see the &#8220;BLUE TEXT&#8221; in blue, but is not (at least in FireFox). So what the hell is wrong with my code? probably many things but because I&#8217;m not interested in following/reading the W3C recommendations for my uncle&#8217;s home page, here is one possible solution: Add the transitional DOCTYPE to your document, or the DOCTYPE that adequate better to your page (<a title="W3C DOCTYPE" href="http://www.w3.org/QA/2002/04/valid-dtd-list.html" target="_blank">read more here</a>). E.g.:</p>
<pre>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;</pre>
<p>So, now you know. Never forget the DOCTYPE it is very important&#8230; Why? <a title="Don't forget to add a doctype" href="http://www.w3.org/QA/Tips/Doctype" target="_blank">read this</a>&#8230; It has to do with poor standards, browsers and all that crap.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.alfersoft.com.ar/blog/2009/06/04/html-table-cell-not-inheriting-style/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Simplified DES (S-DES) file encryption implementation in C</title>
		<link>http://www.alfersoft.com.ar/blog/2009/05/08/simplified-des-s-des-file-encryption-implementation-in-c/</link>
		<comments>http://www.alfersoft.com.ar/blog/2009/05/08/simplified-des-s-des-file-encryption-implementation-in-c/#comments</comments>
		<pubDate>Fri, 08 May 2009 20:34:11 +0000</pubDate>
		<dc:creator>fvicente</dc:creator>
				<category><![CDATA[Software Development]]></category>
		<category><![CDATA[C encryption S-DES simplified DES cipher]]></category>

		<guid isPermaLink="false">http://www.alfersoft.com.ar/blog/?p=103</guid>
		<description><![CDATA[This encryption algorithm is not secure at all, in fact it was made for educational purposes. It uses a 10-bit key that can be quickly broken by a brute force attack. But, who knows, it may be good for someone interested in some quick encryption where security is not that important, or for embedded devices [...]]]></description>
			<content:encoded><![CDATA[<div class="wp-caption alignnone" style="width: 138px"><img title="Simplified DES" src="http://www.alfersoft.com.ar/files/lock.png" alt="Simplified DES" width="128" height="128" /><p class="wp-caption-text">Simplified DES</p></div>
<p>This encryption algorithm is not secure at all, in fact it was made for educational purposes. It uses a 10-bit key that can be quickly broken by a brute force attack. But, who knows, it may be good for someone interested in some quick encryption where security is not that important, or for embedded devices were resources are limited, or just for lazy students. <img src='http://www.alfersoft.com.ar/blog/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> </p>
<p><span id="more-103"></span></p>
<p>So, here is my version of the S-DES algorithm in plain C. Differently from the <a title="S-DES cpp implementation" href="http://www.programmersheaven.com/download/47588/download.aspx" target="_blank">other version in the web</a> my implementation is more byte-oriented while the other (probably more didactic) is converting the input to a string with &#8217;0&#8242;s and &#8217;1&#8242;s and working over it. Also the other implementation uses some C++ functions while mine is written in plain C.</p>
<p><a title="Simplified DES C implementation" href="http://www.alfersoft.com.ar/files/sdes/sdes.c" target="_blank">Download it</a> and take a look to the code (it is less than 300 lines) &#8212; released under BSD license.</p>
<p>To build it with gcc type:</p>
<p>gcc sdes.c -lm -o sdes</p>
<p>Note that I&#8217;m using math library only because of the pow() function that is used only in debug mode.</p>
<p>And here is a copy of a <a title="S-DES algorithm PDF" href="http://www.alfersoft.com.ar/files/sdes/sdes.pdf" target="_blank">document explaining the algorithm</a>.</p>
<h6><a title="Lock" href="http://upload.wikimedia.org/wikipedia/commons/f/ff/Crystal_Clear_action_lock.png" target="_blank">Image source</a></h6>
]]></content:encoded>
			<wfw:commentRss>http://www.alfersoft.com.ar/blog/2009/05/08/simplified-des-s-des-file-encryption-implementation-in-c/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

