diff options
Diffstat (limited to 'lib/VNWeb/Releases/Lib.pm')
-rw-r--r-- | lib/VNWeb/Releases/Lib.pm | 164 |
1 files changed, 123 insertions, 41 deletions
diff --git a/lib/VNWeb/Releases/Lib.pm b/lib/VNWeb/Releases/Lib.pm index 7b36a290..6688dedb 100644 --- a/lib/VNWeb/Releases/Lib.pm +++ b/lib/VNWeb/Releases/Lib.pm @@ -3,18 +3,55 @@ package VNWeb::Releases::Lib; use VNWeb::Prelude; use Exporter 'import'; -our @EXPORT = qw/enrich_release release_row_/; +our @EXPORT = qw/enrich_release_elm releases_by_vn enrich_release sort_releases release_row_/; + + +# Enrich a list of releases so that it's suitable as 'Releases' Elm response. +# Given objects must have 'id' and 'rtype' fields (appropriate for the VN in context). +sub enrich_release_elm { + enrich_merge id => sql('SELECT id, title[1+1] AS title, title[1+1+1+1] AS alttitle, released, reso_x, reso_y FROM', releasest, 'r WHERE id IN'), @_; + enrich_flatten lang => id => id => sub { sql('SELECT id, lang FROM releases_titles WHERE id IN', $_, 'ORDER BY lang') }, @_; + enrich_flatten platforms => id => id => sub { sql('SELECT id, platform FROM releases_platforms WHERE id IN', $_, 'ORDER BY platform') }, @_; +} + +# Return the list of releases associated with a VN in the format suitable as 'Releases' Elm response. +sub releases_by_vn { + my($id) = @_; + my $l = tuwf->dbAlli('SELECT r.id, rv.rtype FROM', releasest, 'r JOIN releases_vn rv ON rv.id = r.id WHERE NOT r.hidden AND rv.vid =', \$id, 'ORDER BY r.released, r.sorttitle, r.id'); + enrich_release_elm $l; + $l +} # Enrich a list of releases so that it's suitable for release_row_(). -# Assumption: Each release already has id, type, patch, released, gtin and enrich_extlinks(). +# Assumption: Each release already has id, patch, released, gtin and enrich_extlinks(). sub enrich_release { my($r) = @_; - enrich_merge id => 'SELECT id, title, original, notes, minage, freeware, doujin, reso_x, reso_y, voiced, ani_story, ani_ero, uncensored FROM releases WHERE id IN', $r; + enrich_merge id => sql( + 'SELECT id, title, olang, notes, minage, official, freeware, has_ero, reso_x, reso_y, voiced, uncensored + , ani_story, ani_ero, ani_story_sp, ani_story_cg, ani_cutscene, ani_ero_sp, ani_ero_cg, ani_face, ani_bg + FROM', releasest, 'r WHERE id IN'), $r; + enrich_merge id => sub { sql 'SELECT id, MAX(rtype) AS rtype FROM releases_vn WHERE id IN', $_, 'GROUP BY id' }, grep !$_->{rtype}, ref $r ? @$r : $r; enrich_merge id => sql('SELECT rid as id, status as rlist_status FROM rlists WHERE uid =', \auth->uid, 'AND rid IN'), $r if auth; - enrich_flatten lang => id => id => sub { sql 'SELECT id, lang FROM releases_lang WHERE id IN', $_, 'ORDER BY id, lang' }, $r; enrich_flatten platforms => id => id => sub { sql 'SELECT id, platform FROM releases_platforms WHERE id IN', $_, 'ORDER BY id, platform' }, $r; + enrich titles => id => id => sub { 'SELECT id, lang, mtl, title, latin FROM releases_titles WHERE id IN', $_, 'ORDER BY id, mtl, lang' }, $r; enrich media => id => id => sub { 'SELECT id, medium, qty FROM releases_media WHERE id IN', $_, 'ORDER BY id, medium' }, $r; + enrich drm => id => id => sub { 'SELECT r.id, r.drm, r.notes, d.name,', sql_comma(keys %DRM_PROPERTY), 'FROM releases_drm r JOIN drm d ON d.id = r.drm WHERE r.id IN', $_, 'ORDER BY r.id, r.drm' }, $r; +} + + +# Sort an array of releases, assumes the objects come from enrich_release() +# (Not always possible with an SQL ORDER BY due to rtype being context-dependent and platforms coming from other tables) +sub sort_releases { + return [ sort { + $a->{released} <=> $b->{released} || + $b->{rtype} cmp $a->{rtype} || + $b->{official} cmp $a->{official} || + $a->{patch} cmp $b->{patch} || + ($a->{platforms}[0]||'') cmp ($b->{platforms}[0]||'') || + $a->{title}[1] cmp $b->{title}[1] || + idcmp($a->{id}, $b->{id}) + } $_[0]->@* ]; } @@ -23,8 +60,8 @@ sub release_extlinks_ { return if !$r->{extlinks}->@*; if($r->{extlinks}->@* == 1 && $r->{website}) { - a_ href => $r->{website}, sub { - abbr_ class => 'icons external', title => 'Official website', ''; + a_ href => $r->{extlinks}[0]{url2}, sub { + abbr_ class => 'icon-external', title => 'Official website', ''; }; return } @@ -33,16 +70,18 @@ sub release_extlinks_ { div_ class => 'elm_dd', sub { a_ href => $r->{website}||'#', sub { txt_ scalar $r->{extlinks}->@*; - abbr_ class => 'icons external', title => 'External link', ''; + abbr_ class => 'icon-external', title => 'External link', ''; }; div_ sub { - ul_ sub { - li_ sub { - a_ href => $_->[1], sub { - span_ $_->[2] if length $_->[2]; - txt_ $_->[0]; - } - } for $r->{extlinks}->@*; + div_ sub { + ul_ sub { + li_ sub { + a_ href => $_->{url2}, sub { + span_ $_->{price} if length $_->{price}; + txt_ $_->{label}; + } + } for $r->{extlinks}->@*; + } } } } @@ -50,55 +89,98 @@ sub release_extlinks_ { } +# Options +# id: unique identifier if the same release may be listed on a page twice. +# lang: $lang, whether to display language icons and which language to use for the title and MTL flag. +# prod: 0/1 whether to display Pub/Dev indication sub release_row_ { - my($r, $id, $prodpage) = @_; + my($r, $opt) = @_; + + my $lang = $opt->{lang} && (grep $_->{lang} eq $opt->{lang}, $r->{titles}->@*)[0]; + my $mtl = $lang ? $lang->{mtl} : (grep $_->{mtl}, $r->{titles}->@*) == $r->{titles}->@*; + + my $storyani = join "\n", map "$_.", + $r->{ani_story} == 1 ? 'Not animated' : + defined $r->{ani_story_sp} || defined $r->{ani_story_cg} || defined $r->{ani_cutscene} || defined $r->{ani_bg} || defined $r->{ani_face} ? ( + defined $r->{ani_story_sp} ? fmtanimation $r->{ani_story_sp}, 'sprites' : (), + defined $r->{ani_story_cg} ? fmtanimation $r->{ani_story_cg}, 'CGs' : (), + defined $r->{ani_cutscene} ? fmtanimation $r->{ani_cutscene}, 'cutscenes' : (), + defined $r->{ani_bg} ? ($r->{ani_bg} ? 'Animated background effects' : 'No background effects') : (), + defined $r->{ani_face} ? ($r->{ani_face} ? 'Lip and/or eye movement' : 'No facial animations') : (), + ) : $ANIMATED{$r->{ani_story}}{txt}; + + my $eroani = join "\n", map "$_.", + $r->{ani_ero} == 1 ? 'Not animated' : + defined $r->{ani_ero_sp} || defined $r->{ani_ero_cg} ? ( + defined $r->{ani_ero_sp} ? fmtanimation $r->{ani_ero_sp}, 'sprites' : (), + defined $r->{ani_ero_cg} ? fmtanimation $r->{ani_ero_cg}, 'CGs' : (), + ) : $ANIMATED{$r->{ani_ero}}{txt}; my sub icon_ { my($img, $label, $class) = @_; - $class = $class ? " release_icon_$class" : ''; - img_ src => config->{url_static}."/f/$img.svg", class => "release_icons$class", title => $label; + $class = $class ? " icon-rel-$class" : ''; + abbr_ class => "icon-rel-$img$class", title => $label, ''; } my sub icons_ { my($r) = @_; - icon_ 'voiced', $VOICED{$r->{voiced}}{txt}, "voiced$r->{voiced}" if $r->{voiced}; - icon_ 'story_animated', "Story: $ANIMATED{$r->{ani_story}}{txt}", "anim$r->{ani_story}" if $r->{ani_story}; - icon_ 'ero_animated', "Ero: $ANIMATED{$r->{ani_ero}}{txt}", "anim$r->{ani_ero}" if $r->{ani_ero}; - icon_ 'free', 'Freeware' if $r->{freeware}; - icon_ 'nonfree', 'Non-free' if !$r->{freeware}; - icon_ 'doujin', 'Doujin' if !$r->{patch} && $r->{doujin}; - icon_ 'commercial', 'Commercial' if !$r->{patch} && !$r->{doujin}; + icon_ 'notes', bb_format $r->{notes}, text => 1 if $r->{notes}; + icon_ $MEDIUM{ $r->{media}[0]{medium} }{icon}, join ', ', map fmtmedia($_->{medium}, $_->{qty}), $r->{media}->@* if $r->{media}->@*; if($r->{reso_y}) { - my $type = $r->{reso_y} == 1 ? 'custom' : $r->{reso_x} / $r->{reso_y} > 4/3 ? '16-9' : '4-3'; + my $ratio = $r->{reso_x} / $r->{reso_y}; + my $type = $ratio == 4/3 ? '43' : $ratio == 16/9 ? '169' : 'custom'; # Ugly workaround: PC-98 has non-square pixels, thus not widescreen - $type = '4-3' if $type eq '16-9' && grep $_ eq 'p98', $r->{platforms}->@*; - icon_ "resolution_$type", resolution $r; + $type = '43' if $ratio > 4/3 && grep $_ eq 'p98', $r->{platforms}->@*; + icon_ "reso-$type", resolution $r; } - icon_ $MEDIUM{ $r->{media}[0]{medium} }{icon}, join ', ', map fmtmedia($_->{medium}, $_->{qty}), $r->{media}->@* if $r->{media}->@*; - icon_ 'uncensor', 'Uncensored' if $r->{uncensored}; - icon_ 'notes', bb2text $r->{notes} if $r->{notes}; + icon_ 'free', 'Freeware' if $r->{freeware}; + icon_ 'nonfree', 'Non-free' if !$r->{freeware}; + icon_ 'ani-ero', "Erotic scene animation:\n$eroani", "a$r->{ani_ero}" if $r->{ani_ero}; + icon_ 'ani-story', "Story scene animation:\n$storyani", "a$r->{ani_story}" if $r->{ani_story}; + icon_ 'voiced', $VOICED{$r->{voiced}}{txt}, "v$r->{voiced}" if $r->{voiced}; } - tr_ sub { + tr_ $mtl ? (class => 'mtl') : (), sub { td_ class => 'tc1', sub { rdate_ $r->{released} }; - td_ class => 'tc2', $r->{minage} < 0 ? '' : minage $r->{minage}; + td_ class => 'tc2', sub { + span_ class => 'releaseero releaseero_'.(!$r->{has_ero} ? 'no' : $r->{uncensored} ? 'unc' : defined $r->{uncensored} ? 'cen' : 'yes'), + title => !$r->{has_ero} ? 'No erotic scenes' : + $r->{uncensored} ? 'Contains uncensored erotic scenes' + : defined $r->{uncensored} ? 'Contains erotic scenes with optical censoring' : 'Contains erotic scenes', '♥'; + txt_ !$r->{minage} ? 'All' : minage $r->{minage} if defined $r->{minage}; + }; td_ class => 'tc3', sub { - abbr_ class => "icons $_", title => $PLATFORM{$_}, '' for grep $_ ne 'oth', $r->{platforms}->@*; - if($prodpage) { - abbr_ class => "icons lang $_", title => $LANGUAGE{$_}, '' for $r->{lang}->@*; + platform_ $_ for $r->{platforms}->@*; + if(!$opt->{lang}) { + abbr_ class => "icon-lang-$_->{lang}".($_->{mtl}?' mtl':''), title => $LANGUAGE{$_->{lang}}{txt}, '' for $r->{titles}->@*; } - abbr_ class => "icons rt$r->{type}", title => $r->{type}, ''; + abbr_ class => "icon-rt$r->{rtype}", title => $r->{rtype}, ''; }; td_ class => 'tc4', sub { - a_ href => "/r$r->{id}", title => $r->{original}||$r->{title}, $r->{title}; - b_ class => 'grayedout', ' (patch)' if $r->{patch}; + my $title = + $lang && defined $lang->{title} ? titleprefs_obj $lang->{lang}, [$lang] : + $lang ? titleprefs_obj $r->{olang}, [grep $_->{lang} eq $r->{olang}, $r->{titles}->@*] + : $r->{title}; + a_ href => "/$r->{id}", tattr $title; + my $note = join ' ', $r->{official} ? () : 'unofficial', $mtl ? 'machine translation' : (), $r->{patch} ? 'patch' : (); + small_ " ($note)" if $note; + if ($r->{drm}->@*) { + my($free,$drm); + for my $d ($r->{drm}->@*) { + ${ (grep $d->{$_}, keys %DRM_PROPERTY)[0] ? \$drm : \$free } = 1 + } + my $nfo = join "\n", map $_->{name}.($_->{notes} ? ' ('.bb_format($_->{notes}, text => 1).')' : ''), $r->{drm}->@*; + ($free && $drm ? \&span_ : $drm ? \&b_ : \&small_)->(title => $nfo, $free && !$drm ? ' (drm-free)' : ' (drm)'); + } }; td_ class => 'tc_icons', sub { icons_ $r }; - td_ class => 'tc_prod', join ' & ', $r->{publisher} ? 'Pub' : (), $r->{developer} ? 'Dev' : () if $prodpage; + td_ class => 'tc_prod', join ' & ', $r->{publisher} ? 'Pub' : (), $r->{developer} ? 'Dev' : () if $opt->{prod}; td_ class => 'tc5 elm_dd_left', sub { - elm_ 'UList.ReleaseEdit', $VNWeb::User::Lists::RLIST_STATUS, { rid => $r->{id}, uid => auth->uid, status => $r->{rlist_status}, empty => '--' } if auth; + elm_ 'UList.ReleaseEdit', $VNWeb::ULists::Elm::RLIST_STATUS, { rid => $r->{id}, uid => auth->uid, status => $r->{rlist_status}, empty => '--' } if auth; + }; + td_ class => 'tc6', sub { + release_extlinks_ $r, "$opt->{id}_$r->{id}" if $r->{patch} || $r->{official} || !grep $_->{mtl}, $r->{titles}->@*; }; - td_ class => 'tc6', sub { release_extlinks_ $r, "${id}_$r->{id}" }; } } |