#!/usr/bin/perl use strict; $|++; use warnings; # # Pick random flickr images for X11 wallpaper. # -Rex Roof # use Getopt::Std; use Imager; use List::Util 'shuffle'; use LWP::Simple qw/get mirror/; use Time::HiRes qw(time); use Flickr::API; use Data::Dumper; my $height = 1024; my $width = 1280; $ENV{'DISPLAY'} = ":0.0"; my @wallpaper_cmd = ("/usr/X11R6/bin/hsetroot", "-full"); my $info_file = $ENV{'HOME'}.'/.slather.info'; sub findurl ($); sub timewalk ($); my $final_file = "slather_final-$$.jpg"; my $max_results = 4000; ## current bug in flickr. workaround below require "/home/rex/flickr_keys.pl"; ## contains my flickr key our ($api_key, $api_secret); ## this file contains lines like these, optionally use these lines and ## remove the above two lines: (replace with your own api key and secret, duh) ### my $api_key = "99999x9xx999999xxxx9x9xx99999x99" ; ### my $api_secret = "9xx9xxxx9x99x999"; my ($photos, @pics); our($opt_i, $opt_t, $opt_s, $opt_f, $opt_u, $opt_d, $opt_o, $opt_q, $opt_T); our $VERSION = 0.99; getopts('t:idu:oqTs:f'); # tag we're searching for. I like food. (ignored if -i) my $tag = $opt_t || 'food'; sub HELP_MESSAGE () { print "slather: set x11 backgrounds from random flickr images\n"; print "Usage:\n"; print "\t -t [tag,tag,...] comma separated list of tags to search for\n"; print "\t -u [user] pick random pictures from one user\n"; print "\t -i use most recent top interesting photos (tags ignored)\n"; print "\t -d debug mode (just dumps flickr api result)\n"; print "\t -q be quiet\n"; print "\t -f Just fetch images (-o is implied)\n"; print "\t -s [num] number of screens (defaults to 2)\n"; print "\t -T print timing info\n"; print "\t -o write jpegs out to files.\n"; exit; } my $start = time; my $response; my ($method, $method_options); timewalk(scalar(localtime).": script started"); my $api = Flickr::API->new({ 'key'=>$api_key, 'secret'=> $api_secret }); timewalk("api loaded"); my $nsid; if ( $opt_u ) { if ( $opt_u =~ m/^\d+\@...$/ ) { # this looks like an nsid to me... $nsid = $opt_u; } else { die "username not valid" unless ($opt_u =~ m/\w/); $response = $api->execute_method('flickr.people.findByUsername', { username => $opt_u } ); $nsid = $response->{tree}->{children}->[1]->{attributes}->{nsid}; } unless ( $nsid and $nsid =~ m/^\d+\@...$/ ) { die "no nsid for username found.\n"; } } if ( $opt_i ) { $method = 'flickr.interestingness.getList'; $method_options = { 'sort' => 'interestingness-desc', 'per_page' => 20, 'extras' => 'original_format,icon_server' }; } else { $method = 'flickr.photos.search'; $method_options = { 'tags' => $tag, 'sort' => 'interestingness-desc', 'per_page' => 200, 'extras' => 'original_format,icon_server' }; if ( $opt_u and $nsid ) { $method_options->{user_id} = $nsid; unless ( $opt_t ) { delete $method_options->{tags}; } } } $opt_s = 2 unless ( defined $opt_s ) ; print "calling $method with these options: \n"; print Dumper($method_options); $response = $api->execute_method($method, $method_options); timewalk("api response"); if ( defined $opt_d ) { print Dumper($response); exit; } my $result_count = $response->{tree}->{children}->[1]->{attributes}->{total}; die "no images found.\n" unless ($result_count); ## current flickr bug workaround. # $result_count = $max_results if ($result_count > $max_results); # my $pages = int($result_count / $per_page); $photos = $response->{tree}->{children}->[1]->{children}; # only every other array element has a picture in it... so I remove the rest. while ( $#$photos >= 0 ) { pop(@$photos); push(@pics, pop(@$photos)); } timewalk("photos extracted"); # shuffle pics, and fill @selections with random picture urls that match: # # @pics = shuffle(@pics); timewalk("photos shuffled"); my @selections; open I, ">$info_file" or die $!; while ( $#selections < ($opt_s - 1) ) { if ( my ($jpg,$url) = findurl(pop(@pics)) ) { next unless ( $jpg ); push(@selections, $jpg); print I $url; print I $/; } } close I or die $!; timewalk("$opt_s qualifiying pics found"); # unlink "leftscreen.jpg", "rightscreen.jpg"; my @jpegdata; foreach my $url ( @selections ) { push(@jpegdata, get($url)); timewalk("... picture downloaded."); die "no data found for $url " unless (length($jpegdata[-1])); } my $final_img = Imager->new(xsize=>($width * $opt_s),ysize=>$height); my @imagers; foreach ( my $i=0 ; $i <= $#jpegdata; $i++ ) { push(@imagers, Imager->new); timewalk("Imager object $i made."); $imagers[-1]->read(data=>$jpegdata[$i]) or die $imagers[-1]->errstr; timewalk("Imager data for #$i read."); #$imagers[-1] = $imagers[-1]->scale(xpixels=>$width, ypixels=>$height); $imagers[-1] = $imagers[-1]->scaleX(pixels=>$width); $imagers[-1] = $imagers[-1]->scaleY(pixels=>$height); timewalk("Imager object $i scaled."); if ( defined($opt_o) or defined($opt_f) ) { $imagers[-1]->write(file=> int($i)."_slather_$$.jpg"); timewalk("Image $i written."); } } $final_img->paste(left=>0, top=>0, img=>(pop(@imagers)) ); timewalk("first image added to final image."); foreach ( my $i=0 ; $i <= $#imagers; $i++ ) { $final_img->paste(left=>(($width * ($i+1))+1), top=>0, img=>($imagers[$i])); timewalk("Next image ($i) added to final image."); } $final_img->write(file=>$final_file); timewalk("final image written"); exit if ( defined $opt_f ); if ( system("/usr/X11R6/bin/xset q 2>/dev/null >/dev/null") == 0 ) { system(@wallpaper_cmd, $final_file); warn "background seemingly slathered\n" unless ( defined $opt_q ); } else { warn "display apparently not found.\n" unless ( defined $opt_q ); } timewalk("display set"); unlink $final_file unless ( defined $opt_o ); sub findurl ($) { my $ref = shift; my $response = $api->execute_method('flickr.photos.getSizes', { 'photo_id' => $ref->{attributes}->{id} }); my $pics = $response->{tree}->{children}->[1]->{children}; foreach my $img ( @{$pics} ) { next unless ( $img->{attributes} ); my $h = $img->{attributes}->{height}; my $w = $img->{attributes}->{width}; # my criteria: # width > 800 # 1.2 < (width/height) < 1.4 ## aspect ratio like wallpaper next unless ( $w > 800 ); my $aspect = ($w/$h); next if ( $aspect < 1.2 or $aspect > 1.4 ); # find the real url to the picture $response = $api->execute_method('flickr.photos.getInfo', { 'photo_id' => $ref->{attributes}->{id} }); my $photourl; foreach my $h ( @{$response->{tree}->{children}->[1]->{children}} ) { if ( defined($h->{name}) and $h->{name} eq 'urls' ) { $photourl = $h->{children}->[1]->{children}->[0]->{content}; } } return ($img->{attributes}->{source}, $photourl); } return (undef, undef); } sub timewalk ($) { return unless (defined $opt_T); my $msg = shift; print $msg; print " "; print (time - $start); print " seconds from start."; print $/; }