X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fwebcollage;h=520e53ae5e9efb423f7a578bc329deb76f62c974;hb=f0261d8acab611f3433160e4f07367b870439739;hp=3a8ea8c93b30bf7e666f3d1958eae68a9c6403dc;hpb=0d6b320def9180cf907ceaed56b23a972a11b757;p=xscreensaver diff --git a/hacks/webcollage b/hacks/webcollage index 3a8ea8c9..520e53ae 100755 --- a/hacks/webcollage +++ b/hacks/webcollage @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# webcollage, Copyright (c) 1999-2005 by Jamie Zawinski +# webcollage, Copyright (c) 1999-2008 by Jamie Zawinski # This program decorates the screen with random images from the web. # One satisfied customer described it as "a nonstop pop culture brainbath." # @@ -60,21 +60,31 @@ use bytes; # Larry can take Unicode and shove it up his ass sideways. my $progname = $0; $progname =~ s@.*/@@g; -my $version = q{ $Revision: 1.129 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/; -my $copyright = "WebCollage $version, Copyright (c) 1999-2005" . +my $version = q{ $Revision: 1.149 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/; +my $copyright = "WebCollage $version, Copyright (c) 1999-2008" . " Jamie Zawinski \n" . " http://www.jwz.org/webcollage/\n"; -my @search_methods = ( 56, "altavista", \&pick_from_alta_vista_random_link, - 11, "livejournal", \&pick_from_livejournal_images, - 5, "yahoorand", \&pick_from_yahoo_random_link, - 10, "googlephotos", \&pick_from_google_image_photos, - 5, "googleimgs", \&pick_from_google_images, - 3, "googlenums", \&pick_from_google_image_numbers, - 2, "flickr_recent", \&pick_from_flickr_recent, - 8, "flickr_random", \&pick_from_flickr_random, +my @search_methods = ( 20, "googlephotos", \&pick_from_google_image_photos, + 10, "googleimgs", \&pick_from_google_images, + 10, "googlenums", \&pick_from_google_image_numbers, + + 19, "altavista", \&pick_from_alta_vista_random_link, + 12, "flickr_recent", \&pick_from_flickr_recent, + 10, "flickr_random", \&pick_from_flickr_random, + 10, "livejournal", \&pick_from_livejournal_images, + 5, "twitter", \&pick_from_twitter_images, + 4, "yahoorand", \&pick_from_yahoo_random_link, + + + # This is a cute way to search for a certain webcams. + # Not included in default methods, since these images + # aren't terribly interesting by themselves. + # See also "SurveillanceSaver". + # + 0, "securitycam", \&pick_from_security_camera, # In Apr 2002, Google asked me to stop searching them. # I asked them to add a "random link" url. They said @@ -181,6 +191,11 @@ my %poisoners = ( # (I don't see how they did it, though!) "alwayshotels.com" => 1, # Poisoned Lycos pretty heavily. "nextag.com" => 1, # Poisoned Alta Vista real good. + "ghettodriveby.com" => 1, # Poisoned Google Images. + "crosswordsolver.org" => 1, # Poisoned Google Images. + "xona.com" => 1, # Poisoned Google Images. + "freepatentsonline.com" => 1, # Poisoned Google Images. + "herbdatanz.com" => 1, # Poisoned Google Images. ); @@ -199,6 +214,12 @@ my %warningless_sites = ( "pics.livejournal.com" => 1, "tinypic.com" => 1, "flickr.com" => 1, + "pbase.com" => 1, + "blogger.com" => 1, + "multiply.com" => 1, + "wikimedia.org" => 1, + "twitpic.com" => 1, + "amazonaws.com" => 1, # used by twitpic.com "yimg.com" => 1, # This is where dailynews.yahoo.com stores "eimg.com" => 1, # its images, so pick_from_yahoo_news_text() @@ -209,6 +230,7 @@ my %warningless_sites = ( "images.quizdiva.net" => 1, "driftnet" => 1, # builtin... + "local-directory" => 1, # builtin... ); @@ -241,7 +263,19 @@ my %entity_table = ( "ocirc" => 'ô', "otilde" => 'õ', "ouml" => 'ö', "divide" => '÷', "oslash" => 'ø', "ugrave" => 'ù', "uacute" => 'ú', "ucirc" => 'û', "uuml" => 'ü', "yacute" => 'ý', "thorn" => 'þ', "yuml" => 'ÿ', - "ndash" => '-', "mdash" => "--" + + # HTML 4 entities that do not have 1:1 Latin1 mappings. + "bull" => "*", "hellip"=> "...", "prime" => "'", "Prime" => "\"", + "frasl" => "/", "trade" => "[tm]", "larr" => "<-", "rarr" => "->", + "harr" => "<->", "lArr" => "<=", "rArr" => "=>", "hArr" => "<=>", + "empty" => "Ø", "minus" => "-", "lowast"=> "*", "sim" => "~", + "cong" => "=~", "asymp" => "~", "ne" => "!=", "equiv" => "==", + "le" => "<=", "ge" => ">=", "lang" => "<", "rang" => ">", + "loz" => "<>", "OElig" => "OE", "oelig" => "oe", "Yuml" => "Y", + "circ" => "^", "tilde" => "~", "ensp" => " ", "emsp" => " ", + "thinsp"=> " ", "ndash" => "-", "mdash" => "--", "lsquo" => "`", + "rsquo" => "'", "sbquo" => "'", "ldquo" => "\"", "rdquo" => "\"", + "bdquo" => "\"", "lsaquo"=> "<", "rsaquo"=> ">", ); @@ -282,6 +316,7 @@ my $min_gif_area = (120 * 120); my $no_output_p = 0; my $urls_only_p = 0; +my $cocoa_p = 0; my $imagemap_base = undef; my @pids_to_kill = (); # forked pids we should kill when we exit, if any. @@ -290,6 +325,9 @@ my $driftnet_magic = 'driftnet'; my $driftnet_dir = undef; my $default_driftnet_cmd = "driftnet -a -m 100"; +my $local_magic = 'local-directory'; +my $local_dir = undef; + my $wordlist; my %rejected_urls; @@ -391,10 +429,17 @@ sub get_document_1($$$) { if ($url =~ m@^http://www\.altavista\.com/@ || $url =~ m@^http://random\.yahoo\.com/@ || - $url =~ m@^http://images\.google\.com/@) { + $url =~ m@^http://images\.google\.com/@ || + $url =~ m@^http://www\.google\.com/@) { # block this, you turkeys. - $user_agent = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5)" . - " Gecko/20041111 Firefox/1.0"; + $user_agent = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.7)" . + " Gecko/20070914 Firefox/2.0.0.7"; + + # 28-Jun-2007: Google Images now emits the entire page in JS if + # you claim to be Gecko. They also still block "webcollage". + # They serve non-JS for unrecognised agents, so let's try this... + $user_agent = "NoJavascriptPlease/1.0" + if ($url =~ m@^http://[a-z]+\.google\.com/@); } my $hdrs = "GET " . ($http_proxy ? $url : "/$path") . " HTTP/1.0\r\n" . @@ -486,6 +531,10 @@ sub get_document($$;$) { return get_driftnet_file ($url); } + if (defined($referer) && $referer eq $local_magic) { + return get_local_file ($url); + } + my $orig_url = $url; my $loop_count = 0; my $max_loop_count = 4; @@ -839,7 +888,10 @@ sub pick_image_from_rss($$) { sub pick_dictionary() { my @dicts = ("/usr/dict/words", "/usr/share/dict/words", - "/usr/share/lib/dict/words"); + "/usr/share/lib/dict/words", + "/usr/share/dict/cracklib-small", + "/usr/share/dict/cracklib-words" + ); foreach my $f (@dicts) { if (-f $f) { $wordlist = $f; @@ -1174,6 +1226,7 @@ sub pick_from_yahoo_random_link($) { ############################################################################ # # Pick images from random pages returned by the Alta Vista Random Link +# Note: this seems to have gotten a *lot* less random lately (2007). # ############################################################################ @@ -1272,6 +1325,55 @@ sub pick_from_alta_vista_images($) { } + +############################################################################ +# +# Pick images from Aptix security cameras +# Cribbed liberally from google image search code. +# By Jason Sullivan +# +############################################################################ + +my $aptix_images_url = ("http://www.google.com/search" . + "?q=inurl:%22jpg/image.jpg%3Fr%3D%22"); + +# securitycam +sub pick_from_security_camera($) { + my ($timeout) = @_; + + my $page = (int(rand(9)) + 1); + my $num = 20; # 20 images per page + my $search_url = $aptix_images_url; + + if ($page > 1) { + $search_url .= "&start=" . $page*$num; # page number + $search_url .= "&num=" . $num; #images per page + } + + my ($search_hit_count, @subpages) = + pick_from_search_engine ($timeout, $search_url, ''); + + my @candidates = (); + my %referers; + foreach my $u (@subpages) { + next if ($u =~ m@[/.]google\.com\b@i); # skip google builtins (most links) + next unless ($u =~ m@jpg/image.jpg\?r=@i); # All pics contain this + + LOG ($verbose_filter, " candidate: $u"); + push @candidates, $u; + $referers{$u} = $u; + } + + @candidates = depoison (@candidates); + return () if ($#candidates < 0); + my $i = int(rand($#candidates+1)); + my $img = $candidates[$i]; + my $ref = $referers{$img}; + + LOG ($verbose_load, "picked image " . ($i+1) . ": $img (on $ref)"); + return ($ref, $img); +} + ############################################################################ # @@ -1767,6 +1869,105 @@ sub pick_from_ircimages($) { return ($search_url, $img); } + +############################################################################ +# +# Pick images from Twitter's list of recently-posted images. +# +############################################################################ + +my $twitter_img_url = "http://twitpic.com/public_timeline/feed.rss"; + +# With most of our image sources, we get a random page and then select +# from the images on it. However, in the case of Twitter, the page +# of images tends to update slowly; so we'll remember the last N entries +# on it and randomly select from those, to get a wider variety each time. + +my $twit_cache_size = 1000; +my @twit_cache = (); # fifo, for ordering by age +my %twit_cache = (); # hash, for detecting dups + +# twitter +sub pick_from_twitter_images($) { + my ($timeout) = @_; + + $last_search = $twitter_img_url; # for warnings + + my ( $base, $body ) = get_document ($twitter_img_url, undef, $timeout); + + # Update the cache. + + if ($body) { + $body =~ s/\n/ /gs; + $body =~ s/(([^<>]*)@si); + my $page = html_unquote ($1); + + $page =~ s@/$@@s; + $page .= '/full'; + + next if ($twit_cache{$page}); # already have it + + LOG ($verbose_filter, " candidate: $page"); + push @twit_cache, $page; + $twit_cache{$page} = $page; + } + } + + # Pull from the cache. + + return () if ($#twit_cache == -1); + + my $n = $#twit_cache+1; + my $i = int(rand($n)); + my $page = $twit_cache[$i]; + + # delete this one from @twit_cache and from %twit_cache. + # + @twit_cache = ( @twit_cache[0 .. $i-1], + @twit_cache[$i+1 .. $#twit_cache] ); + delete $twit_cache{$page}; + + # Keep the size of the cache under the limit by nuking older entries + # + while ($#twit_cache >= $twit_cache_size) { + my $page = shift @twit_cache; + delete $twit_cache{$page}; + } + + ( $base, $body ) = get_document ($page, undef, $timeout); + my $img = undef; + + foreach (split (/) { $body .= $_; } + close IN || error ("$id: $file: $!"); + return ($id, $body); +} + + ############################################################################ # @@ -2228,8 +2469,9 @@ sub report_performance() { my $suc = $stats_successes{$name} || 0; my $pct = int($suc * 100 / $try); my $secs = $stats_elapsed{$name}; - my $secs_link = int($secs / $try); - print STDERR sprintf ("$blurb %-12s %4s (%d/%d);\t %2d secs/link\n", + my $secs_link = $secs / $try; + print STDERR sprintf ("$blurb %-14s %4s (%d/%d);" . + " \t %.1f secs/link\n", "$name:", "$pct%", $suc, $try, $secs_link); } } @@ -2250,8 +2492,8 @@ sub save_recent_url($$) { my ($site) = m@^http://([^ \t\n\r/:]+)@; return unless defined ($site); - if ($base eq $driftnet_magic) { - $site = $driftnet_magic; + if ($base eq $driftnet_magic || $base eq $local_magic) { + $site = $base; @recent_images = (); } @@ -2320,6 +2562,7 @@ sub gif_size($) { return () unless ($type =~ /GIF8[7,9]a/); $s = substr ($body, 6, 10); my ($a,$b,$c,$d) = unpack ("C"x4, $s); + return () unless defined ($d); return (($b<<8|$a), ($d<<8|$c)); } @@ -2623,9 +2866,43 @@ sub image_to_pnm($$$) { } } + +# Same as the "ppmmake" command: creates a solid-colored PPM. +# Does not understand the rgb.txt color names except "black" and "white". +# +sub ppmmake($$$$) { + my ($outfile, $bgcolor, $w, $h) = @_; + + my ($r, $g, $b); + if ($bgcolor =~ m/^\#?([\dA-F][\dA-F])([\dA-F][\dA-F])([\dA-F][\dA-F])$/i || + $bgcolor =~ m/^\#?([\dA-F])([\dA-F])([\dA-F])$/i) { + ($r, $g, $b) = (hex($1), hex($2), hex($3)); + } elsif ($bgcolor =~ m/^black$/i) { + ($r, $g, $b) = (0, 0, 0); + } elsif ($bgcolor =~ m/^white$/i) { + ($r, $g, $b) = (0xFF, 0xFF, 0xFF); + } else { + error ("unparsable color name: $bgcolor"); + } + + my $pixel = pack('CCC', $r, $g, $b); + my $bits = "P6\n$w $h\n255\n" . ($pixel x ($w * $h)); + + local *OUT; + open (OUT, ">$outfile") || error ("$outfile: $!"); + print OUT $bits; + close OUT; +} + + sub pick_root_displayer() { my @names = (); + if ($cocoa_p) { + # see "xscreensaver/hacks/webcollage-cocoa.m" + return "echo COCOA LOAD "; + } + foreach my $cmd (@root_displayers) { $_ = $cmd; my ($name) = m/^([^ ]+)/; @@ -2659,9 +2936,14 @@ sub x_or_pbm_output($) { LOG (($verbose_pbm || $verbose_load), "no $_ program"); } + if ($cocoa_p && !defined ($webcollage_helper)) { + error ("webcollage-helper not found in Cocoa-mode!"); + } + + # make sure the various programs we execute exist, right up front. # - my @progs = ("ppmmake"); # always need this one + my @progs = (); if (!defined($webcollage_helper)) { # Only need these others if we don't have the helper. @@ -2676,7 +2958,9 @@ sub x_or_pbm_output($) { # find a root-window displayer program. # - $ppm_to_root_window_cmd = pick_root_displayer(); + if (!$no_output_p) { + $ppm_to_root_window_cmd = pick_root_displayer(); + } if (defined ($window_id)) { error ("-window-id only works if xscreensaver-getimage is installed") @@ -2737,9 +3021,8 @@ sub x_or_pbm_output($) { # Create the sold-colored base image. # - $_ = "ppmmake '$bgcolor' $img_width $img_height"; - LOG ($verbose_pbm, "creating base image: $_"); - nontrapping_system "$_ > $image_ppm"; + LOG ($verbose_pbm, "creating base image: ${img_width}x${img_height}"); + $_ = ppmmake ($image_ppm, $bgcolor, $img_width, $img_height); # Paste the default background image in the middle of it. # @@ -3096,7 +3379,7 @@ sub paste_image($$$$) { # the next network retrieval, which is probably a better thing # to do anyway. # - $cmd .= " &"; + $cmd .= " &" unless ($cocoa_p); $rc = nontrapping_system ($cmd); @@ -3229,6 +3512,40 @@ sub update_imagemap($$$$$$$$) { } +# Figure out what the proxy server should be, either from environment +# variables or by parsing the output of the (MacOS) program "scutil", +# which tells us what the system-wide proxy settings are. +# +sub set_proxy() { + + if (! $http_proxy) { + # historical suckage: the environment variable name is lower case. + $http_proxy = $ENV{http_proxy} || $ENV{HTTP_PROXY}; + } + + if (defined ($http_proxy)) { + if ($http_proxy && $http_proxy =~ m@^http://([^/]*)/?$@ ) { + # historical suckage: allow "http://host:port" as well as "host:port". + $http_proxy = $1; + } + + } else { + my $proxy_data = `scutil --proxy 2>/dev/null`; + my ($server) = ($proxy_data =~ m/\bHTTPProxy\s*:\s*([^\s]+)/s); + my ($port) = ($proxy_data =~ m/\bHTTPPort\s*:\s*([^\s]+)/s); + # Note: this ignores the "ExceptionsList". + if ($server) { + $http_proxy = $server; + $http_proxy .= ":$port" if $port; + } + } + + if ($http_proxy) { + LOG ($verbose_net, "proxy server: $http_proxy"); + } +} + + sub init_signals() { $SIG{HUP} = \&signal_cleanup; @@ -3259,9 +3576,6 @@ sub main() { my $root_p = 0; my $window_id = undef; - # historical suckage: the environment variable name is lower case. - $http_proxy = $ENV{http_proxy} || $ENV{HTTP_PROXY}; - while ($_ = $ARGV[0]) { shift @ARGV; if ($_ eq "-display" || @@ -3281,6 +3595,8 @@ sub main() { } elsif ($_ eq "-urls-only") { $urls_only_p = 1; $no_output_p = 1; + } elsif ($_ eq "-cocoa") { + $cocoa_p = 1; } elsif ($_ eq "-imagemap") { $imagemap_base = shift @ARGV; $no_output_p = 1; @@ -3321,6 +3637,13 @@ sub main() { } else { $driftnet_cmd = $default_driftnet_cmd; } + } elsif ($_ eq "-directory" || $_ eq "--directory") { + @search_methods = ( 100, "local", \&pick_from_local_dir ); + if (! ($ARGV[0] =~ m/^-/)) { + $local_dir = shift @ARGV; + } else { + error ("local directory path must be set") + } } elsif ($_ eq "-debug" || $_ eq "--debug") { my $which = shift @ARGV; my @rest = @search_methods; @@ -3347,25 +3670,18 @@ sub main() { "\t\t [-filter cmd] [-filter2 cmd] [-background color]\n" . "\t\t [-dictionary dictionary-file] [-http-proxy host[:port]]\n" . "\t\t [-driftnet [driftnet-program-and-args]]\n" . + "\t\t [-directory local-image-directory]\n" . "\n"; exit 1; } } - if ($http_proxy && $http_proxy eq "") { - $http_proxy = undef; - } - if ($http_proxy && $http_proxy =~ m@^http://([^/]*)/?$@ ) { - # historical suckage: allow "http://host:port" as well as "host:port". - $http_proxy = $1; - } - - if (!$root_p && !$no_output_p) { + if (!$root_p && !$no_output_p && !$cocoa_p) { print STDERR $copyright; error "the -root argument is mandatory (for now.)"; } - if (!$no_output_p && !$ENV{DISPLAY}) { + if (!$no_output_p && !$cocoa_p && !$ENV{DISPLAY}) { error "\$DISPLAY is not set."; } @@ -3431,7 +3747,13 @@ sub main() { error ("-size WxH is required with -imagemap"); } + if (defined ($local_dir)) { + $_ = "xscreensaver-getimage-file"; + which ($_) || error "$_ not found on \$PATH."; + } + init_signals(); + set_proxy(); spawn_driftnet ($driftnet_cmd) if ($driftnet_cmd);