diff options
author | Yorhel <git@yorhel.nl> | 2020-04-16 16:26:53 +0200 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2020-04-17 09:32:16 +0200 |
commit | 17445db2d7d7ce34616e21dd5cce6d8c81b66c21 (patch) | |
tree | a68822887460ee28ca2a72d0b334babc5898c479 /lib | |
parent | 86c0a244fa47a301c43e0750cedff494b0d0ef64 (diff) |
v2rw/VN::Page: Start conversion of VN pages
Currently implemented:
- Revision diff
- Info box & tags (already implemented from /v+/chars page)
- Stats
There's a few annoying TODO's left, this conversion will take some
effort.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/VNDB/Handler/VNPage.pm | 2 | ||||
-rw-r--r-- | lib/VNWeb/Prelude.pm | 9 | ||||
-rw-r--r-- | lib/VNWeb/VN/Page.pm | 176 |
3 files changed, 180 insertions, 7 deletions
diff --git a/lib/VNDB/Handler/VNPage.pm b/lib/VNDB/Handler/VNPage.pm index 30ddb43c..89ee33c9 100644 --- a/lib/VNDB/Handler/VNPage.pm +++ b/lib/VNDB/Handler/VNPage.pm @@ -15,7 +15,7 @@ TUWF::register( qr{v/rand} => \&rand, qr{v([1-9]\d*)/releases} => \&releases, qr{v([1-9]\d*)/staff} => sub { $_[0]->resRedirect("/v$_[1]#staff") }, - qr{v([1-9]\d*)(?:\.([1-9]\d*))?} => \&page, + qr{old/v([1-9]\d*)(?:\.([1-9]\d*))?} => \&page, ); diff --git a/lib/VNWeb/Prelude.pm b/lib/VNWeb/Prelude.pm index cd95c046..87810ac7 100644 --- a/lib/VNWeb/Prelude.pm +++ b/lib/VNWeb/Prelude.pm @@ -71,6 +71,7 @@ sub import { no strict 'refs'; *{$c.'::RE'} = *RE; *{$c.'::in'} = \∈ + *{$c.'::idcmp'} = \&idcmp; } @@ -113,4 +114,12 @@ sub in { 0 } + +# Compare two vndbids, using proper numeric order +sub idcmp($$) { + my($a1, $a2) = $_[0] =~ /^([a-z]+)([0-9]+)$/; + my($b1, $b2) = $_[1] =~ /^([a-z]+)([0-9]+)$/; + $a1 cmp $b1 || $a2 <=> $b2 +} + 1; diff --git a/lib/VNWeb/VN/Page.pm b/lib/VNWeb/VN/Page.pm index 89903d62..bdd7ff5a 100644 --- a/lib/VNWeb/VN/Page.pm +++ b/lib/VNWeb/VN/Page.pm @@ -1,6 +1,7 @@ package VNWeb::VN::Page; use VNWeb::Prelude; +use VNDB::Func 'fmtrating'; use POSIX 'strftime'; @@ -25,6 +26,20 @@ sub enrich_vn { } +# Enrich everything necessary for rev_() (that also needs enrich_vn()) +sub enrich_item { + my($v) = @_; + enrich_merge aid => 'SELECT id AS sid, aid, name, original FROM staff_alias WHERE aid IN', $v->{staff}, $v->{seiyuu}; + enrich_merge cid => 'SELECT id AS cid, name AS char_name, original AS char_original FROM chars WHERE id IN', $v->{seiyuu}; + + $v->{relations} = [ sort { $a->{vid} <=> $b->{vid} } $v->{relations}->@* ]; + $v->{anime} = [ sort { $a->{aid} <=> $b->{aid} } $v->{anime}->@* ]; + $v->{staff} = [ sort { $a->{aid} <=> $b->{aid} || $a->{role} cmp $b->{role} } $v->{staff}->@* ]; + $v->{seiyuu} = [ sort { $a->{aid} <=> $b->{aid} || $a->{cid} <=> $b->{cid} || $a->{note} cmp $b->{note} } $v->{seiyuu}->@* ]; + $v->{screenshots} = [ sort { idcmp($a->{scr}, $b->{scr}) } $v->{screenshots}->@* ]; +} + + sub og { my($v) = @_; +{ @@ -35,6 +50,44 @@ sub og { } +sub rev_ { + my($v) = @_; + revision_ v => $v, \&enrich_item, + [ title => 'Title (romaji)' ], + [ original => 'Original title' ], + [ alias => 'Alias' ], + [ desc => 'Description' ], + [ length => 'Length', fmt => \%VN_LENGTH ], + [ staff => 'Credits', fmt => sub { + a_ href => "/s$_->{sid}", title => $_->{original}||$_->{name}, $_->{name}; + txt_ " [$CREDIT_TYPE{$_->{role}}]"; + txt_ " [$_->{note}]" if $_->{note}; + }], + [ seiyuu => 'Seiyuu', fmt => sub { + a_ href => "/s$_->{sid}", title => $_->{original}||$_->{name}, $_->{name}; + txt_ ' as '; + a_ href => "/c$_->{cid}", title => $_->{char_original}||$_->{char_name}, $_->{char_name}; + txt_ " [$_->{note}]" if $_->{note}; + }], + [ relations => 'Relations', fmt => sub { + txt_ sprintf '[%s] %s: ', $_->{official} ? 'official' : 'unofficial', $VN_RELATION{$_->{relation}}{txt}; + a_ href => "/v$_->{vid}", title => $_->{original}||$_->{title}, $_->{title}; + }], + [ anime => 'Anime', fmt => sub { a_ href => "https://anidb.net/anime/$_->{aid}", "a$_->{aid}" }], + [ screenshots => 'Screenshots', fmt => sub { + txt_ '['; + a_ href => "/r$_->{rid}", "r$_->{rid}" if $_->{rid}; + txt_ 'no release' if !$_->{rid}; + txt_ '] '; + a_ href => tuwf->imgurl($_->{scr}), $_->{scr}; # TODO: Image viewer + txt_ $_->{nsfw} ? ' (Not safe)' : ' (Safe)'; + }], + [ image => 'Image', fmt => sub { a_ href => tuwf->imgurl($_), $_ } ], # TODO: Preview if SFW + [ img_nsfw => 'Image NSFW', fmt => sub { $_ ? 'Not safe' : 'Safe' } ], + revision_extlinks 'v' +} + + sub infobox_img_ { my($v) = @_; p_ 'No image uploaded yet.' if !$v->{image}; @@ -342,13 +395,103 @@ sub tabs_ { return if !$haschars && !auth->permEdit; div_ class => 'maintabs', sub { ul_ sub { - li_ class => (!$char ? ' tabselected' : ''), sub { a_ href => "/v$v->{id}#main", name => 'main', 'main' }; - li_ class => ($char ? ' tabselected' : ''), sub { a_ href => "/v$v->{id}/chars#chars", name => 'chars', 'characters' }; - } if $haschars; + if($haschars) { + li_ class => (!$char ? ' tabselected' : ''), sub { a_ href => "/v$v->{id}#main", name => 'main', 'main' }; + li_ class => ($char ? ' tabselected' : ''), sub { a_ href => "/v$v->{id}/chars#chars", name => 'chars', 'characters' }; + } + }; ul_ sub { - li_ sub { a_ href => "/v$v->{id}/add", 'add release' }; - li_ sub { a_ href => "/c/new?vid=$v->{id}", 'add character' }; - } if auth->permEdit; + if(auth->permEdit) { + li_ sub { a_ href => "/v$v->{id}/add", 'add release' }; + li_ sub { a_ href => "/c/new?vid=$v->{id}", 'add character' }; + } + }; + } +} + + +sub stats_ { + my($v) = @_; + + my $stats = tuwf->dbAlli(' + SELECT (uv.vote::numeric/10)::int AS idx, COUNT(uv.vote) as votes, SUM(uv.vote) AS total + FROM ulist_vns uv + WHERE uv.vote IS NOT NULL + AND NOT EXISTS(SELECT 1 FROM users u WHERE u.id = uv.uid AND u.ign_votes) + AND uv.vid =', \$v->{id}, ' + GROUP BY (uv.vote::numeric/10)::int' + ); + my $sum = sum map $_->{total}, @$stats; + my $max = max map $_->{votes}, @$stats; + my $num = sum map $_->{votes}, @$stats; + + my $recent = @$stats && tuwf->dbAlli(' + SELECT uv.vote,', sql_totime('uv.vote_date'), 'as date, ', sql_user(), ' + , NOT EXISTS(SELECT 1 FROM ulist_vns_labels uvl JOIN ulist_labels ul ON ul.uid = uvl.uid AND ul.id = uvl.lbl WHERE uvl.uid = uv.uid AND uvl.vid = uv.vid AND NOT ul.private) AS hide_list + FROM ulist_vns uv + JOIN users u ON u.id = uv.uid + WHERE uv.vid =', \$v->{id}, 'AND uv.vote IS NOT NULL + AND NOT EXISTS(SELECT 1 FROM users u WHERE u.id = uv.uid AND u.ign_votes) + ORDER BY uv.vote_date DESC + LIMIT', \8 + ); + + my $rank = $v->{c_votecount} && tuwf->dbRowi(' + SELECT c_rating, c_popularity + , (SELECT COUNT(*)+1 FROM vn iv WHERE NOT iv.hidden AND iv.c_popularity > COALESCE(v.c_popularity, 0.0)) AS pop_rank + , (SELECT COUNT(*)+1 FROM vn iv WHERE NOT iv.hidden AND iv.c_rating > COALESCE(v.c_rating, 0.0)) AS rating_rank + FROM vn v WHERE id =', \$v->{id} + ); + + my sub votestats_ { + table_ class => 'votegraph', sub { + thead_ sub { tr_ sub { td_ colspan => 2, 'Vote stats' } }; + tfoot_ sub { tr_ sub { td_ colspan => 2, sprintf '%d vote%s total, average %.2f (%s)', $num, $num == 1 ? '' : 's', $sum/$num/10, fmtrating(ceil($sum/$num/10-1)||1) } }; + tr_ sub { + my $num = $_; + my $votes = [grep $num == $_->{idx}, @$stats]->[0]{votes} || 0; + td_ class => 'number', $num; + td_ class => 'graph', sub { + div_ style => sprintf('width: %dpx', ($votes||0)/$max*250), ' '; + txt_ $votes||0; + }; + } for (reverse 1..10); + }; + + table_ class => 'recentvotes stripe', sub { + thead_ sub { tr_ sub { td_ colspan => 3, sub { + txt_ 'Recent votes'; + b_ sub { + txt_ '('; + a_ href => "/v$v->{id}/votes", 'show all'; + txt_ ')'; + } + } } }; + tr_ sub { + td_ sub { + b_ class => 'grayedout', 'hidden' if $_->{hide_list}; + user_ $_ if !$_->{hide_list}; + }; + td_ fmtvote $_->{vote}; + td_ fmtdate $_->{date}; + } for @$recent; + } if $recent && @$recent; + + clearfloat_; + div_ sub { + h3_ 'Ranking'; + p_ sprintf 'Popularity: ranked #%d with a score of %.2f', $rank->{pop_rank}, ($rank->{c_popularity}||0)*100; + p_ sprintf 'Bayesian rating: ranked #%d with a rating of %.2f', $rank->{rating_rank}, $rank->{c_rating}/10; + } if $v->{c_votecount}; + } + + div_ class => 'mainbox', sub { + h1_ 'User stats'; + if(!@$stats) { + p_ 'Nobody has voted on this visual novel yet...'; + } else { + div_ class => 'votestats', \&votestats_; + } } } @@ -393,6 +536,27 @@ sub chars_ { } +TUWF::get qr{/$RE{vrev}}, sub { + my $v = db_entry v => tuwf->capture('id'), tuwf->capture('rev'); + return tuwf->resNotFound if !$v; + + enrich_vn $v; + enrich_item $v; + + framework_ title => $v->{title}, index => !tuwf->capture('rev'), type => 'v', dbobj => $v, hiddenmsg => 1, og => og($v), + sub { + rev_ $v if tuwf->capture('rev'); + infobox_ $v; + tabs_ $v, 0; + # TODO: Releases + # TODO: Staff + # TODO: Character summary + stats_ $v; + # TODO: Screenshots + }; +}; + + TUWF::get qr{/$RE{vid}/chars}, sub { my $v = db_entry v => tuwf->capture('id'); return tuwf->resNotFound if !$v; |