#!/usr/bin/perl -w # Copyright © 2003-2013 Jamie Zawinski # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation. No representations are made about the suitability of this # software for any purpose. It is provided "as is" without express or # implied warranty. # # Created: 19-Aug-2003. require 5; use diagnostics; use strict; use HTML::Entities; #use open ":encoding(utf8)"; # costs 53% speed my $progname = $0; $progname =~ s@.*/@@g; my $version = q{ $Revision: 1.190 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/; my $verbose = 1; my $debug_p = 0; BEGIN { push @INC, "utils/"; } use Menuify; my $flyer_dir = "flyers"; my $cnames_file = "calendar/names.txt"; my $gnames_file = "gallery/names.txt"; my %rss_files = ("Death Guild" => "deathguild.rss"); my %rss_data; my $rss_max_links = 500; my @months = ("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"); my @days = ("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"); my %monthvals = ( 'jan' => 1, 'january' => 1, 'february' => 2, 'feb' => 2, 'march' => 3, 'mar' => 3, 'april' => 4, 'apr' => 4, 'may' => 5, 'jun' => 6, 'june' => 6, 'jul' => 7, 'july' => 7, 'august' => 8, 'aug' => 8, 'sep' => 9, 'sept' => 9, 'september' => 9, 'oct' => 10, 'october' => 10, 'nov' => 11, 'november' => 11, 'dec' => 12, 'december' => 12 ); my $dim_fg = "\#666"; my $body_template = undef; # This table maps dates to HTML of flyer thumbnails. # Keys are YYYY-MM-DD, values are hashes mapping flyer event names to HTML. # my %flyers = (); my %titles = (); # This table maps each act in any gallery to the gallery pages on which # it occurs. Keys are downcased band names, values are list references # of "YYYY/MM-DD", ordered older to newer. # my %xrefs = (); # This table maps each gallery to the list of bands in that gallery # (omitting any aliases.) Keys are "YYYY/MM-DD", values are list references # of downcased band names. # my %xref_acts = (); # Those act names that were also titles of events. Act names that were # only seen in subheadings aren't included here. my %xref_titles = (); # Table of how we abbeviate certain special case names. # (Exceptions to the normal abbreviation algorithm.) # my %abbreviations = ( 'all hallow\'s eve' => 'halloween', 'atomic jungle' => 'atomic jungle', 'beyond the pale' => 'pale', 'body manipulations' => 'circus', 'cabaret verdalet' => 'verdalet', 'cantankerous lollies' => 'lollies', 'castle spectacular' => 'spectacular', 'chris connelly' => 'connelly', 'control theory' => 'ctrl', 'cruxshadows' => 'cs', 'david j' => 'david j', 'death guild thunderdome' => 'thunderdome', 'eric lewis' => 'elew', 'extreme circus show' => 'circus', 'flying fox' => 'fox', 'flying tiger circus' => 'circus', 'genitorturers' => 'gt', 'hot pink feathers' => 'feathers', 'jill tracy' => 'jill', 'king\'s x' => 'king\'s x', 'meg lee chin' => 'meg', 'momentum dancers' => 'momentum', 'monochrome' => 'mc', 'naughty nursies' => 'nursies', 'nina hagen' => 'nina', 'psyclon nine' => 'p9', 'qool' => 'qoöl', 'red square' => 'red square', 'rosin coven' => 'rosin coven', 'san francisco fetish ball' => 'fetish ball', 'sf fetish ball' => 'fetish ball', 'sf fetish ball gallery show' => 'fetish ball', 'scar tissue' => 'scar tissue', 'see colin slash' => 'colon', 'simon stinger' => 'stinger', 'slick fetish ball' => 'slick', 'spectacular spectacular' => 'spectacular', 'storm and the balls' => 'storm', 'storm inc' => 'storm', 'tesseract7' => 't7', 'thunderdome' => 'thunderdome', 'creepshow peepshow' => 'creepshow', 'qbert' => 'qbert', 'staff christmas party' => 'staff', 'drag king contest' => 'drag kings', 'pop rocks' => 'poprocks', 'pop roxx' => 'poproxx', 'front 242' => 'f242', 'male or female' => 'morf', 'kitten on the keys' => 'kitten', 'jay walker' => 'jay', 'diamond daggers' => 'daggers', 'good vibrations' => 'goodvibes', 'catherine d\'lish' => 'c d\'l', 'smash-up derby' => 'sud', 'stromkern' => 'sk', 'scenic sisters' => 'scenics', 'hubba hubba revue' => 'hubba', 'mynx d\'meanor' => 'mynx', 'rj owens' => 'rj', 'deathline international' => 'dli', 'devil-ettes' => 'de', 'devilettes' => 'de', 'rock and roll adventure kids' => 'kids', 'bawdy island beach party a go-go' => 'bawdy', 'bawdy island' => 'bawdy', 'groovie ghoulies' => 'ghoulies', 'meat vs. death guild' => 'meatguild', 'equilibrium' => 'eq', 'ignite sf' => 'ignite', '16 volt' => '16v', 'sf drag king contest' => 'sfdk', 'zombie prom' => 'zombies', 'zombie dna' => 'zombies', 'apoptygma berzerk' => 'apop', 'assemblage 23' => 'a23', 'bootie sf' => 'bootie', 'blow up' => 'blow up', 'me first and the gimme gimmes' => 'gimmes', 'atlas obscura' => 'obscura', ); # Convert any HTML entities to Unicode characters. # sub html_unquote($) { my ($s) = @_; return HTML::Entities::decode_entities ($s); } # Given the body of a gallery page, extracts the bands mentioned in the # title or subheadings. Returns a downcased list of them all. # sub page_to_xrefs($) { my ($body) = @_; my ($title) = ($body =~ m@\s*(.*?)\s*@si); $title =~ s/^DNA Lounge:\s*//si; # lose heading $title =~ s/^.*@\s*//gsi; # "Groundscore @ d:CODE" $title =~ s/^[nd]:(C[oø0]DE)/CODE/gsi; # merge various CODEs my $otitle = $title; $otitle =~ s/: .*$//s; $otitle =~ s/\s+\+\s+.*$//s; $otitle =~ s/, .*//s; # also extract any subheadings in the body $body =~ s%([^<>\n]*?)% my $x = $1; if (! ($x =~ m@^(Photos|<|>)@i)) { $title .= " + $x"; } %xegsi; $title =~ s/:\s+/ + /gsi; # split at ": " or at " + " my @xrefs = (); my %dups = (); foreach my $act (split (/\s*\+\s*/, $title)) { $act =~ s/,.*$//gsi; # truncate at comma $act =~ s/\s+20\d\d\b//gsi; # take off year $act =~ s/[^a-z\d]*$//gsi; # lose trailing non-alpha $act =~ s/^The\s*//gsi; next if ($act =~ m/anniversary/i); $act = lc($act); push @xrefs, $act unless ($dups{$act}); $dups{$act} = 1; } return ($otitle, @xrefs); } # Populate the %xrefs table with the contents of every gallery: # load each index and parse each title to construct the table. # sub load_xrefs($) { my ($dir) = @_; opendir (my $gdir, "$dir") || error ("$dir: $!"); my @gfiles = sort { $b cmp $a } (readdir ($gdir)); closedir $gdir; $dir .= "/" unless ($dir =~ m@/$@); $dir = "" if $dir eq "./"; print STDERR "$progname: scanning xrefs in $dir\n" if ($verbose > 2); foreach my $year (@gfiles) { next unless ($year =~ m/^\d{4}$/); opendir(my $ydir, "$dir$year") || error ("$dir$year: $!"); my @yfiles = sort { $b cmp $a } (readdir ($ydir)); closedir $ydir; foreach my $gallery (@yfiles) { next unless ($gallery =~ m/^\d\d-\d\d[a-z]?$/); my $date = "$year/$gallery"; my $file = "$dir$date/index.html"; open (my $in, '<', $file) || error ("$file: $!"); local $/ = undef; # read entire file my $body = <$in>; close $in; my ($title, @acts) = page_to_xrefs ($body); $xref_acts{$date} = \@acts; $xref_titles{$title} = 1; foreach my $key (@acts) { my $valP = $xrefs{$key}; my @val = (defined($valP) ? @$valP : ()); my %dups = (); foreach (@val) { $dups{$_} = 1; } unshift @val, $date unless ($dups{$date}); $xrefs{$key} = \@val; } } } if ($verbose > 4) { print STDERR "$progname: xrefs:\n"; foreach my $key (sort (keys (%xrefs))) { my $valP = $xrefs{$key}; my @val = @$valP; my $join = ",\n$progname: " . (' ' x 31); print STDERR "$progname: " . sprintf("%-30s ", $key) . join ($join, @val) . "\n"; } } } # Populate the %flyers table with the thumbnails corresponding # to each date. # sub load_flyers() { print STDERR "\n$progname: flyers:\n" if ($verbose > 4); my $count = 0; opendir (my $ydir, "$flyer_dir") || error ("$flyer_dir: $!"); print STDERR "$progname: reading $flyer_dir\n" if ($verbose > 2); foreach my $year (sort (readdir ($ydir))) { next unless ($year =~ m/^\d{4}$/); opendir (my $mdir, "$flyer_dir/$year") || error ("$flyer_dir/$year: $!"); print STDERR "$progname: reading $flyer_dir/$year\n" if ($verbose > 2); foreach my $month (sort (readdir ($mdir))) { next unless ($month =~ m/^\d{2}$/); my $file = "$flyer_dir/$year/$month/index.html"; print STDERR "$progname: reading $file\n" if ($verbose > 4); open (my $in, '<', $file) || error ("$file: $!"); local $/ = undef; # read entire file my $body = <$in>; close $in; $body =~ s/\s+/ /gs; $body =~ s/(]+>)/si; next unless $img; my ($name) = ($img =~ m/src=\"([^\"]+)\"/si); my ($day, $ord, $n) = ($name =~ m/^(\d\d)([a-z]?)-(\d)-thumb\./si); # Take flyer thumbnails #1 and #2 (if it exists) next unless ($n && $n <= 2); my $key = "$year-$month-$day$ord"; $name = "../../../$flyer_dir/$year/$month/$name"; $img =~ s/(SRC=\")([^\"]+)\"/$1$name\"/si; my $href = $name; $href =~ s/-\d+-thumb.*$/.html/si; $img = "$img"; print STDERR "$progname: $key = $href\n" if ($verbose > 4); my $val = $flyers{$key} || ''; $val .= $img; $val =~ s@(\s*)]*>@$1@s; $flyers{$key} = $val; $count++; } } closedir $mdir; } closedir $ydir; print STDERR "$progname: $count flyer thumbnails\n" if ($verbose > 2); # Now load the calendar data to hack the duplicate flyers, sigh... # { my $count = 0; print STDERR "$progname: reading $cnames_file\n" if ($verbose > 2); open (my $in, '<:utf8', $cnames_file) || error ("$cnames_file: $!"); while (<$in>) { my ($key, $pres, $title, $flyer) = split(/\t/, $_); $title =~ s/^\*//s; $titles{$key} = $title; next unless $flyer; my ($yyyy, $mm, $dd, $ord) = ($key =~ m/^(\d{4})-(\d\d)-(\d\d)([a-z]?)$/s); my ($fyyyy, $fmm, $fdd, $ford) = ($flyer =~ m@/(\d{4})/(\d\d)/(\d\d)([a-z]?)\.@s); my $fkey = "$fyyyy-$fmm-$fdd$ford"; next if ($key eq $fkey); print STDERR "$progname: $key = $fkey\n" if ($verbose > 4); my $html = $flyers{$fkey}; error ("no HTML for flyer $fkey") unless $html; $flyers{$key} = $html; } close $in; } } # Constructs a URL with minimal "../" action. # sub make_relative_link($$) { my ($self, $other) = @_; error ("no self") unless defined($self); error ("no other") unless defined($other); return $other if ($other =~ m!^(/|../)!); my ($self_year, $self_mon) = ($self =~ m!^(\d\d\d\d)/(.*)!); my ($other_year, $other_mon) = ($other =~ m!^(\d\d\d\d)/(.*)!); error ("unparsable: $self") unless ($self_year); error ("unparsable: $other") unless ($other_year); if ($self_year eq $other_year) { $other = "../$other_mon/"; } else { $other = "../../$other/"; } return $other; } # abbreviate a phrase, if there are multiple words in it. # sub abbreviate($) { my ($name) = @_; my $abbr = $abbreviations{$name}; return $abbr if defined($abbr); if ($name =~ m/\s/) { $name =~ s/([^\s])[^\s]+\b/$1/gs; $name =~ s/[^a-z\d]+//gs; } return $name; } # Given a list of names of acts on *this* page, and the date of a page we # intend this page to link to, returns a guess as to what name to use to # describe that link. That is, returns the first name that the two pages # have in common. # sub find_link_name($@) { my ($date, @names) = @_; $date =~ s@^(\.\./)+@@gs; $date =~ s@/$@@s; my $dnamesP = $xref_acts{$date}; error ("no xref_acts entry for $date") unless defined($dnamesP); my @dnames = @$dnamesP; my $result = undef; foreach my $name (@names) { foreach my $dname (@dnames) { if ($name eq $dname) { $result = $dname; last; } } last if ($result); } error ("no match in $date!\n" . "$progname: want: " . join(", ", @names) . "\n" . "$progname: in: " . join(", ", @dnames)) unless defined ($result); $result = abbreviate ($result); error ("WTF is this $1 doing here: $result") if ($result =~ m/(&;)/); return $result; } # Construct tags, and HTML for the "<< prev" and "next >>" parts # based on the body of this document and on the %xrefs table. # sub generate_links($$$$) { my ($file, $title, $body, $oheader_nav) = @_; my %links_table; # maps text to urls my %titles_table; # maps urls to anchor text my $prev_nav = ''; # html of the set of tags for the prev links my $next_nav = ''; # likewise # insert the default links. # $links_table{'top'} = "../../../"; $links_table{'up'} = "../"; my ($otitle, @acts) = page_to_xrefs ($body); my ($date, $year, $suf, $subdir) = ($file =~ m@\b((\d{4})/\d\d-\d\d([a-z]?)(/[^/]+)?)/index\.html$@); $titles_table{'../'} = "$year Photo Gallery"; my %already_done = (); # keys are: # YYYY/MM that have already been emitted, or # anchor-text that has already been emitted print STDERR "\n$date - $title\n" if ($debug_p > 1); foreach my $act (@acts) { my $xrefsP = $xrefs{$act}; my @xrefs = ($xrefsP ? @$xrefsP : ()); # list of the dates of $act my $first = $xrefs[0]; my $last = $xrefs[$#xrefs]; my $prev = undef; # date of $act before today my $next = undef; # date of $act after today $first = undef if ($first && $date eq $first); $last = undef if ($last && $date eq $last); while (@xrefs) { my $x = shift @xrefs; if ($x eq $date) { $next = shift @xrefs; last; } $prev = $x; } if ($debug_p > 1) { print STDERR ("\n" . " $act:\n" . " first: " . ($first ? $first : "-") . ($first && $already_done{$first} ? " [done]" : "") . "\n" . " prev: " . ($prev ? $prev : "-") . ($prev && $already_done{$prev} ? " [done]" : "") . "\n" . " next: " . ($next ? $next : "-") . ($next && $already_done{$next} ? " [done]" : "") . "\n" . " last: " . ($last ? $last : "-") . ($last && $already_done{$last} ? " [done]" : "") . "\n"); } my $fprev = $prev; my $fnext = $next; if (defined ($prev) && !$already_done{$prev}) { my $name = find_link_name ($fprev, @acts); $titles_table{$prev} = $name; if (!$already_done{"prev $name"}) { $prev_nav .= "<< prev $name
\n"; print STDERR "$progname: $date: emit \"prev $name\"\n" if ($verbose > 4); $already_done{$prev} = 1; $already_done{"prev $name"} = 1; } else { print STDERR "$progname: $date: already done \"prev $name\"\n" if ($verbose > 4); } } elsif (defined ($prev)) { print STDERR "$progname: $date: already done \"$prev\"\n" if ($verbose > 4); } if (defined ($next) && !$already_done{$next}) { my $name = find_link_name ($fnext, @acts); $titles_table{$next} = $name; if (!$already_done{"next $name"}) { $next_nav .= "next $name >>
\n"; print STDERR "$progname: $date: emit \"next $name\"\n" if ($verbose > 4); $already_done{$next} = 1; $already_done{"next $name"} = 1; } else { print STDERR "$progname: $date: already done \"next $name\"\n" if ($verbose > 4); } } elsif (defined ($next)) { print STDERR "$progname: $date: already done \"$next\"\n" if ($verbose > 4); } $titles_table{$first} = find_link_name ($first, @acts) if (defined ($first)); $titles_table{$last} = find_link_name ($last, @acts) if (defined ($last)); # For the tags, take the earliest "first" and "next" we've seen. # take the latest "last" and "prev" we've seen. my $ofirst = $links_table{'first'}; my $olast = $links_table{'last'}; my $oprev = $links_table{'prev'}; my $onext = $links_table{'next'}; $first = $ofirst if (!defined($first) || (defined($ofirst) && $ofirst lt $first)); $last = $olast if (!defined($last) || (defined($olast) && $olast gt $last)); $prev = $oprev if (!defined($prev) || (defined($oprev) && $oprev gt $prev)); $next = $onext if (!defined($next) || (defined($onext) && $onext lt $next)); $links_table{'first'} = $first; $links_table{'last'} = $last; $links_table{'prev'} = $prev; $links_table{'next'} = $next; } my $links = ''; foreach my $key ('top', 'up', 'first', 'prev', 'next', 'last') { my $href = $links_table{$key}; next unless defined ($href); my $title = $titles_table{$href}; $title = "$key $title" if ($title && !($key =~ m/^up$/)); $href = make_relative_link ($date, $href); $href = "../$href" if $subdir; $links .= " 4); } sub navcmp($$) { my ($a, $b) = @_; $a =~ s@\"(\.\./)+@@gs; $b =~ s@\"(\.\./)+@@gs; return $a cmp $b; } $next_nav = join("\n", sort {navcmp($a,$b)} (split(/\n/, $next_nav))) . "\n"; $prev_nav = join("\n", sort {navcmp($b,$a)} (split(/\n/, $prev_nav))) . "\n"; my $header_nav = ("<< up
\n" . $prev_nav . $next_nav); if ($debug_p > 1) { print STDERR "\n Finally:\n"; my @lines = split (m/\n/, $header_nav); shift @lines if ($#lines >= 0); foreach (@lines) { my ($href, $name) = m/HREF=\"([^\"\s]+).*((prev|next)[^<>&]+)/si; $name =~ s/\s+$//s; print STDERR " $name: $href\n"; } print STDERR "\n"; } # Shrink all URLs to minimal relativism (after sorting!) $header_nav =~ s@(HREF=\")([^<>\"\s]+) @{ my ($h, $u) = ($1, $2); $u = make_relative_link ($date, $u); $u = "../$u" if $subdir; $h . $u }@gxei; $header_nav =~ s/\n\n+/\n/gs; $header_nav =~ s@
\s*$@@si; # lose trailing BR $header_nav = "$header_nav"; $header_nav =~ s/^/ /gm; $header_nav =~ s/^\s*//gs; # put spaces after any << lines, if they aren't there already. # but only if there are any >> lines. if ($header_nav =~ m@>>@) { $header_nav =~ s@(<<\s[^<>]+)([ \t]*[<\n]) @$1     $2@xgs; } return ($links, $header_nav); } # Given a date, returns the day of the week on which it falls (0-6, sun-sat) # sub dotw($$$) { my ($dotm, $month, $year) = @_; # From the sci.math FAQ: # # The following formula, which is for the Gregorian calendar only, may be # more convenient for computer programming. Note that in some programming # languages the remainder operation can yield a negative result if given # a negative operand, so "mod 7" may not translate to a simple remainder. # # W == (k + [2.6m - 0.2] - 2C + Y + [Y/4] + [C/4]) mod 7 # where [] denotes the integer floor function (round down), # k is day (1 to 31) # m is month (1 = March, ..., 10 = December, 11 = Jan, 12 = Feb) # Treat Jan & Feb as months of the preceding year # C is century (1987 has C = 19) # Y is year (1987 has Y = 87 except Y = 86 for Jan & Feb) # W is week day (0 = Sunday, ..., 6 = Saturday) # # Here the century & 400 year corrections are built into the formula. # The [2.6m-0.2] term relates to the repetitive pattern that the 30-day # months show when March is taken as the first month. my $k = $dotm; my $m = 1 + (($month + 9) % 12); # (1-12) => (11, 12, 1-10) my $y = $year - ($m >= 11 ? 1 : 0); # subtract a year for jan/feb my $C = int ($y/100); my $Y = int ($y%100); my $W = (($k + int ((2.6 * $m) - 0.2) - (2 * $C) + $Y + int ($Y / 4) + int ($C / 4) + 7) % 7); return $W; } # Make sure the date in the HTML is the right one for the file name # and has a correct link into the calendar around it. # sub fix_dates($$$$) { my ($file, $title, $cal_url, $html) = @_; my ($yyyy, $mm, $dd, $ord, $subdir) = ($file =~ m@(\d\d\d\d)/(\d\d)-(\d\d)([a-z]?)(/[^/]+)?/index\.html$@); error ("$file: unparsable filename") unless defined ($dd); my $cal_url2 = $cal_url; $cal_url2 = "../$cal_url" if $subdir; my ($head, $dhref, $date, $tail) = ($html =~ m@^(.*?) (]*>)? \s* ([a-z][a-z][a-z],\s\d\d?\s[a-z][a-z][a-z]+\s\d\d\d\d)\b (.*)$@xsi); error ("$file: unparsable date") unless defined ($date); my $mmm = $months[$mm-1]; my $ddd = $days[dotw($dd, $mm, $yyyy)]; $mmm =~ s/^(...).*$/$1/; $ddd =~ s/^(...).*$/$1/; $date = sprintf ("%s, %02d %s %d", $ddd, $dd, $mmm, $yyyy); my ($old) = ($head =~ m@]*?calendar/[^"<>]+)"@s); error ("$file: missed existing flyer URL") if (!$old && $html =~ m@/flyers/@s); if (!$old) { print STDERR "$progname: $file: no text link to $cal_url2\n" unless ($file =~ m@2009/10-26a@s); # Kludge for EC Hearing photos } elsif (($old || '') ne $cal_url2) { print STDERR "$progname: $file: text flyer mismatch:\n" . " was: " . ($old || '') . "\n" . " now: " . $cal_url2 . "\n"; if ($old) { $head =~ s/\Q$old\E/$cal_url/g; } } if ($dhref) { $dhref = ""; } else { $dhref = ''; } $html = $head . $dhref . $date . $tail; $html =~ s@(../../../)../@$1@gs if $subdir; return $html; } # Loads the template file and constructs our etc from it. # sub load_template() { my $template_file = $DNA::Menuify::template_file; open (my $in, '<', $template_file) || error ("$template_file: $!"); print STDERR "$progname: reading $template_file\n" if ($verbose > 2); local $/ = undef; # read entire file $body_template = <$in>; close $in; # lose everything inside $body_template =~ s@(]*>).*(.*)$@$1\n $2@si; $body_template =~ s@^\s*\s*\n@@gmi; $body_template =~ s@%%ROOT%%@../../../@gs; # We generate our own version of these. $body_template =~ s@^\s*]*>\n@@gmi; } # Clean up the HTML in the head/tail of the given gallery index file. # Returns the title of the gallery, and the gallery thumbnail image. # sub wrap_gallery_1($$$) { my ($file, $wrap_images_p, $check_thumbs_p) = @_; open (my $in, '<', $file) || error ("$file: $!"); print STDERR "$progname: reading $file\n" if ($verbose > 3); local $/ = undef; # read entire file my $ohtml = <$in>; close $in; my $html = $ohtml; $_ = $html; ####################################### # Sectionize the whole document ####################################### my ($title) = m@\s*(.*?)\s*@; error ("$file: no title?") unless (defined($title)); error ("$file: title doesn't begin with DNA Lounge?") unless ($title =~ m/^DNA Lounge: /); my ($body) = m@]*>(.*).*$@xis; error ("$file: unparsable ") unless (defined ($body)); $body =~ s@^(.*?)
\s*?\n(.*)$@$2@si || error ("$file: unparsable top"); my $header = $1; $body =~ s@\s*]*>\s*\s*@@si; $body =~ s@\n" . "\n" . "\n" . "\n" . "
\n" . "
\n" . " $nav\n" . $likers . "
\n" . "\n" . "
\n" . " $fbox\n" . "
\n" . "\n" . "
\n" . "$ibox\n" . "
\n" . "\n" . "
\n" . "
\n" . # "\n" . "$body\n" . "\n" . "
\n" . "
\n" . "
\n" . " $nav\n" . "
\n" . "
\n" . "\n"); $html =~ s/(\n\n)\n+/$1/gsi; $html =~ s/(>)(\n * 2); local $/ = undef; # read entire file my $ocontents = <$in>; close $in; my $contents = $ocontents; my ($title) = ($contents =~ m@\s*(.*?)\s*@s); error ("$file: no title?") unless (defined($title)); error ("$file: title doesn't begin with DNA Lounge?") unless ($title =~ m/^DNA Lounge: /); my ($dotdot) = ($file =~ m@^.*\bgallery/(.*?)(/[^/]*)?$@s); $dotdot =~ s@[^/]+@..@gs; $dotdot =~ s@/+$@@s; my $script = ""; $contents =~ s@[ \t]*]*>\s*()[ \t]*\n?@@si; $contents =~ s@([ \t]*)()@$1 $script\n$1$2@si; $contents =~ s@(]*>).*()@$1$title$2@s; $DNA::Menuify::validate = 1; DNA::Menuify::write_file ($file, $contents); } my %image_size_cache = (); # only barely worth it here. sub image_size($) { my ($file) = @_; my $cache = $image_size_cache{$file}; return @{$cache} if $cache; error ("$file does not exist") unless -f $file; return (0, 0) unless -f $file; my $cmd = ('identify' . ' -define pdf:use-trimbox=true' . ' -density 300x300' . ' -format "%wx%h\n"' . ' "' . $file . '"'); print STDERR "$progname: executing: $cmd\n" if ($verbose > 7); my $result = `$cmd`; $result =~ s/\s+$//s; print STDERR "$progname: ==> $result\n" if ($verbose > 7); my ($ww, $hh, $pages) = (0, 0, 0); foreach (split(/\n/, $result)) { next unless $_; my ($w, $h) = m/^(\d+)x(\d+)$/; error ("unparsable: $_") unless ($w && $h); $ww = $w; $hh += $h; $pages++; } error ("no size: $file") unless ($ww && $hh); print STDERR "$progname: $pages pages\n" if ($pages > 1 && $verbose > 7); my @c = ($ww, $hh); $image_size_cache{$file} = \@c; return ($ww, $hh); } # Print a warning if any of the IMG tags have sizes that don't match # the sizes of the inlined files themselves. # sub check_thumb_sizes($$) { my ($filename, $body) = @_; my $update_thumb_sizes = 1; my $dir = $filename; $dir =~ s@/[^/]*$@/@; my $obody = $body; foreach my $tag (split (/]+)\"@si; next unless $img; $img =~ s/\.([a-z]+)$//s; $img =~ s/-thumb$//s; print STDERR "$progname: $filename: $img is duplicated\n" if $linked{$img}; $linked{$img} = 1; } opendir (my $fdir, "$dir") || error ("$dir: $!"); foreach my $f (readdir ($fdir)) { next if ($f eq 'thumb.jpg'); my ($base) = ($f =~ m@([^/<>"']+)\.(jpg|gif|html)$@s); next unless $base; $base =~ s/-thumb$//s; next if ($base eq 'index'); $existing{$base} = 1; } closedir $fdir; # Check for dangling thumbs/images... # print STDERR "$progname: $filename: checking for dangling thumbs\n" if ($verbose > 1); foreach my $base (sort (keys (%existing))) { my $html_p = (-f "$dir/$base.html"); my $img_p = (-f "$dir/$base.jpg" || -f "$dir/$base.gif"); my $thumb_p = (-f "$dir/$base-thumb.jpg" || -f "$dir/$base-thumb.gif"); if (! $linked{$base}) { print STDERR "$progname: $filename: no link to $base.html\n"; } elsif (! $thumb_p) { print STDERR "$progname: $filename: no thumb for $base\n"; } elsif (! $img_p) { print STDERR "$progname: $filename: no image for $base-thumb\n"; } elsif (! $html_p) { print STDERR "$progname: $filename: no $base.html\n"; } } } # Iterate over all of the gallery files and call wrap_gallery_1 on # each of the DIR/YYYY/MM-DD/index.html files. # sub wrap_gallery($$$$) { my ($dir, $wrap_images_p, $check_thumbs_p, $regal_p) = @_; load_xrefs ($dir); load_flyers (); load_template (); opendir (my $gdir, "$dir") || error ("$dir: $!"); my @gfiles = sort { $b cmp $a } (readdir ($gdir)); closedir $gdir; $dir .= "/" unless ($dir =~ m@/$@); $dir = "" if $dir eq "./"; my $first_year = 99999; my $first_year2 = $first_year; my $last_year = 0; foreach my $year (@gfiles) { next unless ($year =~ m/^\d{4}$/); $first_year = $year if ($year < $first_year); $first_year2 = $year if ($year < $first_year2 && $year >= 2001); $last_year = $year if ($year > $last_year); } foreach my $year (@gfiles) { next unless ($year =~ m/^\d{4}$/); next if ($year == 1985); # This gallery is different. opendir (my $ydir, "$dir$year") || error ("$dir$year: $!"); my @yfiles = sort { my ($aa, $bb) = ($a, $b); $aa .= 'b' unless ($aa =~ m/[a-z]$/); $bb .= 'b' unless ($bb =~ m/[a-z]$/); $bb cmp $aa; } (readdir ($ydir)); closedir $ydir; if ($year == 2004) { push @yfiles, ('04-24/all'); } elsif ($year == 2005) { push @yfiles, ('03-13/oh_jim'); } elsif ($year == 2006) { push @yfiles, ('11-18/photoboof', '12-16/photoboof'); } my $year_html = ""; foreach my $gallery (@yfiles) { next unless ($gallery =~ m@^(\d\d)-(\d\d)([a-z]?)(/[^/]+)?$@s); my ($month, $dotm, $suf, $subdir) = ($1, $2, $3, $4); my $file = "$dir$year/$gallery/index.html"; # next if ("$year$month" > 200404); #### my $mmm = $months[$month-1]; my $ddd = $days[dotw($dotm, $month, $year)]; $mmm =~ s/^(...).*$/$1/; $ddd =~ s/^(...).*$/$1/; my $date = sprintf ("%s, %02d %s %d", $ddd, $dotm, $mmm, $year); my $title = ($regal_p ? '' : wrap_gallery_1 ($file, $wrap_images_p, $check_thumbs_p)); $title =~ s/^DNA Lounge:\s*//si; # if ($title =~ s@(\s*[-+:]|\s+vs\.)\s@$1 @gsi) { # $title = "$title"; # } if ($regal_p) { # Regenerate all of the MM-DD/NNN.html files by re-running gallery.pl. # Don't change MM-DD/index.html. my $d = "$dir$year/$gallery"; if ($d gt "xgallery/2005/02-09") { print STDERR "$progname: skipping $d\n"; } else { my $cmd = `cd $d; gallery.pl --guess`; $cmd =~ s/\s*$//s; $cmd =~ s/ / --noindex /s; $cmd =~ s/ / --debug /s if ($debug_p); #$cmd = "gallery.pl --noindex [0-9]*.jpg; $cmd"; # stragglers $cmd = "cd $d; $cmd"; print STDERR "\n##### $cmd\n"; if (system ($cmd) != 0) { my $status = $? >> 8; my $signal = $? & 127; my $core = $? & 128; exit(1) if ($status != 0 || $signal || $core); } } } next if $subdir; my $thumb = "$gallery/thumb.jpg"; error ("$thumb does not exist") unless -f ("$dir$year/$thumb"); $title =~ s/: /:
/s; $year_html = ("
\n" . " $date:\n" . " $title\n" . "
\n" . $year_html); } my $prev_year = $year-1; my $next_year = $year+1; $prev_year = 1985 if ($year == 2001); # Kludge my $first_p = ($year != $first_year); my $last_p = ($year != $last_year); my $prev_p = (-d "$dir$prev_year"); my $next_p = (-d "$dir$next_year"); my ($prev_a, $prev_b) = ($prev_p ? ("", "") : ("", "")); my $prev = ($prev_p ? "" . "<< $prev_year" : "<< $prev_year"); my $next = ($next_p ? "" . "$next_year >>" : "$next_year >>"); my $output = ("DNA Lounge: Photo Gallery: $year\n" . " \n" . " \n" . ($prev_p ? " \n" : "") . ($next_p ? " \n" : "") . ($first_p ? " \n" : "") . ($last_p ? " \n" : "")); $output .= ("\n" . "\n" . "\n" . "\n" . "
\n" . "
\n" . " $prev\n" . " $next\n" . " $year\n" . "
\n" . "\n" . "


\n" . $year_html . # Center the columns, but keep the same number of columns on each row # (at the expense of sometimes having blank space at the bottom). "

\n" . "
\n" . "
\n" . "
\n" . "\n"); if (!$regal_p && !$check_thumbs_p) { # Sometimes we don't have a title in these modes, so fuck it. $DNA::Menuify::validate = 0; DNA::Menuify::write_file ("$dir$year/index.html", $output); } } write_years_index ($dir, $first_year2, $last_year); write_rss ($dir); write_names ($dir); } # Writes the HTML for the page listing all years (gallery/index.html). # sub write_years_index($$$) { my ($dir, $first, $last) = @_; $dir .= "/" unless ($dir =~ m@/$@); $dir = "" if $dir eq "./"; my $output = ""; my $page_title = "Photo Gallery"; my $td = ("TD VALIGN=TOP STYLE=\"font-size:larger; font-weight: bold;" . " padding: 0.1em 0.5em;\""); $output .= ("$page_title\n\n" . "\n" . "
$page_title
\n\n" . "

\n" . "
\n" . "\n" . " \n" . " <$td ALIGN=RIGHT>\n"); my $i = 0; foreach my $year ('1985', ($first .. $last)) { $output .= " \n <$td ALIGN=LEFT>\n" if ($i == int(($last-$first+1)/2)+1); $output .= " $year
\n"; $i++; } $output .= ( "\n" . " \n" . " \n" . " <$td ALIGN=CENTER COLSPAN=2>
\n" . "Latest\n" . "\n" . " \n" . " \n" . " <$td ALIGN=CENTER COLSPAN=2>
" . "". "Alphabetically
by performer
" . "\n" . "\n" . "
\n" . "
\n" . "\n"); my $outfile = "${dir}index.html"; DNA::Menuify::write_file ($outfile, $output); } # If this is a gallery that should have an RSS feed generated, save the imgs. # sub generate_rss($$$) { my ($file, $title, $body) = @_; $file =~ s@/[^/]*$@@s; foreach my $key (keys %rss_files) { if ($title =~ m/\b\Q$key\E\b/si) { my @files = (); $body =~ s%(]*>)%{ my $tag = $1; my ($img) = ($tag =~ m/\bSRC=\"([^<>\"]+)\"/si); if ($img =~ m/-thumb/s && $img !~ m@/@s) { my ($tw) = ($tag =~ m/\bmax-width: \s* (\d+) \s*px/six); my ($th) = ($tag =~ m/\bmax-height: \s* (\d+) \s*px/six); error ("unparsable w/h: $tag") unless ($tw && $th); my @D = ("$file/$img", $tw, $th); unshift @files, \@D; } }%gsexi; my $ref = $rss_data{$key}; my @L = ($ref ? @$ref : ()); push @L, @files; $rss_data{$key} = \@L; @L = @L[0 .. $rss_max_links] if ($#L >= $rss_max_links); } } } # Write out the gathered-up RSS data. # sub write_rss($) { my ($dir) = @_; foreach my $title (keys %rss_files) { my $file = $rss_files{$title}; my $ref = $rss_data{$title}; next unless $ref; my @imgs = @$ref; my $t2 = "DNA Lounge: Photos from $title"; my $rss = ' ' . $t2 . ' http://www.dnalounge.com/ Photos from recent ' . $title . ' events at DNA Lounge in San Francisco. en webmaster@dnalounge.com (DNA Lounge) webmaster@dnalounge.com (DNA Lounge) ' . $t2 . ' http://www.dnalounge.com/logo2.gif http://www.dnalounge.com/ 100 34 DNA Lounge '; foreach my $imgdata (@imgs) { my ($thumb, $tw, $th) = @$imgdata; my $file = $thumb; $file =~ s/-thumb\././s; $thumb = "http://www.dnalounge.com/$thumb"; my $img = $thumb; $img =~ s/-thumb\././s; my $href = $img; $href =~ s@(\.[a-z]+)$@.html@s; my ($tail) = ($img =~ m@/([^/]+)\.[^/]+$@s); my ($iw, $ih) = image_size ($file); my ($yyyy, $mm, $dd) = ($img =~ m@/(\d{4})/(\d\d)-(\d\d)@s); my $mmm = $months[$mm-1]; my $ddd = $days[dotw($dd, $mm, $yyyy)]; $mmm =~ s/^(...).*$/$1/; $ddd =~ s/^(...).*$/$1/; my $date = sprintf ("%s, %02d %s %d", $ddd, $dd, $mmm, $yyyy); $rss .= (" \n" . " DNA Lounge: $title: $date: $tail\n" . " $href\n" . " $href\n" . " " . "<A HREF=\"$href\">" . "<IMG SRC=\"$thumb\">" . "</A>" . "\n" . " \n" . " \n" . " \n"); } $rss .= " \n\n"; DNA::Menuify::write_file ("$dir$file", $rss); } } # Write out the gallery/names.txt file. # sub write_names($) { my ($dir) = @_; my @lines = (); foreach my $t (keys %xref_titles) { my $valP = $xrefs{lc($t)}; next unless defined($valP); $t = html_unquote ($t); push @lines, "$t\t" . join (' ', @$valP); } my $body = join("\n", sort { my ($aa, $bb) = (lc($a), lc($b)); $aa =~ s/^[^a-z]+//s; $bb =~ s/^[^a-z]+//s; $aa cmp $bb; } (@lines)) . "\n"; DNA::Menuify::write_file ("$gnames_file", $body); } sub error($) { my ($err) = @_; print STDERR "$progname: $err\n"; exit 1; } sub usage() { print STDERR "usage: $progname [--verbose] [--quiet] [--debug] [--all] " . "[--check] directory\n"; exit 1; } sub main() { my $dir = undef; my $wrap_images_p = 0; my $check_thumbs_p = 0; my $regal_p = 0; while ($_ = $ARGV[0]) { shift @ARGV; if (m/^--?verbose$/s) { $verbose++; } elsif (m/^-v+$/) { $verbose += length($_)-1; } elsif (m/^--?q(uiet)?$/s) { $verbose = 0; } elsif (m/^--?debug$/s) { $debug_p++; } elsif (m/^--?all$/s) { $wrap_images_p++; } elsif (m/^--?check$/s) { $check_thumbs_p++; } elsif (m/^--?regalleryize$/s) { $regal_p++; } elsif (m/^-./) { usage; } elsif (!defined($dir)) { $dir = $_; } else { usage; } } usage unless $dir; $dir =~ s@/+$@@; $DNA::Menuify::verbose = $verbose; $DNA::Menuify::debug = $debug_p; wrap_gallery ($dir, $wrap_images_p, $check_thumbs_p, $regal_p); } main(); exit 0;