diff options
Diffstat (limited to 'lib/VNDB/Handler/VNPage.pm')
-rw-r--r-- | lib/VNDB/Handler/VNPage.pm | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/lib/VNDB/Handler/VNPage.pm b/lib/VNDB/Handler/VNPage.pm new file mode 100644 index 00000000..35a9ac8c --- /dev/null +++ b/lib/VNDB/Handler/VNPage.pm @@ -0,0 +1,484 @@ + +package VNDB::Handler::VNPage; + +use strict; +use warnings; +use YAWF ':html', 'xml_escape'; +use VNDB::Func; + + +YAWF::register( + qr{v([1-9]\d*)/rg} => \&rg, + qr{v([1-9]\d*)(?:\.([1-9]\d*))?} => \&page, +); + + +sub rg { + my($self, $vid) = @_; + + my $v = $self->dbVNGet(id => $vid, what => 'relgraph')->[0]; + return 404 if !$v->{id} || !$v->{rgraph}; + + $self->htmlHeader(title => 'Relation graph for '.$v->{title}); + $self->htmlMainTabs('v', $v, 'rg'); + div class => 'mainbox'; + h1 'Relation graph for '.$v->{title}; + lit $v->{cmap}; + p class => 'center'; + img src => sprintf('%s/rg/%02d/%d.png', $self->{url_static}, $v->{rgraph}%100, $v->{rgraph}), + alt => 'Relation graph for '.$v->{title}, usemap => '#rgraph'; + end; + end; +} + + +sub page { + my($self, $vid, $rev) = @_; + + my $v = $self->dbVNGet( + id => $vid, + what => 'extended categories anime relations screenshots'.($rev ? ' changes' : ''), + $rev ? (rev => $rev) : (), + )->[0]; + return 404 if !$v->{id}; + + my $r = $self->dbReleaseGet(vid => $vid, what => 'producers platforms'); + + $self->htmlHeader(title => $v->{title}, noindex => $rev); + $self->htmlMainTabs('v', $v); + return if $self->htmlHiddenMessage('v', $v); + + _revision($self, $v, $rev); + + div class => 'mainbox'; + $self->htmlItemMessage('v', $v); + h1 $v->{title}; + h2 class => 'alttitle', $v->{original} if $v->{original}; + + div class => 'vndetails'; + + # image + div class => 'vnimg'; + if(!$v->{image}) { + p 'No image uploaded yet'; + } elsif($v->{image} < 0) { + p '[processing image, please return in a few minutes]'; + } elsif($v->{img_nsfw} && !$self->authInfo->{show_nsfw}) { + img id => 'nsfw_hid', src => sprintf("%s/cv/%02d/%d.jpg", $self->{url_static}, $v->{image}%100, $v->{image}), alt => $v->{title}; + p id => 'nsfw_show'; + txt "This image has been flagged\nas Not Safe For Work.\n\n"; + a href => '#', 'Show me anyway'; + txt "\n\n(This warning can be disabled in your account)"; + end; + } else { + img src => sprintf("%s/cv/%02d/%d.jpg", $self->{url_static}, $v->{image}%100, $v->{image}), alt => $v->{title}; + i 'Flagged as NSFW' if $v->{img_nsfw} && $self->authInfo->{show_nsfw}; + } + end; + + # general info + table; + Tr; + td class => 'key', ' '; + td ' '; + end; + my $i = 0; + if($v->{length}) { + Tr ++$i % 2 ? (class => 'odd') : (); + td 'Length'; + td "$self->{vn_lengths}[$v->{length}][0] ($self->{vn_lengths}[$v->{length}][1])"; + end; + } + if($v->{alias}) { + Tr ++$i % 2 ? (class => 'odd') : (); + td 'Aliases'; + td $v->{alias}; + end; + } + my @links = ( + $v->{l_wp} ? [ 'Wikipedia', 'http://en.wikipedia.org/wiki/%s', $v->{l_wp} ] : (), + $v->{l_encubed} ? [ 'Encubed', 'http://novelnews.net/tag/%s/', $v->{l_encubed} ] : (), + $v->{l_renai} ? [ 'Renai.us', 'http://renai.us/game/%s.shtml', $v->{l_renai} ] : (), + $v->{l_vnn} ? [ 'V-N.net', 'http://visual-novels.net/vn/index.php?option=com_content&task=view&id=%d', $v->{l_vnn} ] : (), + ); + if(@links) { + Tr ++$i % 2 ? (class => 'odd') : (); + td 'Links'; + td; + for(@links) { + a href => sprintf($_->[1], $_->[2]), $_->[0]; + txt ', ' if $_ ne $links[$#links]; + } + end; + end; + } + + _producers($self, \$i, $r); + _categories($self, \$i, $v) if @{$v->{categories}}; + _relations($self, \$i, $v) if @{$v->{relations}}; + _anime($self, \$i, $v) if @{$v->{anime}}; + + # User options + if($self->authInfo->{id}) { + my $vote = $self->dbVoteGet(uid => $self->authInfo->{id}, vid => $v->{id})->[0]; + my $wish = $self->dbWishListGet(uid => $self->authInfo->{id}, vid => $v->{id})->[0]; + Tr ++$i % 2 ? (class => 'odd') : (); + td 'User options'; + td; + Select id => 'votesel'; + option $vote ? "your vote: $vote->{vote}" : 'not voted yet'; + optgroup label => $vote ? 'Change vote' : 'Vote'; + option value => $_, "$_ ($self->{votes}[$_-1])" for (reverse 1..10); + end; + option value => -1, 'revoke' if $vote; + end; + if(!$vote || $wish) { + br; + Select id => 'wishsel'; + option $wish ? "wishlist: $self->{wishlist_status}[$wish->{wstat}]" : 'not on your wishlist'; + optgroup label => $wish ? 'Change status' : 'Add to wishlist'; + option value => $_, $self->{wishlist_status}[$_] for (0..$#{$self->{wishlist_status}}); + end; + option value => -1, 'remove from wishlist'; + end; + } + end; + end; + } + + end; + end; + + # description + div class => 'vndescription'; + h2 'Description'; + p; + lit bb2html $v->{desc}; + end; + end; + end; + + _releases($self, $v, $r); + _stats($self, $v); + _screenshots($self, $v, $r) if @{$v->{screenshots}}; + + $self->htmlFooter; +} + + +sub _revision { + my($self, $v, $rev) = @_; + return if !$rev; + + my $prev = $rev && $rev > 1 && $self->dbVNGet( + id => $v->{id}, rev => $rev-1, what => 'extended categories anime relations screenshots changes' + )->[0]; + + $self->htmlRevision('v', $prev, $v, + [ title => 'Title (romaji)', diff => 1 ], + [ original => 'Original title', diff => 1 ], + [ alias => 'Alias', diff => 1 ], + [ desc => 'Description', diff => 1 ], + [ length => 'Length', serialize => sub { $self->{vn_lengths}[$_[0]][0] } ], + [ l_wp => 'Wikipedia link', htmlize => sub { + $_[0] ? sprintf '<a href="http://en.wikipedia.org/wiki/%s">%1$s</a>', xml_escape $_[0] : '[no link]' + }], + [ l_encubed => 'Encubed tag', htmlize => sub { + $_[0] ? sprintf '<a href="http://novelnews.net/tag/%s/">%1$s</a>', xml_escape $_[0] : '[no link]' + }], + [ l_renai => 'Renai.us link', htmlize => sub { + $_[0] ? sprintf '<a href="http://renai.us/game/%s.shtml">%1$s</a>', xml_escape $_[0] : '[no link]' + }], + [ l_vnn => 'V-N.net link', htmlize => sub { + $_[0] ? sprintf '<a href="http://visual-novels.net/vn/index.php?option=com_content&task=view&id=%d">%1$d</a>', xml_escape $_[0] : '[no link]' + }], + [ categories => 'Categories', join => ', ', split => sub { + my @r = map $self->{categories}{substr($_->[0],0,1)}[1]{substr($_->[0],1,2)}."($_->[1])", sort { $a->[0] cmp $b->[0] } @{$_[0]}; + return @r ? @r : ('[no categories selected]'); + }], + [ relations => 'Relations', join => '<br />', split => sub { + my @r = map sprintf('%s: <a href="/v%d" title="%s">%s</a>', + $self->{vn_relations}[$_->{relation}][0], $_->{id}, xml_escape($_->{original}||$_->{title}), xml_escape shorten $_->{title}, 40 + ), sort { $a->{id} <=> $b->{id} } @{$_[0]}; + return @r ? @r : ('[none]'); + }], + [ anime => 'Anime', join => ', ', split => sub { + my @r = map sprintf('<a href="http://anidb.net/a%d">a%1$d</a>', $_->{id}), sort { $a->{id} <=> $b->{id} } @{$_[0]}; + return @r ? @r : ('[none]'); + }], + [ screenshots => 'Screenshots', join => '<br />', split => sub { + my @r = map sprintf('[%s] <a href="%s/sf/%02d/%d.jpg" rel="iv:%dx%d">%4$d</a> (%s)', + $_->{rid} ? qq|<a href="/r$_->{rid}">r$_->{rid}</a>| : 'no release', + $self->{url_static}, $_->{id}%100, $_->{id}, $_->{width}, $_->{height}, $_->{nsfw} ? 'NSFW' : 'Safe' + ), @{$_[0]}; + return @r ? @r : ('[no screenshots]'); + }], + [ image => 'Image', htmlize => sub { + $_[0] > 0 ? sprintf '<img src="%s/cv/%02d/%d.jpg" />', $self->{url_static}, $_[0]%100, $_[0] : $_[0] < 0 ? '[processing]' : 'No image'; + }], + [ img_nsfw => 'Image NSFW', serialize => sub { $_[0] ? 'Not safe' : 'Safe' } ], + ); +} + + +sub _producers { + my($self, $i, $r) = @_; + return if !grep @{$_->{producers}}, @$r; + + my @lang; + for my $l (@$r) { + push @lang, $l->{language} if !grep $l->{language} eq $_, @lang; + } + + Tr ++$$i % 2 ? (class => 'odd') : (); + td 'Producers'; + td; + for my $l (@lang) { + my %p = map { $_->{id} => $_ } map @{$_->{producers}}, grep $_->{language} eq $l, @$r; + my @p = values %p; + next if !@p; + cssicon "lang $l", $self->{languages}{$l}; + for (@p) { + a href => "/p$_->{id}", title => $_->{original}||$_->{name}, shorten $_->{name}, 30; + txt ' & ' if $_ != $p[$#p]; + } + txt "\n"; + } + end; + end; +} + + +sub _categories { + my($self, $i, $v) = @_; + + # create an ordered list of selected categories in the form of: [ parent, [ p, sub, lvl ], .. ], .. + my @cat; + my %nolvl = (map {$_=>1} qw| pli pbr gaa gab hfa hfe |); + for my $cp (qw|e s g p h|) { + my $thisparent = 0; + my @sel = sort { $a->[0] cmp $b->[0] } grep substr($_->[0], 0, 1) eq $cp, @{$v->{categories}}; + if(@sel) { + push @cat, [ $self->{categories}{$cp}[0] ]; + push @{$cat[$#cat]}, map [ $cp, substr($_->[0],1,2), $nolvl{$_->[0]} ? 0 : $_->[1] ], @sel; + } + } + my @placetime = grep $_->[0] =~ /^[tl]/, @{$v->{categories}}; + if(@placetime) { + push @cat, [ 'Place/Time' ]; + push @{$cat[$#cat]}, map [ substr($_->[0],0,1), substr($_->[0],1,2), 0], sort { $a->[0] cmp $b->[0] } @placetime; + } + + # format & output categories + Tr ++$$i % 2 ? (class => 'odd') : (); + td 'Categories'; + td; + dl; + for (@cat) { + dt shift(@$_).':'; + dd; + lit join ', ', map qq|<i class="catlvl_$_->[2]">$self->{categories}{$_->[0]}[1]{$_->[1]}</i>|, @$_; + end; + } + end; + end; + end; +} + + +sub _relations { + my($self, $i, $v) = @_; + + my %rel; + push @{$rel{$_->{relation}}}, $_ + for (sort { $a->{title} cmp $b->{title} } @{$v->{relations}}); + + + Tr ++$$i % 2 ? (class => 'odd') : (); + td 'Relations'; + td class => 'relations'; + dl; + for(sort keys %rel) { + dt $self->{vn_relations}[$_][0]; + dd; + for (@{$rel{$_}}) { + a href => "/v$_->{id}", title => $_->{original}||$_->{title}, shorten $_->{title}, 40; + br; + } + end; + } + end; + end; + end; +} + + +sub _anime { + my($self, $i, $v) = @_; + + Tr ++$$i % 2 ? (class => 'odd') : (); + td 'Related anime'; + td class => 'anime'; + for (sort { ($a->{year}||9999) <=> ($b->{year}||9999) } @{$v->{anime}}) { + if($_->{lastfetch} < 1) { + b; + txt $_->{lastfetch} < 0 ? '[unknown anidb id: ' : '[no information available at this time: '; + a href => "http://anidb.net/a$_->{id}", $_->{id}; + txt ']'; + end; + } else { + b; + txt '['; + a href => "http://anidb.net/a$_->{id}", title => 'AniDB', 'DB'; + if($_->{nfo_id}) { + txt '-'; + a href => "http://animenfo.com/animetitle,$_->{nfo_id},a.html", title => 'AnimeNFO', 'NFO'; + } + if($_->{ann_id}) { + txt '-'; + a href => "http://www.animenewsnetwork.com/encyclopedia/anime.php?id=$_->{ann_id}", title => 'Anime News Network', 'ANN'; + } + txt '] '; + end; + acronym title => $_->{title_kanji}, shorten $_->{title_romaji}, 50; + b ' ('.($self->{anime_types}[$_->{type}][0] eq 'unknown' ? '' : $self->{anime_types}[$_->{type}][0].', ').$_->{year}.')'; + txt "\n"; + } + } + end; + end; +} + + +sub _releases { + my($self, $v, $r) = @_; + + div class => 'mainbox releases'; + a class => 'addnew', href => "/v$v->{id}/add", 'add release'; + h1 'Releases'; + if(!@$r) { + p 'We don\'t have any information about releases of this visual novel yet...'; + end; + return; + } + + if($self->authInfo->{id}) { + my $l = $self->dbVNListGet(uid => $self->authInfo->{id}, rid => [map $_->{id}, @$r]); + for my $i (@$l) { + [grep $i->{rid} == $_->{id}, @$r]->[0]{ulist} = $i; + } + } + + my @lang; + for my $l (@$r) { + push @lang, $l->{language} if !grep $l->{language} eq $_, @lang; + } + + table; + for my $l (@lang) { + Tr class => 'lang'; + td colspan => 6; + cssicon "lang $l", $self->{languages}{$l}; + txt $self->{languages}{$l}; + end; + end; + for my $rel (grep $l eq $_->{language}, @$r) { + Tr; + td class => 'tc1'; lit datestr $rel->{released}; end; + td class => 'tc2', $rel->{minage} < 0 ? '' : $self->{age_ratings}{$rel->{minage}}; + td class => 'tc3'; + for (sort @{$rel->{platforms}}) { + next if $_ eq 'oth'; + cssicon $_, $self->{platforms}{$_}; + } + cssicon lc(substr($self->{release_types}[$rel->{type}],0,3)), $self->{release_types}[$rel->{type}]; + end; + td class => 'tc4'; + a href => "/r$rel->{id}", title => $rel->{original}||$rel->{title}, $rel->{title}; + end; + td class => 'tc5'; + if($rel->{ulist}) { + a href => "/r$rel->{id}"; + lit liststat $rel->{ulist}; + end; + } else { + txt ' '; + } + end; + td class => 'tc6'; + if($rel->{website}) { + a href => $rel->{website}, rel => 'nofollow'; + cssicon 'ext', 'External link'; + end; + } else { + txt ' '; + } + end; + end; + } + } + end; + end; +} + + +sub _screenshots { + my($self, $v, $r) = @_; + div class => 'mainbox', id => 'screenshots'; + + if(grep $_->{nsfw}, @{$v->{screenshots}}) { + p class => 'nsfwtoggle'; + lit sprintf 'Showing <i id="nsfwshown">%d</i> out of %d screenshots, ', + $self->authInfo->{show_nsfw} ? scalar @{$v->{screenshots}} : scalar grep(!$_->{nsfw}, @{$v->{screenshots}}), + scalar @{$v->{screenshots}}; + a href => '#', id => "nsfwhide", 'show/hide NSFW'; + txt '.'; + end; + } + + h1 'Screenshots'; + table; + for my $rel (@$r) { + my @scr = grep $_->{rid} && $rel->{id} == $_->{rid}, @{$v->{screenshots}}; + next if !@scr; + Tr class => 'rel'; + td colspan => 5; + cssicon 'lang '.$rel->{language}, $self->{languages}{$rel->{language}}; + txt $rel->{title}; + end; + end; + Tr; + td class => 'scr'; + for (@scr) { + div $_->{nsfw} ? (class => 'nsfw'.(!$self->authInfo->{show_nsfw} ? ' hidden' : '')) : (); + a href => sprintf('%s/sf/%02d/%d.jpg', $self->{url_static}, $_->{id}%100, $_->{id}), + rel => "iv:$_->{width}x$_->{height}:scr", $_->{nsfw} && !$self->authInfo->{show_nsfw} ? (class => 'hidden') : (); + img src => sprintf('%s/st/%02d/%d.jpg', $self->{url_static}, $_->{id}%100, $_->{id}), alt => "Screenshot #$_->{id}"; + end; + end; + } + end; + end; + } + end; + end; +} + + +sub _stats { + my($self, $v) = @_; + + my $stats = $self->dbVoteStats(vid => $v->{id}); + div class => 'mainbox'; + h1 'User stats'; + if(!grep $_ > 0, @$stats) { + p "Nobody has voted on this visual novel yet..."; + } else { + $self->htmlVoteStats(v => $v, $stats); + } + end; +} + + +1; + |