randomfox: (Default)
[personal profile] randomfox
Because it's such a bother to do that manually.

1. self2.pl, scan Flickr photostream. either sequentially or randomly, and add/remove photos to/from Flickr views groups:


#!perl -w
use strict;

use ApiKey;

use Encode;
use Flickr::API;
use LWP::UserAgent;
use XML::Simple;
use Encode;
use Getopt::Long;
use FileHandle;
use Time::HiRes qw(usleep);

my @groups = (
    { name=>"1-25 Views", id=>'66969363@N00', lbound=>1, ubound=>24 },
    { name=>"25-50 Views", id=>'55265535@N00', lbound=>25, ubound=>49 },
    { name=>"50-75 Views", id=>'38541060@N00', lbound=>50, ubound=>74 },
    { name=>"75-100 Views", id=>'45499242@N00', lbound=>75, ubound=>99 },
    { name=>"100-150 Views", id=>'25694047@N00', lbound=>100, ubound=>149 },
    { name=>"150-200 Views", id=>'57008537@N00', lbound=>150, ubound=>199 },

    { name=>"Views: 25", id=>'14813384@N00', lbound=>25, ubound=>49 },
    { name=>"Views: 50", id=>'63923506@N00', lbound=>50, ubound=>74 },
    { name=>"Views: 75", id=>'26651003@N00', lbound=>75, ubound=>99 },
    { name=>"Views: 100", id=>'37045109@N00', lbound=>100, ubound=>199 },
    { name=>"Views: 200", id=>'35419517@N00', lbound=>200, ubound=>299 },
    { name=>"Views: 300", id=>'76852794@N00', lbound=>300, ubound=>399 },
    { name=>"Views: 400", id=>'97866666@N00', lbound=>400, ubound=>499 },
    { name=>"Views: 500", id=>'32266655@N00', lbound=>500, ubound=>599 },
    { name=>"Views: 600", id=>'50687206@N00', lbound=>600, ubound=>699 },
    { name=>"Views: 700", id=>'65104419@N00', lbound=>700, ubound=>799 },
    { name=>"Views: 800", id=>'87608476@N00', lbound=>800, ubound=>899 },
    { name=>"Views: 900", id=>'79303709@N00', lbound=>900, ubound=>999 },
    { name=>"Views: 1000", id=>'85342170@N00', lbound=>1000, ubound=>1249 },
    { name=>"Views: 1250", id=>'66448677@N00', lbound=>1250, ubound=>99999 },

    { name=>" 100+ Views OR 10+ Favorites", id=>'53758961@N00', lbound=>100, ubound=>99999 },
    { name=>"50 Views - 100", id=>'33128095@N00', lbound=>50, ubound=>99 },
    { name=>"50 to 99 views", id=>'47017726@N00', lbound=>50, ubound=>99 },
    { name=>"Centurian Club", id=>'38475367@N00', lbound=>100, ubound=>199 },
    { name=>"200 Views", id=>'12476408@N00', lbound=>200, ubound=>299 },    
    { name=>"200 Views (freeminded)", id=>'23348528@N00', lbound=>200, ubound=>299 },
    { name=>"100 Views", id=>'49688781@N00', lbound=>100, ubound=>199 },
    { name=>"100 Views - 200", id=>'49864370@N00', lbound=>100, ubound=>199 },
    { name=>"300 Views - 499", id=>'94294471@N00', lbound=>300, ubound=>499 },
    { name=>"400 Views", id=>'73162739@N00', lbound=>400, ubound=>499 },
    { name=>"500 Views", id=>'29315372@N00', lbound=>500, ubound=>599 },
    { name=>"600 Views", id=>'93769266@N00', lbound=>600, ubound=>999 },
    { name=>"1000 Views", id=>'83435940@N00', lbound=>1000, ubound=>1499 },

    { name=>"Views: 7-25", id=>'54402757@N00', lbound=>7, ubound=>25 },
    { name=>"Views: 26-50", id=>'54083170@N00', lbound=>26, ubound=>50 },
    { name=>"Views: 51-75", id=>'25553907@N00', lbound=>51, ubound=>75 },
    { name=>"Views: 76-100", id=>'76213832@N00', lbound=>76, ubound=>100 },
    { name=>"Views: 101-200", id=>'37847006@N00', lbound=>101, ubound=>200 },
    { name=>"Views: 201-300", id=>'23297148@N00', lbound=>201, ubound=>300 },
    { name=>"Views: 301-400", id=>'51348184@N00', lbound=>301, ubound=>400 },
    { name=>"Views: 401-500", id=>'36708499@N00', lbound=>401, ubound=>500 },
    { name=>"Views: 501-600", id=>'82555169@N00', lbound=>501, ubound=>600 },
    { name=>"Views: 601-700", id=>'55018883@N00', lbound=>601, ubound=>700 },
    { name=>"Views: 701-800", id=>'57949802@N00', lbound=>701, ubound=>800 },
    { name=>"Views: 801-900", id=>'11312712@N00', lbound=>801, ubound=>900 },
    { name=>"Views: 901-1000", id=>'25733561@N00', lbound=>901, ubound=>1000 },
    { name=>"Views: 1000-1250", id=>'52647536@N00', lbound=>1001, ubound=>1249 },
    { name=>"Views: 1250-1500", id=>'55588602@N00', lbound=>1250, ubound=>1500 },

    { name=>"10 views (-25)", id=>'33472647@N00', lbound=>10, ubound=>24 },
    { name=>"25 Views (-50)", id=>'43124025@N00', lbound=>25, ubound=>49 },
    { name=>"50 Views (-75)", id=>'93546797@N00', lbound=>50, ubound=>74 },
    { name=>"75 Views (-100)", id=>'20113502@N00', lbound=>75, ubound=>99 },

    { name=>"25 View Club", id=>'39214358@N00', lbound=>25, ubound=>49 },
    { name=>"50 View Club", id=>'16704534@N00', lbound=>50, ubound=>99 },
    { name=>"100 View Club", id=>'78732724@N00', lbound=>100, ubound=>199 },
    { name=>"200 View Club", id=>'35288132@N00', lbound=>200, ubound=>299 },
    { name=>"300 View Club", id=>'45273677@N00', lbound=>300, ubound=>399 },
    { name=>"400 View Club", id=>'87174132@N00', lbound=>400, ubound=>499 },
    { name=>"500 View Club", id=>'56517715@N00', lbound=>500, ubound=>599 },
    { name=>"600 View Club", id=>'38079087@N00', lbound=>600, ubound=>699 },
    { name=>"700 View Club", id=>'64182597@N00', lbound=>700, ubound=>799 },
    { name=>"800 View Club", id=>'95039446@N00', lbound=>800, ubound=>899 },
    { name=>"900 View Club", id=>'98113559@N00', lbound=>900, ubound=>999 },
    { name=>"1,000 View Club", id=>'30605672@N00', lbound=>1000, ubound=>1099 },
    { name=>"1,100 Plus View Club", id=>'62602380@N00', lbound=>1100, ubound=>99999 },

    { name=>"minimum 100 views", id=>'88744574@N00', lbound=>100, ubound=>249 },
    { name=>"minimum 250 views", id=>'53262654@N00', lbound=>250, ubound=>499 },
    { name=>"minimum 500 views", id=>'51308661@N00', lbound=>500, ubound=>699 },
    { name=>"minimum 700 views", id=>'71794126@N00', lbound=>700, ubound=>999 },
    { name=>"minimum 1000 views", id=>'21441943@N00', lbound=>1000, ubound=>1999 },

    { name=>"Views 100", id=>'64501366@N00', lbound=>100, ubound=>199 },
    { name=>"Views 200", id=>'40663022@N00', lbound=>200, ubound=>299 },
    { name=>"Views 300", id=>'17449184@N00', lbound=>300, ubound=>399 },
    { name=>"Views 400", id=>'42514748@N00', lbound=>400, ubound=>499 },
    { name=>"Views 500", id=>'29164599@N00', lbound=>500, ubound=>599 },
    { name=>"Views 600", id=>'89901924@N00', lbound=>600, ubound=>699 },
    { name=>"Views 700", id=>'10145901@N00', lbound=>700, ubound=>799 },
    { name=>"Views 800", id=>'94735186@N00', lbound=>800, ubound=>899 },
    { name=>"Views 900", id=>'42093148@N00', lbound=>900, ubound=>999 },
    { name=>"Views 1000", id=>'23848474@N00', lbound=>1000, ubound=>1499 },

    { name=>"600 Views", id=>'59171278@N00', lbound=>600, ubound=>699 },
    { name=>"700 Views", id=>'22931522@N00', lbound=>700, ubound=>799 },
    { name=>"800 views (-900)", id=>'15389017@N00', lbound=>800, ubound=>899 },
    { name=>"900 Views (-1000)", id=>'52663578@N00', lbound=>900, ubound=>999 },
    { name=>"1100 views (-1200)", id=>'48585996@N00', lbound=>1100, ubound=>1199 },
    { name=>"1200 Views (-1300)", id=>'23626801@N00', lbound=>1200, ubound=>1299 },

    { name=>"~Under 25~", id=>'15983640@N00', lbound=>1, ubound=>24 },
    { name=>"~25 to 50~", id=>'47846660@N00', lbound=>25, ubound=>49 },
    { name=>"~50 to 75~", id=>'95181683@N00', lbound=>50, ubound=>74 },
    { name=>"~75 to 100~", id=>'86604429@N00', lbound=>75, ubound=>99 },
    { name=>"~100 to 150~", id=>'65878370@N00', lbound=>100, ubound=>149 },
    { name=>"~150 to 200~", id=>'82648087@N00', lbound=>150, ubound=>199 },
    { name=>"~200 to 300~", id=>'32499602@N00', lbound=>200, ubound=>299 },
    { name=>"~300 to 400~", id=>'99788504@N00', lbound=>300, ubound=>399 },
    { name=>"~400 to 500~", id=>'10512556@N00', lbound=>400, ubound=>499 },
    { name=>"~500 to 600~", id=>'27112172@N00', lbound=>500, ubound=>599 },
    { name=>"~600 to 700~", id=>'32613242@N00', lbound=>600, ubound=>699 },
    { name=>"~700 to 800~", id=>'55043006@N00', lbound=>700, ubound=>799 },
    { name=>"~800 to 900~", id=>'62802662@N00', lbound=>800, ubound=>899 },
    { name=>"~900 to 1,000~", id=>'87655904@N00', lbound=>900, ubound=>999 },
    { name=>"~1,000 to 1,250~", id=>'84191108@N00', lbound=>1000, ubound=>1249 },
    { name=>"~1,250 to 1,500~", id=>'63751279@N00', lbound=>1250, ubound=>1500 },

    { name=>"Photos with 100 Views", id=>'28905528@N00', lbound=>100, ubound=>199 },
    { name=>"Photos with 200 (- 299) views", id=>'81383576@N00', lbound=>200, ubound=>299 },
    { name=>"Photos with 300 (- 399) views", id=>'32096434@N00', lbound=>300, ubound=>399 },
    { name=>"Photos with 400 (- 499) views", id=>'94544825@N00', lbound=>400, ubound=>499 },
    { name=>"Photos with 500 (- 599) views", id=>'51742214@N00', lbound=>500, ubound=>599 },
    { name=>"Photos with 600 (- 699) views", id=>'51952502@N00', lbound=>600, ubound=>699 },
    { name=>"Photos with 700 (- 799) views", id=>'11514012@N00', lbound=>700, ubound=>799 },
    { name=>"Photos with 800 (- 899) views", id=>'29157411@N00', lbound=>800, ubound=>899 },
    { name=>"Photos with 900 (- 999) views", id=>'27112996@N00', lbound=>900, ubound=>999 },
    { name=>"Photos with 1000 views (and more)", id=>'42827898@N00', lbound=>1000, ubound=>99999 },

    # { name=>"", id=>'', lbound=>, ubound=> },
);

BEGIN {
    # Open log file.
    sub OpenLog {
	my $prefix = shift;
	my $logfn = sprintf("$prefix%X.htm", time);
	my $fh = new FileHandle $logfn, "w";
	defined $fh or die "Error opening $logfn for writing: $!\n";
	$fh->autoflush(1);
	$fh;
    }

    my $logfh = OpenLog "sf";

    sub LogPrint { print $logfh @_; print @_; }
    sub LogWarn { print $logfh @_; warn @_; }
    sub LogDie { print $logfh @_; die @_; }
}

my $SLEEPTIME = 500000;

# Query Flickr with retry.
sub FlickrRetry {
    my $method = shift;
    my $param = shift;

    $param->{auth_token} = $auth_token;

    my $retry_count = 0;
    my $response;
    do {
	my $api = new Flickr::API(
	    {
		'key' => $api_key,
		secret => $shared_secret
	    }
	);
	$response = $api->execute_method($method, $param);
	usleep $SLEEPTIME;
    } while $retry_count++ < 5 and not $response->{success};
    $response;
}

# Generate a list of photos from the Flickr query response.
sub GenPhotoList {
    my $response = shift;

    my $xmlp = new XML::Simple;
    my $xm = $xmlp->XMLin($response->{_content}, forcearray=>['photo']);

    my $photos = $xm->{photos};
#    LogPrint "Page $photos->{page} of $photos->{pages}...\n";

    my $photolist = $photos->{photo};
    my @photoarr;

    for my $id (keys %{$photolist}) {
	my $photo = $photolist->{$id};
	$photo->{id} = $id;
	$photo->{url} = "http://www.flickr.com/photos/$photo->{owner}/$photo->{id}";
	push @photoarr, $photo;
    }
    ( $photos->{pages}, \@photoarr );
}

# Get a list of photos in the photostream.
sub GetPhotos {
    my $userid = shift;
    my $pagenum = shift;
    my $pagelen = shift;

    my $response = FlickrRetry("flickr.people.getPublicPhotos",
	{
	    user_id => $userid,
	    per_page => $pagelen,
	    page => $pagenum
	});
    unless ($response->{success}) {
	LogWarn "Error: $response->{error_message}\n";
	return;
    }

    GenPhotoList($response);
}

# HTTP query with retry.
sub AgentRetry {
    my $url = shift;
    my $retry_count = 0;
    my $response;

    my $agent = new LWP::UserAgent;
    $agent->parse_head(0);

    do {
	$response = $agent->get($url);
	usleep $SLEEPTIME;
    } while $retry_count++ < 5 and $response->is_error;

    $response;
}

# Get view count for one photo.
sub GetViewCount {
    my $photo = shift;

    my $response = AgentRetry($photo->{url});
    if ($response->is_error) {
	LogWarn $response->status_line, "\n";
	next;
    }

    my $resp = $response->content;
    # print "$resp";
    if ($resp =~ /<li class="Stats"[^>]*>\s*Viewed <b>(\d+)<\/b> times[^<]*<\/li>/) {
	$photo->{views} = $1;
    }

    $photo->{views};
}

sub AddPhotoToGroup {
    my $photo = shift;
    my $group = shift;

    return if $group->{hitlimit};

    LogPrint "Adding photo $photo->{id} to group $group->{name}...\n";

    my $response = FlickrRetry("flickr.groups.pools.add",
	{
	    photo_id => $photo->{id},
	    group_id => $group->{id},
	});
    unless ($response->{success}) {
	LogWarn "$response->{error_code}: $response->{error_message}\n";

	# If we hit the limit, stop trying to add photos to the group.
	$response->{error_code} == 96 and $group->{hitlimit} = 1;
    }
}

sub RemovePhotoFromGroup {
    my $photo = shift;
    my $group = shift;

    LogPrint "Removing photo $photo->{id} from group $group->{name}...\n";

    my $response = FlickrRetry("flickr.groups.pools.remove",
	{
	    photo_id => $photo->{id},
	    group_id => $group->{id},
	});
    $response->{success} or LogWarn "$response->{error_message}\n";
}

sub CheckPhotoGroups {
    my $photo = shift;

    defined $photo->{views} or return;

    my $response = FlickrRetry("flickr.photos.getAllContexts",
	{ photo_id => $photo->{id} });
    unless ($response->{success}) {
	LogWarn "Error getting contexts: $response->{error_message}\n";
	return;
    }

    my $xmlp = new XML::Simple;
    my $xm = $xmlp->XMLin($response->{_content}, forcearray=>['pool']);

    my $pool = $xm->{pool};

    for my $group (@groups) {
	# Check if photo is in a group that it shouldn't be in.
	if (defined $pool->{$group->{id}} and
	    (
		$photo->{views} < $group->{lbound} or 
		$photo->{views} > $group->{ubound}
	    )) {
	    RemovePhotoFromGroup $photo, $group;
	}

	# Check if photo is not in a group that it should be in.
	if (not defined $pool->{$group->{id}} and
	    $photo->{views} >= $group->{lbound} and
	    $photo->{views} <= $group->{ubound}) {

	    AddPhotoToGroup $photo, $group;
	}
    }
}

sub CheckPhoto {
    my $count = shift;
    my $photo = shift;

    # Need to encode because there could be wide characters.
    my $title = encode("iso-8859-1", $photo->{title});

    LogPrint "$count. Checking photo $photo->{id} \"$title\"...\n";

    GetViewCount $photo;
    defined $photo->{views} or return;
    LogPrint "$photo->{views} views\n";
    CheckPhotoGroups $photo;
}

# Process one page in the photostream.
sub ProcessPage {
    my $pagenum = shift;
    my $pagelen = shift;

    my ($totalpages, $photolist) = GetPhotos($myid, $pagenum, $pagelen);
    return unless defined $totalpages;
    LogPrint "Page $pagenum of $totalpages...\n";

    my $count = 0;
    for my $photo (@$photolist) {
	++$count;
	CheckPhoto $count, $photo;
    }
}

# Process pictures chosen at random from photostream.
sub RandomProbe {
    srand;
    my ($totalpics, $photolist) = GetPhotos($myid, 1, 1);
    return unless defined $totalpics;

    my $count = 0;
    while (1) {
	my ($totalpages, $photolist) = GetPhotos($myid, 1 + int rand $totalpics, 1);
	unless (defined $totalpages) {
	    sleep 10;
	    next;
	}
	++$count;
	CheckPhoto $count, $photolist->[0];
    }
}

sub usage {
    print <<EOM;
self2.pl [-pagelen=n] pagenum

-pagelen=n: 
	Set the page length to n. 
	n must be at least 1. 
	Default n is 100.

pagenum:
	Page # to scan. 
	First page is 1.
	If omitted, probe the photostream randomly.
EOM
    exit 1;
}

my $pagelen = 100;

Getopt::Long::Configure("bundling_override");
GetOptions('pagelen=s' => \$pagelen) or usage();
die "Page length must be at least 1\n" if $pagelen < 1;

my $pagenum = shift;
if (defined $pagenum) {
    ProcessPage $pagenum, $pagelen;
}
else {
    RandomProbe;
}

__END__



2. ApiKey.pm, a short module containing the API key:


#!perl -w
use strict;

package ApiKey;
use base 'Exporter';
our @EXPORT = qw($api_key $shared_secret $auth_token $myid);

our $api_key = "...";
our $shared_secret = "...";
our $auth_token = "...";
our $myid = '22759125@N00';

1;

__END__

Profile

randomfox: (Default)
randomfox

November 2012

S M T W T F S
    123
45678910
11121314151617
18192021222324
25262728 2930 

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Sep. 26th, 2017 05:44 am
Powered by Dreamwidth Studios