FW: Open enrollment Perl classes in April in Chicago

From use Perl:

Open enrollment Perl classes in April in Chicago
Journal written by brian_d_foy (44) and posted by brian_d_foy on 2008.02.15 2:51

Stonehenge is offering open-enrollment Perl classes in downtown Chicago in April. These are the full Stonehenge courses in a slightly compressed-time format that we offer to the public only a couple times a year:

* Beginning Catalyst with Jon Rockway, April 14-15, 9am to 5pm, $500
* Test Driven Development with Josh McAdams, April 14-15, 6pm to 9pm, $250
* Learning Perl with brian d foy, April 16-18, 9am to 5pm, $750

We can also take purchase orders and alternate payment methods for any of there. We can also offer team discounts for people from the same group paying at the same time. Write to me (brian@stonehenge.com) with any questions about the classes.

Share Button

(Ubuntu 64bit) miro dies after playing a video

After updating my xine libs to 1.1.10-1~gutsy, miro started crashing after playing a single video.  I was able to figure out that by running miro –xine-driver=opengl the problem went away.  It appears that the xine developers might have added a bug.  The default xine driver is ‘xv’ so it is likely the problem is there.
WARNING  downloader: connection closed — quitting
INFO     Shutting down downloaders…
Segmentation fault (core dumped)

Share Button

FlickrDownload.pl

If you’ve ever tried to back up your Flickr account or download the public Flickr photos of other users, then your options were pretty limited on Linux. After trying and failing to get FlickrDown to work under wine, I decided to write my own… in perl 🙂

The various Flickr API’s on CPAN are incomplete with respect to obtaining the list of public/friends photos. I had to use XML::Parser::Lite::Tree::XPath to extract the photos from each Flickr page.

By default, we obtain the largest photo available (orginal -> large -> medium -> small), populate the EXIF comments field with the Flickr photo title, description and url. We verify the file type with File::MimeInfo::Magic and change/add the file extension as necessary.

During testing I received numerous duplicate files which were mostly resolved by adding a rudimentary duplicate file checker.

It should work on just about any platform that Perl does.

Thoughts?
FlickrDownload.pl

FlickrDownload.pl

#!/usr/bin/perl

use strict;
use warnings;

use Digest::MD5 qw(md5_hex);
use Config::Simple;
use Image::ExifTool;
use File::MimeInfo::Magic qw(mimetype extensions);
use Flickr::API::Photos;
use Flickr::Person;
use Getopt::Std;
use HTML::Entities ();
use File::MimeInfo::Magic;
use IO::Scalar;
use LWP;
use Tie::File::AsHash;
use XML::Parser::Lite::Tree::XPath;

##############################

my $cfg = new Config::Simple('FlickrDownload.ini');

my $user_name;
my $id;
my $email;
my %arg_options;
my $photo_directory;
my $found;

getopts('e:i:u:', \%arg_options);

$user_name = $arg_options{u};
$id = $arg_options{i};
$email = $arg_options{e};

my $flickr_api_key = $cfg->param('Flickr.API_KEY');
my $flickr_secret = $cfg->param('Flickr.API_SHARED_SECRET');
my $flickr_email = $cfg->param('Flickr.email');
my $flickr_password = $cfg->param('Flickr.password');

##############################
sub decode_html {
  my $string = shift;

  my $new_string = HTML::Entities::decode($string);

  if ($string ne $new_string) {
    $new_string = decode_html($new_string);
  }

  return $new_string;
}
##############################

my $flickr_person = Flickr::Person->new( {
    api_key    => $flickr_api_key,
    email    => $flickr_email,
    password  => $flickr_password
  } );

if ($user_name) {
  $found = $flickr_person->find( { username => $user_name } );
} elsif ($email) {
  $found = $flickr_person->find( { email => $email } );
  $user_name = $flickr_person->username();
  $found = $flickr_person->find( { username => $user_name } );
} elsif ($id) {
  $found = $flickr_person->id( {id => $id} );
  $user_name = $flickr_person->username();
  $found = $flickr_person->find( { username => $user_name } );
}

if ( $found ) {
  my $page_num = 1;
  my $more_pages = 1;
  my $api = $flickr_person->{people_api}->{api};

  $photo_directory = $cfg->param('Photos.directory') . "/" . $user_name;
  $api->{api_secret} = $flickr_secret;

  my $flickr_photos = Flickr::API::Photos->new(
    $flickr_api_key,
    $flickr_email,
    $flickr_password);

  unless (-d $photo_directory) {
    mkdir $photo_directory
      or die ('Unable to create directory "' . $photo_directory . '"' );
  }

  # for determine whether we might be downloading a duplicate, we need a hash with
  #   the MD5 sum & filename.  We tie both hashes to a file in the flickr user's
  #   directory
  tie my %MD5_HASH, 'Tie::File::AsHash', $photo_directory . "/.md5s", split => ':'
    or die "Problem tying %hash: $!\n";

  tie my %FILES_HASH, 'Tie::File::AsHash', $photo_directory . "/.files_md5s", split => ':'
    or die "Problem tying %hash: $!\n";

  while ($more_pages) {
    my $response = $api->execute_method('flickr.people.getPublicPhotos', {
      api_key    => $flickr_api_key,
      user_id    => $flickr_person->id,
      per_page  => 500,
      page    => $page_num
      } );

    my $xpath = new XML::Parser::Lite::Tree::XPath($response->{tree});
    my @nodes = $xpath->select_nodes('/photos/photo');

    if ($#nodes > 0) {
      foreach my $node (@nodes) {
        my $original_photo;
        my $photo_id = $node->{attributes}->{id};
        my $photo_hash = $flickr_photos->getInfo($photo_id);
        my $photo_title =
          $photo_hash->{'title'}
            ? $photo_hash->{'title'}
            : "";
        my $description =
          $photo_hash->{'description'}
            ? decode_html( $photo_hash->{'description'} )
            : "";

        my %photo_sizes =
          map { $_->{'label'} => $_ }
            @{ $flickr_photos->getSizes($photo_id)->{sizes} };

        if (exists $photo_sizes{'Original'}) {
          $original_photo = $photo_sizes{'Original'};
        } elsif (exists $photo_sizes{'Large'}) {
          $original_photo = $photo_sizes{'Large'};
        } elsif (exists $photo_sizes{'Medium'}) {
          $original_photo = $photo_sizes{'Medium'};
        } elsif (exists $photo_sizes{'Small'}) {
          $original_photo = $photo_sizes{'Small'};
        } else {
          warn "Unable to find url.  Skipping photo."
        }

        printf "name: %s id: %s description: %s\n",
          $photo_title,
          $photo_hash->{'id'},
          $description;

        my $photo_filename = $photo_directory . '/' . $photo_title;
        $photo_filename =~ s/\.\w+$//;

        # Prepopulating the file extension will allow us to eliminate
        #   the vast majority of duplicate images by not downloading
        #   them in the first place.
        if (exists $photo_hash->{'originalformat'}) {
          my $extension = $photo_hash->{'originalformat'};
          $photo_filename .= "_" . $photo_hash->{'id'} . "." . $extension;
        } else {
          # if we don't know at this point what format the image file is
          #   without downloading the image, we can assume it is a jpg
          #   because the vast majority of the photos are jpg.
          $photo_filename .= "_" . $photo_hash->{'id'} . ".jpeg";
        }

        if (-f $photo_filename && (stat($photo_filename))[7] > 2048) {
          printf "We already have photo %s .. Skipping\n", $photo_title;
        } else {
          my $FH;

          my $request = HTTP::Request->new(GET => $original_photo->{'source'} );
          my $response = $api->request($request);
          my $md5_sum = md5_hex($response->content);

          # since we have downloaded the photo, let's put the proper file
          #   extension on it.
          if (my $file_ext = extensions( mimetype(new IO::Scalar \($response->content) ) ) ) {
            $photo_filename =~ s/\.\w+$//;
            $photo_filename .= "." . $file_ext;
          }

          if (exists $MD5_HASH{$md5_sum}) {
            printf "We already have photo %s .. Skipping\n", $photo_title;
          } else {
            $MD5_HASH{$md5_sum} = $photo_filename;
            $FILES_HASH{$photo_filename} = $md5_sum;

            open($FH, ">", $photo_filename)
              or warn ("Unable to write to $photo_filename.\n" );

            binmode $FH;
            print $FH $response->content;
            close $FH;

            # We're going to use Image::ExifTool instead of the built in
            #  exif extracted information from Flickr::API::Photos because
            #  we want to write to the file.
            my $exifTool = new Image::ExifTool;
            my $info = $exifTool->ImageInfo($photo_filename);

            unless ($info->{'DateTimeOriginal'}) {
              if ($photo_hash->{'dates'}->{'taken'}) {
                my $taken_date = $photo_hash->{'dates'}->{'taken'};
                $taken_date =~ s/\-/\:/g;
                $exifTool->SetNewValue("DateTimeOriginal", $taken_date);
              }
            }

            $exifTool->SetNewValue("Comment", $photo_title . ": " . $description . " " . $original_photo->{'source'});
            my $result = $exifTool->WriteInfo($photo_filename);
          }
        }
      }

      $page_num++;
    } else {
      $more_pages = undef;
    }

    untie %MD5_HASH;
    untie %FILES_HASH;
  }
}

FlickrDownload.ini

[Flickr]
email=jason_froebe@email.org
password=**SuperSecretPassword**
API_KEY=**YOUR Flickr API KEY**
API_SHARED_SECRET=**YOUR SHARED SECRET FROM Flickr**

[Photos]
#  where you want to put the photos
directory=/home/jason/flickr
Share Button

FW: Geek Entertainment: Vintage Computers

Geek Entertainment TV

A Festival of Vintage Computers

Posted in GETV Episode by ekai on the December 4th, 2007

[flashvideo filename=http://blip.tv/scripts/flash/showplayer.swf?enablejs=true&feedurl=http%3A%2F%2Fgetv%2Eblip%2Etv%2Frss&file=http%3A%2F%2Fblip%2Etv%2Frss%2Fflash%2F524727&showplayerpath=http%3A%2F%2Fblip%2Etv%2Fscripts%2Fflash%2Fshowplayer%2Eswf width=500 displaywidth=500 /]

Share Button

ISUG: Just sent off the outstanding ASE enhancements list to Sybase Product Manager

As you may remember, I’m a board director in the International Sybase Users’ Group (ISUG).  I’ve recently been appointed the ‘chair’ of the Enhancements Committee.  The committee currently has two people: Chris Pollach and me.  We are responsible for maintaining the enhancements list on the ISUG website and ensuring that the enhancements are reviewed/commented upon by Sybase Product Management.

I’ve just sent off the current list of Enhancements (for ASE) to the PM.  Other products will follow shortly — this was just the first product I emailed off so don’t worry 🙂

Share Button

FW: GigaOM: Episode 27 – Free-to-play MMOs profitable?

In episode 27 of GigaOM, Joyce Kim discusses the future of MMO games. Is it possible to make money off of free to play MMOs?


The GigaOm Show – Free-to-play MMOs profitable?

Share Button

Neumann Homes Bankruptcy Sale (Illinois)

Lisa Thomas, one of the last employees of Neumann Homes, announced on the NeumannHomesBankruptcy@yahoogroups.com mailing list that they are selling their office stuff.

From: Lisa Thomas <lisa.thomas@ neumannhomes. com>
Date: Feb 1, 2008 8:46 AM
Subject: Bankruptcy Sale
To:

Neumann Homes

BANKRUPTCY SALE

2 LOCATIONS

4355 Weaver Parkway

Warrenville, Illinois

OFFICE EQUIPMENT, FURNISHINGS & SUPPIES

Friday, February 15th

Noon – 7 p.m.

&

Saturday, February 16th

7 a.m. – Noon

  • Office equipment including: computers, monitors, fax machines, printers, copiers, audio video equipment, phones, etc.
  • Office furniture including: desks, chairs, filing cabinets and shelving.
  • Office supplies including: training books, binders, folders, organizational & desk supplies

************ ********* ********* ********* ********* ********* ********* ********* ********* ********* **

900 A. Knell Road

Montgomery, IL

OFFICE FURNITURE & MODEL HOME FURNISHINGS

Saturday, February 16th

7 a.m. – Noon

Everything must be removed on the day of sale.

  CASH & CARRY ONLY

Share Button