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__

From:
Anonymous( )Anonymous This account has disabled anonymous posting.
OpenID( )OpenID You can comment on this post while signed in with an account from many other sites, once you have confirmed your email address. Sign in using OpenID.
User
Account name:
Password:
If you don't have an account you can create one now.
Subject:
HTML doesn't work in the subject.

Message:

 
Notice: This account is set to log the IP addresses of everyone who comments.
Links will be displayed as unclickable URLs to help prevent spam.

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 Jul. 23rd, 2017 10:43 am
Powered by Dreamwidth Studios