diff options
author | Yorhel <git@yorhel.nl> | 2019-11-12 20:15:32 +0100 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2019-11-12 20:19:55 +0100 |
commit | 06bacb61526f3945520dd344821d2aa7b85a5f43 (patch) | |
tree | 81e7469ab7ac1c1e3e2a80f79cf49022083f89b6 /lib | |
parent | e525c5fb92612e5459ab04b1c010fb88a3dfa22a (diff) |
v2rw: Move entryLinks() into separate VNDB::ExtLinks module
This is yak shaving. The new module doesn't have much value as it is,
apart from having a central place to define link formats. This new
enrich_extlinks() approach is also a bit more efficient in that it can
avoid separate SQL queries for multiple objects.
But the real reason for these changes is that I can use that %LINKS hash
table to automatically generate the links part in the edit summary and
it should (hopefully) also be useful to generate a more
convenient/streamlined Elm edit form.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Multi/Wikidata.pm | 34 | ||||
-rw-r--r-- | lib/VNDB/DB/Releases.pm | 34 | ||||
-rw-r--r-- | lib/VNDB/ExtLinks.pm | 228 | ||||
-rw-r--r-- | lib/VNDB/Handler/Producers.pm | 11 | ||||
-rw-r--r-- | lib/VNDB/Handler/Releases.pm | 20 | ||||
-rw-r--r-- | lib/VNDB/Handler/Staff.pm | 9 | ||||
-rw-r--r-- | lib/VNDB/Handler/VNPage.pm | 13 | ||||
-rw-r--r-- | lib/VNDB/Util/Misc.pm | 85 | ||||
-rw-r--r-- | lib/VNWeb/DB.pm | 2 |
9 files changed, 269 insertions, 167 deletions
diff --git a/lib/Multi/Wikidata.pm b/lib/Multi/Wikidata.pm index a618f623..d54fbc8b 100644 --- a/lib/Multi/Wikidata.pm +++ b/lib/Multi/Wikidata.pm @@ -11,6 +11,7 @@ use Multi::Core; use JSON::XS 'decode_json'; use AnyEvent::HTTP; use VNDB::Config; +use VNDB::ExtLinks; my %C = ( @@ -56,35 +57,10 @@ sub fetch { } -my %props = ( - P856 => [ 'website', 'text' ], - P3180 => [ 'vndb', 'text' ], - P1933 => [ 'mobygames', 'text' ], - P4773 => [ 'mobygames_company', 'text' ], - P4769 => [ 'gamefaqs_game', 'integer' ], - P6182 => [ 'gamefaqs_company', 'integer' ], - P5646 => [ 'anidb_anime', 'integer' ], - P5649 => [ 'anidb_person', 'integer' ], - P1985 => [ 'ann_anime', 'integer' ], - P1984 => [ 'ann_manga', 'integer' ], - P434 => [ 'musicbrainz_artist', 'uuid' ], - P2002 => [ 'twitter', 'text' ], - P5659 => [ 'vgmdb_product', 'integer' ], - P3435 => [ 'vgmdb_artist', 'integer' ], - P1953 => [ 'discogs_artist', 'integer' ], - P7013 => [ 'acdb_char', 'integer' ], - P7017 => [ 'acdb_source', 'integer' ], - P6717 => [ 'indiedb_game', 'text' ], - P2816 => [ 'howlongtobeat', 'integer' ], - P4110 => [ 'crunchyroll', 'text' ], - P5794 => [ 'igdb_game', 'text' ], - P5247 => [ 'giantbomb', 'text' ], - P6337 => [ 'pcgamingwiki ', 'text' ], - P1733 => [ 'steam', 'integer' ], - P2725 => [ 'gog', 'text' ], - P5435 => [ 'pixiv_user', 'integer' ], - P7511 => [ 'doujinshi_author', 'integer' ], -); +# property_id -> [ column name, sql element type ] +my %props = + map +($VNDB::ExtLinks::WIKIDATA{$_}{property}, [ $_, $VNDB::ExtLinks::WIKIDATA{$_}{type} =~ s/\[\]$//r ]), + grep $VNDB::ExtLinks::WIKIDATA{$_}{property}, keys %VNDB::ExtLinks::WIKIDATA; sub process { diff --git a/lib/VNDB/DB/Releases.pm b/lib/VNDB/DB/Releases.pm index 1c95a3c0..9813029d 100644 --- a/lib/VNDB/DB/Releases.pm +++ b/lib/VNDB/DB/Releases.pm @@ -90,6 +90,9 @@ sub dbReleaseGet { r.notes r.catalog r.gtin r.resolution r.voiced r.freeware r.doujin r.uncensored r.ani_story r.ani_ero r.engine r.hidden r.locked | : (), $o{pid} ? ('rp.developer', 'rp.publisher') : (), + $o{what} =~ /links/ ? qw| + r.gtin r.l_steam r.l_gog r.l_gyutto r.l_digiket r.l_melon r.l_getchu r.l_getchudl r.l_dmm r.l_itch r.l_jastusa r.l_egs r.l_erotrail r.l_mg r.l_denpa r.l_jlist r.l_dlsite r.l_dlsiteen + | : () ); my $order = sprintf { @@ -129,6 +132,7 @@ sub dbReleaseGetRev { $select .= ', r.notes, r.catalog, r.gtin, r.resolution, r.voiced, r.freeware, r.doujin, r.uncensored, r.ani_story, r.ani_ero, r.engine, ro.hidden, ro.locked' if $o{what} =~ /extended/; $select .= ', extract(\'epoch\' from c.added) as added, c.comments, c.rev, c.ihid, c.ilock, '.VNWeb::DB::sql_user(); $select .= ', c.id AS cid, NOT EXISTS(SELECT 1 FROM changes c2 WHERE c2.type = c.type AND c2.itemid = c.itemid AND c2.rev = c.rev+1) AS lastrev'; + $select .= ', r.gtin, r.l_steam, r.l_gog, r.l_gyutto, r.l_digiket, r.l_melon, r.l_getchu, r.l_getchudl, r.l_dmm, r.l_itch, r.l_jastusa, r.l_egs, r.l_erotrail, r.l_mg, r.l_denpa, r.l_jlist, r.l_dlsite, r.l_dlsiteen' if $o{what} =~ /links/; my $r = $self->dbAll(q| SELECT !s @@ -204,36 +208,6 @@ sub _enrich { [ keys %r ] )}); } - - if($what =~ /links/) { - $r->[ delete $r{$_->{xid}} ] = { %{$r->[$r{ $_->{xid} }]}, %$_ } for (@{$self->dbAll(" - SELECT r.$colname AS xid, r.gtin, r.l_steam, r.l_gog, r.l_gyutto, r.l_digiket, r.l_melon, r.l_getchu, r.l_getchudl, r.l_dmm, r.l_itch, r.l_jastusa, l_egs, l_erotrail - , r.l_mg, smg.price AS l_mg_price, smg.r18 AS l_mg_r18 - , r.l_denpa, sdenpa.price AS l_denpa_price - , r.l_jlist, sjlist.price AS l_jlist_price, sjlist.jbox AS l_jlist_jbox - , r.l_dlsite, sdlsite.price AS l_dlsite_price, sdlsite.shop AS l_dlsite_shop - , r.l_dlsiteen, sdlsiteen.price AS l_dlsiteen_price, sdlsiteen.shop AS l_dlsiteen_shop - FROM releases$hist r - LEFT JOIN shop_denpa sdenpa ON sdenpa.id = r.l_denpa AND sdenpa.lastfetch IS NOT NULL AND sdenpa.deadsince IS NULL - LEFT JOIN shop_dlsite sdlsite ON sdlsite.id = r.l_dlsite AND sdlsite.lastfetch IS NOT NULL AND sdlsite.deadsince IS NULL - LEFT JOIN shop_dlsite sdlsiteen ON sdlsiteen.id = r.l_dlsiteen AND sdlsiteen.lastfetch IS NOT NULL AND sdlsiteen.deadsince IS NULL - LEFT JOIN shop_jlist sjlist ON sjlist.id = r.l_jlist AND sjlist.lastfetch IS NOT NULL AND sjlist.deadsince IS NULL - LEFT JOIN shop_mg smg ON smg.id = r.l_mg AND smg.lastfetch IS NOT NULL AND smg.deadsince IS NULL - WHERE r.$colname IN(!l)", - [ keys %r ] - )}); - - my %p = map { - $r->[$_]{l_playasia} = []; - $r->[$_]{gtin} ? ($r->[$_]{gtin}, $_) : () - } 0..$#$r; - if(keys %p) { - push(@{$r->[$p{$_->{gtin}}]{l_playasia}}, $_) for (@{$self->dbAll(" - SELECT gtin, price, url FROM shop_playasia WHERE gtin IN(!l) AND price <> ''", - [ keys %p ] - )}); - } - } } return wantarray ? ($r, $np) : $r; diff --git a/lib/VNDB/ExtLinks.pm b/lib/VNDB/ExtLinks.pm new file mode 100644 index 00000000..8e9bdf72 --- /dev/null +++ b/lib/VNDB/ExtLinks.pm @@ -0,0 +1,228 @@ +package VNDB::ExtLinks; + +use v5.26; +use warnings; +use VNDB::Config; +use Exporter 'import'; + +our @EXPORT = ('enrich_extlinks'); + + +# column name in wikidata table => \%info +# info keys: +# type SQL type, used by Multi to generate the proper SQL +# property Wikidata Property ID, used by Multi +# label How the link is displayed on the website +# fmt How to generate the url (printf-style string or subroutine returning the full URL) +our %WIKIDATA = ( + enwiki => { type => 'text', property => undef, label => 'Wikipedia (en)', fmt => sub { (shift =~ s/ /_/rg) =~ s/\?/%3f/rg } }, + jawiki => { type => 'text', property => undef, label => 'Wikipedia (ja)', fmt => sub { (shift =~ s/ /_/rg) =~ s/\?/%3f/rg } }, + website => { type => 'text[]', property => 'P856', label => undef, fmt => undef }, + vndb => { type => 'text[]', property => 'P3180', label => undef, fmt => undef }, + mobygames => { type => 'text[]', property => 'P1933', label => 'MobyGames', fmt => 'https://www.mobygames.com/game/%s' }, + mobygames_company => { type => 'text[]', property => 'P4773', label => 'MobyGames', fmt => 'https://www.mobygames.com/company/%s' }, + gamefaqs_game => { type => 'integer[]', property => 'P4769', label => 'GameFAQs', fmt => 'https://gamefaqs.gamespot.com/-/%s-' }, + gamefaqs_company => { type => 'integer[]', property => 'P6182', label => 'GameFAQs', fmt => 'https://gamefaqs.gamespot.com/company/%s-' }, + anidb_anime => { type => 'integer[]', property => 'P5646', label => undef, fmt => undef }, + anidb_person => { type => 'integer[]', property => 'P5649', label => 'AniDB', fmt => 'https://anidb.net/cr%s' }, + ann_anime => { type => 'integer[]', property => 'P1985', label => undef, fmt => undef }, + ann_manga => { type => 'integer[]', property => 'P1984', label => undef, fmt => undef }, + musicbrainz_artist => { type => 'uuid[]', property => 'P434', label => 'MusicBrainz', fmt => 'https://musicbrainz.org/artist/%s' }, + twitter => { type => 'text[]', property => 'P2002', label => 'Twitter', fmt => 'https://twitter.com/%s' }, + vgmdb_product => { type => 'integer[]', property => 'P5659', label => 'VGMdb', fmt => 'https://vgmdb.net/product/%s' }, + vgmdb_artist => { type => 'integer[]', property => 'P3435', label => 'VGMdb', fmt => 'https://vgmdb.net/artist/%s' }, + discogs_artist => { type => 'integer[]', property => 'P1953', label => 'Discogs', fmt => 'https://www.discogs.com/artist/%s' }, + acdb_char => { type => 'integer[]', property => 'P7013', label => undef, fmt => undef }, + acdb_source => { type => 'integer[]', property => 'P7017', label => 'ACDB', fmt => 'https://www.animecharactersdatabase.com/source.php?id=%s' }, + indiedb_game => { type => 'text[]', property => 'P6717', label => 'IndieDB', fmt => 'https://www.indiedb.com/games/%s' }, + howlongtobeat => { type => 'integer[]', property => 'P2816', label => 'HowLongToBeat', fmt => 'http://howlongtobeat.com/game.php?id=%s' }, + crunchyroll => { type => 'text[]', property => 'P4110', label => undef, fmt => undef }, + igdb_game => { type => 'text[]', property => 'P5794', label => 'IGDB', fmt => 'https://www.igdb.com/games/%s' }, + giantbomb => { type => 'text[]', property => 'P5247', label => undef, fmt => undef }, + pcgamingwiki => { type => 'text[]', property => 'P6337', label => undef, fmt => undef }, + steam => { type => 'integer[]', property => 'P1733', label => undef, fmt => undef }, + gog => { type => 'text[]', property => 'P2725', label => 'GOG', fmt => 'https://www.gog.com/game/%s' }, + pixiv_user => { type => 'integer[]', property => 'P5435', label => 'Pixiv', fmt => 'https://www.pixiv.net/member.php?id=%d' }, + doujinshi_author => { type => 'integer[]', property => 'P7511', label => 'Doujinshi.org', fmt => 'https://www.doujinshi.org/browse/author/%d/' }, +); + + +# dbentry_type => column name => \%info +# info keys: +# label Name of the link +# fmt How to generate a url (basic version, printf-style only) +# fmt2 How to generate a better url +# (printf-style string or subroutine, given a hashref of the DB entry and returning a new 'fmt' string) +# ("better" meaning proper store section, affiliate link) +our %LINKS = ( + v => { + l_renai => { label => 'Renai.us', fmt => 'https://renai.us/game/%s' }, + l_wikidata => { label => 'Wikidata', fmt => 'https://www.wikidata.org/wiki/Q%d' }, + # deprecated + l_wp => { label => 'Wikipedia', fmt => 'https://en.wikipedia.org/wiki/%s' }, + l_encubed => { label => 'Novelnews', fmt => 'http://novelnews.net/tag/%s/' }, + }, + r => { + website => { label => 'Official website', fmt => '%s' }, + l_egs => { label => 'ErogameScape', fmt => 'https://erogamescape.dyndns.org/~ap2/ero/toukei_kaiseki/game.php?game=%d' }, + l_erotrail => { label => 'ErogeTrailers', fmt => 'http://erogetrailers.com/soft/%d' }, + l_steam => { label => 'Steam', fmt => 'https://store.steampowered.com/app/%d/' }, + l_dlsite => { label => 'DLsite (jpn)', fmt => 'https://www.dlsite.com/home/work/=/product_id/%s.html' + , fmt2 => sub { sprintf config->{dlsite_url}, shift->{l_dlsite_shop}||'home' } }, + l_dlsiteen => { label => 'DLsite (eng)', fmt => 'https://www.dlsite.com/home/eng/=/product_id/%s.html' + , fmt2 => sub { sprintf config->{dlsite_url}, shift->{l_dlsiteen_shop}||'eng' } }, + l_gog => { label => 'GOG', fmt => 'https://www.gog.com/game/%s' }, + l_itch => { label => 'Itch.io', fmt => 'https://%s' }, + l_denpa => { label => 'Denpasoft', fmt => 'https://denpasoft.com/products/%s', fmt2 => config->{denpa_url} }, + l_jlist => { label => 'J-List', fmt => 'https://www.jlist.com/%s', fmt2 => sub { config->{ shift->{l_jlist_jbox} ? 'jbox_url' : 'jlist_url' } } }, + l_jastusa => { label => 'JAST USA', fmt => 'https://jastusa.com/%s' }, + l_gyutto => { label => 'Gyutto', fmt => 'https://gyutto.com/i/item%d' }, + l_digiket => { label => 'Digiket', fmt => 'https://www.digiket.com/work/show/_data/ID=ITM%07d/' }, + l_melon => { label => 'Melonbooks', fmt => 'https://www.melonbooks.com/index.php?main_page=product_info&products_id=IT%010d' }, + l_mg => { label => 'MangaGamer', fmt => 'https://www.mangagamer.com/r18/detail.php?product_code=%d' + , fmt2 => sub { config->{ !defined($_[0]{l_mg_r18}) || $_[0]{l_mg_r18} ? 'mg_r18_url' : 'mg_main_url' } } }, + l_getchu => { label => 'Getchu', fmt => 'http://www.getchu.com/soft.phtml?id=%d' }, + l_getchudl => { label => 'DL.Getchu', fmt => 'http://dl.getchu.com/i/item%d' }, + l_dmm => { label => 'DMM', fmt => 'https://%s' }, + }, + s => { + l_site => { label => 'Official website', fmt => '%s' }, + l_wikidata => { label => 'Wikidata', fmt => 'https://www.wikidata.org/wiki/Q%d' }, + l_twitter => { label => 'Twitter', fmt => 'https://twitter.com/%s' }, + l_anidb => { label => 'AniDB', fmt => 'https://anidb.net/cr%s' }, + l_pixiv => { label => 'Pixiv', fmt => 'https://www.pixiv.net/member.php?id=%d' }, + # deprecated + l_wp => { label => 'Wikipedia', fmt => 'https://en.wikipedia.org/wiki/%s' }, + }, + p => { + website => { label => 'Official website', fmt => '%s' }, + l_wikidata => { label => 'Wikidata', fmt => 'https://www.wikidata.org/wiki/Q%d' }, + # deprecated + l_wp => { label => 'Wikipedia', fmt => 'https://en.wikipedia.org/wiki/%s' }, + }, +); + + +# Fetch a list of links to display at the given database entries, adds the +# following field to each object: +# +# extlinks => [ +# [ $title, $url, $price ], +# .. +# ] +# +# (It also adds a few other fields in some cases, but you can ignore those) +sub enrich_extlinks { + my($type, @obj) = @_; + @obj = map ref $_ eq 'ARRAY' ? @$_ : ($_), @obj; + + my $l = $LINKS{$type} || die "DB entry type $type has no links"; + + my @w_ids = grep $_, map $_->{l_wikidata}, @obj; + my $w = @w_ids ? { map +($_->{id}, $_), $TUWF::OBJ->dbAlli('SELECT * FROM wikidata WHERE id IN', \@w_ids)->@* } : {}; + + # Fetch shop info for releases + if($type eq 'r') { + VNWeb::DB::enrich_merge(id => q{ + SELECT r.id + , smg.price AS l_mg_price, smg.r18 AS l_mg_r18 + , sdenpa.price AS l_denpa_price + , sjlist.price AS l_jlist_price, sjlist.jbox AS l_jlist_jbox + , sdlsite.price AS l_dlsite_price, sdlsite.shop AS l_dlsite_shop + , sdlsiteen.price AS l_dlsiteen_price, sdlsiteen.shop AS l_dlsiteen_shop + FROM releases r + LEFT JOIN shop_denpa sdenpa ON sdenpa.id = r.l_denpa AND sdenpa.lastfetch IS NOT NULL AND sdenpa.deadsince IS NULL + LEFT JOIN shop_dlsite sdlsite ON sdlsite.id = r.l_dlsite AND sdlsite.lastfetch IS NOT NULL AND sdlsite.deadsince IS NULL + LEFT JOIN shop_dlsite sdlsiteen ON sdlsiteen.id = r.l_dlsiteen AND sdlsiteen.lastfetch IS NOT NULL AND sdlsiteen.deadsince IS NULL + LEFT JOIN shop_jlist sjlist ON sjlist.id = r.l_jlist AND sjlist.lastfetch IS NOT NULL AND sjlist.deadsince IS NULL + LEFT JOIN shop_mg smg ON smg.id = r.l_mg AND smg.lastfetch IS NOT NULL AND smg.deadsince IS NULL + WHERE r.id IN}, + grep $_->{l_mg}||$_->{l_denpa}||$_->{l_jlist}||$_->{l_dlsite}||$_->{l_dlsiteen}, @obj + ); + VNWeb::DB::enrich(l_playasia => gtin => gtin => + "SELECT gtin, price, url FROM shop_playasia WHERE price <> '' AND gtin IN", + grep $_->{gtin}, @obj + ); + } + + for my $obj (@obj) { + my @links; + my sub w { + return if !$obj->{l_wikidata}; + my($v, $fmt, $label) = ($w->{$obj->{l_wikidata}}{$_[0]}, @{$WIKIDATA{$_[0]}}{'fmt', 'label'}); + push @links, map [ $label, ref $fmt ? $fmt->($_) : sprintf $fmt, $_ ], ref $v ? @$v : $v ? $v : () + } + my sub l { + my($f, $price) = @_; + my($v, $fmt, $fmt2, $label) = ($obj->{$f}, @{$l->{$f}}{'fmt', 'fmt2', 'label'}); + push @links, map [ $label, sprintf(ref $fmt2 ? $fmt2->($obj) : $fmt2 || $fmt, $_), $price ], ref $v ? @$v : $v ? $v : () + } + + l 'l_site'; + l 'website'; + w 'enwiki'; + w 'jawiki'; + l 'l_wikidata'; + + # VN links + if($type eq 'v') { + w 'mobygames'; + w 'gamefaqs_game'; + w 'vgmdb_product'; + w 'acdb_source'; + w 'indiedb_game'; + w 'howlongtobeat'; + w 'igdb_game'; + l 'l_renai'; + push @links, [ 'VNStat', sprintf 'https://vnstat.net/novel/%d', $obj->{id} ] if $obj->{c_votecount}>=20; + } + + # Release links + if($type eq 'r') { + l 'l_egs'; + l 'l_erotrail'; + l 'l_steam'; + push @links, [ 'SteamDB', sprintf 'https://steamdb.info/app/%d/info', $obj->{l_steam} ] if $obj->{l_steam}; + l 'l_dlsite', $obj->{l_dlsite_price}; + l 'l_dlsiteen', $obj->{l_dlsiteen_price}; + l 'l_gog'; + l 'l_itch'; + l 'l_denpa', $obj->{l_denpa_price}; + l 'l_jlist', $obj->{l_jlist_price}; + l 'l_jastusa'; + l 'l_gyutto'; + l 'l_digiket'; + l 'l_melon'; + l 'l_mg', $obj->{l_mg_price}; + l 'l_getchu'; + l 'l_getchudl'; + l 'l_dmm'; + push @links, map [ 'PlayAsia', $_->{url}, $_->{price} ], @{$obj->{l_playasia}} if $obj->{l_playasia}; + } + + # Staff links + if($type eq 's') { + l 'l_twitter'; w 'twitter' if !$obj->{l_twitter}; + l 'l_anidb'; w 'anidb_person' if !$obj->{l_anidb}; + l 'l_pixiv'; w 'pixiv_user' if !$obj->{l_pixiv}; + w 'musicbrainz_artist'; + w 'vgmdb_artist'; + w 'discogs_artist'; + w 'doujinshi_author'; + } + + # Producer links + if($type eq 'p') { + w 'twitter'; + w 'mobygames_company'; + w 'gamefaqs_company'; + w 'doujinshi_author'; + push @links, [ 'VNStat', sprintf 'https://vnstat.net/developer/%d', $obj->{id} ]; + } + + $obj->{extlinks} = \@links + } +} + + +1; diff --git a/lib/VNDB/Handler/Producers.pm b/lib/VNDB/Handler/Producers.pm index 75ca792f..7a1a287c 100644 --- a/lib/VNDB/Handler/Producers.pm +++ b/lib/VNDB/Handler/Producers.pm @@ -6,6 +6,7 @@ use warnings; use TUWF ':html', ':xml', 'xml_escape', 'html_escape'; use VNDB::Func; use VNDB::Types; +use VNDB::ExtLinks; TUWF::register( @@ -50,6 +51,7 @@ sub page { $rev ? ( rev => $rev ) : () )->[0]; return $self->resNotFound if !$p->{id}; + enrich_extlinks p => $p; my $metadata = { 'og:title' => $p->{name}, @@ -95,11 +97,10 @@ sub page { txt "a.k.a. $alias"; } - my $links = $self->entryLinks(p => $p); - br if @$links; - for(@$links) { + br if $p->{extlinks}->@*; + for($p->{extlinks}->@*) { a href => $_->[1], $_->[0]; - txt ' - ' if $_ ne $links->[$#$links]; + txt ' - ' if $_ ne $p->{extlinks}[$#{$p->{extlinks}}]; } end 'p'; @@ -138,6 +139,8 @@ sub _releases { # prodpage_(dev|pub) my $r = $self->dbReleaseGet(pid => $p->{id}, results => 999, what => 'vn platforms links'); + enrich_extlinks r => $r; + div class => 'mainbox'; a href => '#', id => 'expandprodrel', 'collapse'; h1 'Releases'; diff --git a/lib/VNDB/Handler/Releases.pm b/lib/VNDB/Handler/Releases.pm index 88e9bd47..4b850e58 100644 --- a/lib/VNDB/Handler/Releases.pm +++ b/lib/VNDB/Handler/Releases.pm @@ -6,6 +6,7 @@ use warnings; use TUWF ':html', ':xml', 'uri_escape', 'xml_escape'; use VNDB::Func; use VNDB::Types; +use VNDB::ExtLinks; use Exporter 'import'; our @EXPORT = ('releaseExtLinks'); @@ -33,6 +34,7 @@ sub page { $rev ? (rev => $rev) : (), )->[0]; return $self->resNotFound if !$r->{id}; + enrich_extlinks r => $r; my $metadata = { 'og:title' => $r->{title}, @@ -268,14 +270,13 @@ sub _infotable { end; } - my $links = $self->entryLinks(r => $r); - if(@$links) { + if($r->{extlinks}->@*) { Tr; td 'Links'; td; - for(@$links) { + for($r->{extlinks}->@*) { a href => $_->[1], $_->[0]; - txt ', ' if $_ ne $links->[$#$links]; + txt ', ' if $_ ne $r->{extlinks}[$#{$r->{extlinks}}]; } end; end; @@ -815,19 +816,18 @@ sub enginexml { } -# Generate the html for an 'external links' dropdown +# Generate the html for an 'external links' dropdown, assumes enrich_extlinks() has already been called on this object. sub releaseExtLinks { my($self, $r) = @_; - my $links = $self->entryLinks(r => $r); - my $has_dd = @$links > ($r->{website} ? 1 : 0); - if(@$links) { + my $has_dd = $r->{extlinks}->@* > ($r->{website} ? 1 : 0); + if($r->{extlinks}->@*) { a href => $r->{website}||'#', class => 'rllinks'; - txt scalar @$links if $has_dd; + txt scalar $r->{extlinks}->@* if $has_dd; cssicon 'external', 'External link'; end; if($has_dd) { ul class => 'hidden rllinks_dd'; - for (@$links) { + for ($r->{extlinks}->@*) { li; a href => $_->[1]; span $_->[2] if $_->[2]; diff --git a/lib/VNDB/Handler/Staff.pm b/lib/VNDB/Handler/Staff.pm index 1eb2f927..7da42e8d 100644 --- a/lib/VNDB/Handler/Staff.pm +++ b/lib/VNDB/Handler/Staff.pm @@ -6,6 +6,7 @@ use warnings; use TUWF qw(:html :xml uri_escape xml_escape); use VNDB::Func; use VNDB::Types; +use VNDB::ExtLinks; use List::Util qw(first); TUWF::register( @@ -25,6 +26,7 @@ sub page { $rev ? ( rev => $rev ) : () )->[0]; return $self->resNotFound if !$s->{id}; + enrich_extlinks s => $s; my $metadata = { 'og:title' => $s->{name}, @@ -92,14 +94,13 @@ sub page { end; end; } - my $links = $self->entryLinks(s => $s); - if(@$links) { + if($s->{extlinks}->@*) { Tr; td class => 'key', 'Links'; td; - for(@$links) { + for($s->{extlinks}->@*) { a href => $_->[1], $_->[0]; - br if $_ != $links->[$#$links]; + br if $_ != $s->{extlinks}[$#{$s->{extlinks}}]; } end; end; diff --git a/lib/VNDB/Handler/VNPage.pm b/lib/VNDB/Handler/VNPage.pm index e1e37f2f..4255a221 100644 --- a/lib/VNDB/Handler/VNPage.pm +++ b/lib/VNDB/Handler/VNPage.pm @@ -6,6 +6,7 @@ use warnings; use TUWF ':html', 'xml_escape'; use VNDB::Func; use VNDB::Types; +use VNDB::ExtLinks; use List::Util 'min'; use POSIX 'strftime'; @@ -341,6 +342,9 @@ sub page { my $r = $self->dbReleaseGet(vid => $vid, what => 'extended links vns producers platforms media', results => 200); + enrich_extlinks v => $v; + enrich_extlinks r => $r; + my $metadata = { 'og:title' => $v->{title}, 'og:description' => bb2text $v->{desc}, @@ -422,14 +426,13 @@ sub page { _producers($self, $r); _relations($self, $v) if @{$v->{relations}}; - my $links = $self->entryLinks(v => $v); - if(@$links) { + if($v->{extlinks}->@*) { Tr; td 'Links'; td; - for(@$links) { + for($v->{extlinks}->@*) { a href => $_->[1], $_->[0]; - txt ', ' if $_ ne $links->[$#$links]; + txt ', ' if $_ ne $v->{extlinks}[$#{$v->{extlinks}}]; } end; end; @@ -766,7 +769,7 @@ sub _affiliate_links { $rel->{type} eq 'partial' ? 2 : @{$rel->{vn}} > 1 ? 0 : 1; - for my $l (grep $_->[2], @{$self->entryLinks(r => $rel)}) { + for my $l (grep $_->[2], $rel->{extlinks}->@*) { $links{$l->[1]} = [ @$l, min $type, $links{$l->[1]}[3]||9 ]; } } diff --git a/lib/VNDB/Util/Misc.pm b/lib/VNDB/Util/Misc.pm index 77d61702..b314bf08 100644 --- a/lib/VNDB/Util/Misc.pm +++ b/lib/VNDB/Util/Misc.pm @@ -9,7 +9,7 @@ use VNDB::Func; use VNDB::Types; use VNDB::BBCode; -our @EXPORT = qw|filFetchDB filCompat bbSubstLinks entryLinks|; +our @EXPORT = qw|filFetchDB filCompat bbSubstLinks|; our %filfields = ( @@ -118,88 +118,5 @@ sub bbSubstLinks { } - -# Returns an arrayref of links, each link being [$title, $url, $price] -sub entryLinks { - my($self, $type, $obj) = @_; - my $w = $obj->{l_wikidata} ? $self->dbWikidata($obj->{l_wikidata}) : {}; - - my @links; - my $lnk = sub { - my($v, $title, $url, $xform, $price) = @_; - push @links, map [ $title, sprintf($url, $xform ? $xform->($_) : $_), $price ], ref $v ? @$v : $v ? ($v) : (); - }; - - $lnk->($obj->{l_site}, 'Official website', '%s'); # (staff) Homepage always comes first - $lnk->($obj->{website}, 'Official website', '%s'); # (producers, releases) - $lnk->($w->{enwiki}, 'Wikipedia (en)', 'https://en.wikipedia.org/wiki/%s', sub { (shift =~ s/ /_/rg) =~ s/\?/%3f/rg }); - $lnk->($w->{jawiki}, 'Wikipedia (ja)', 'https://ja.wikipedia.org/wiki/%s', sub { (shift =~ s/ /_/rg) =~ s/\?/%3f/rg }); - $lnk->($obj->{l_wikidata}, 'Wikidata', 'https://www.wikidata.org/wiki/Q%d'); - - # Not everything in the wikidata table is actually used, only those links that - # seem to be directly mappings (i.e. not displaying anime links on VN pages). - - # VN links - if($type eq 'v') { - $lnk->($w->{mobygames}, 'MobyGames', 'https://www.mobygames.com/game/%s'); - $lnk->($w->{gamefaqs_game}, 'GameFAQs', 'https://gamefaqs.gamespot.com/-/%s-'); - $lnk->($w->{vgmdb_product}, 'VGMdb', 'https://vgmdb.net/product/%s'); - $lnk->($w->{acdb_source}, 'ACDB', 'https://www.animecharactersdatabase.com/source.php?id=%s'); - $lnk->($w->{indiedb_game}, 'IndieDB', 'https://www.indiedb.com/games/%s'); - $lnk->($w->{howlongtobeat}, 'HowLongToBeat', 'http://howlongtobeat.com/game.php?id=%s'); - $lnk->($w->{igdb_game}, 'IGDB', 'https://www.igdb.com/games/%s'); - $lnk->($obj->{l_renai}, 'Renai.us', 'https://renai.us/game/%s'); - push @links, [ 'VNStat', sprintf 'https://vnstat.net/novel/%d', $obj->{id} ] if $obj->{c_votecount}>=20; - } - - # Release links - if($type eq 'r') { - $lnk->($obj->{l_egs}, 'ErogameScape', 'https://erogamescape.dyndns.org/~ap2/ero/toukei_kaiseki/game.php?game=%d'); - $lnk->($obj->{l_erotrail}, 'ErogeTrailers','http://erogetrailers.com/soft/%d'); - $lnk->($obj->{l_steam}, 'Steam', 'https://store.steampowered.com/app/%d/'); - $lnk->($obj->{l_steam}, 'SteamDB', 'https://steamdb.info/app/%d/info'); - $lnk->($obj->{l_dlsite}, 'DLsite (jpn)',sprintf($self->{dlsite_url}, $obj->{l_dlsite_shop}||'home'), undef, $obj->{l_dlsite_price}); - $lnk->($obj->{l_dlsiteen}, 'DLsite (eng)',sprintf($self->{dlsite_url}, $obj->{l_dlsiteen_shop}||'eng'), undef, $obj->{l_dlsiteen_price}); - $lnk->($obj->{l_gog}, 'GOG', 'https://www.gog.com/game/%s'); - $lnk->($obj->{l_itch}, 'Itch.io', 'https://%s'); - $lnk->($obj->{l_denpa}, 'Denpasoft', $self->{denpa_url}, undef, $obj->{l_denpa_price}); - $lnk->($obj->{l_jlist}, $obj->{l_jlist_jbox} ? 'JBOX' : 'J-List', $self->{ $obj->{l_jlist_jbox} ? 'jbox_url' : 'jlist_url' }, undef, $obj->{l_jlist_price}); - $lnk->($obj->{l_jastusa}, 'JAST USA', 'https://jastusa.com/%s'); - $lnk->($obj->{l_gyutto}, 'Gyutto', 'https://gyutto.com/i/item%d'); - $lnk->($obj->{l_digiket}, 'Digiket', 'https://www.digiket.com/work/show/_data/ID=ITM%07d/'); - $lnk->($obj->{l_melon}, 'Melonbooks', 'https://www.melonbooks.com/index.php?main_page=product_info&products_id=IT%010d'); - $lnk->($obj->{l_mg}, 'MangaGamer', !defined($obj->{l_mg_r18}) || $obj->{l_mg_r18} ? $self->{mg_r18_url} : $self->{mg_main_url}, undef, $obj->{l_mg_price}); - $lnk->($obj->{l_getchu}, 'Getchu', 'http://www.getchu.com/soft.phtml?id=%d'); - $lnk->($obj->{l_getchudl}, 'DL.Getchu', 'http://dl.getchu.com/i/item%d'); - $lnk->($obj->{l_dmm}, 'DMM', 'https://%s'); - push @links, map [ 'PlayAsia', $_->{url}, $_->{price} ], @{$obj->{l_playasia}} if $obj->{l_playasia}; - } - - # Staff links - if($type eq 's') { - $lnk->($obj->{l_twitter}, 'Twitter', 'https://twitter.com/%s'); - $lnk->($w->{twitter}, 'Twitter', 'https://twitter.com/%s') if !$obj->{l_twitter}; - $lnk->($obj->{l_anidb}, 'AniDB', 'https://anidb.net/cr%s'); - $lnk->($w->{anidb_person}, 'AniDB', 'https://anidb.net/cr%s') if !$obj->{l_anidb}; - $lnk->($obj->{l_pixiv}, 'Pixiv', 'https://www.pixiv.net/member.php?id=%d'); - $lnk->($w->{pixiv_user}, 'Pixiv', 'https://www.pixiv.net/member.php?id=%d') if !$obj->{l_pixiv}; - $lnk->($w->{musicbrainz_artist}, 'MusicBrainz', 'https://musicbrainz.org/artist/%s'); - $lnk->($w->{vgmdb_artist}, 'VGMdb', 'https://vgmdb.net/artist/%s'); - $lnk->($w->{discogs_artist}, 'Discogs', 'https://www.discogs.com/artist/%s'); - $lnk->($w->{doujinshi_author}, 'Doujinshi.org','https://www.doujinshi.org/browse/author/%d/'); - } - - # Producer links - if($type eq 'p') { - $lnk->($w->{twitter}, 'Twitter', 'https://twitter.com/%s'); - $lnk->($w->{mobygames_company}, 'MobyGames', 'https://www.mobygames.com/company/%s'); - $lnk->($w->{gamefaqs_company}, 'GameFAQs', 'https://gamefaqs.gamespot.com/company/%s-'); - $lnk->($w->{doujinshi_author}, 'Doujinshi.org','https://www.doujinshi.org/browse/author/%d/'); - push @links, [ 'VNStat', sprintf 'https://vnstat.net/developer/%d', $obj->{id} ]; - } - - \@links -} - 1; diff --git a/lib/VNWeb/DB.pm b/lib/VNWeb/DB.pm index 1583e731..e4905bf0 100644 --- a/lib/VNWeb/DB.pm +++ b/lib/VNWeb/DB.pm @@ -25,7 +25,7 @@ our @EXPORT = qw/ # (and who'd put effort into escaping strings when placeholders are easier?). sub interp_warn { my @r = sql_interp @_; - carp "Possible SQL injection in '$r[0]'" if tuwf->debug && $r[0] =~ /[2-9]/; # 0 and 1 aren't interesting, "SELECT 1" is a common pattern and so is "x > 0" + carp "Possible SQL injection in '$r[0]'" if tuwf->debug && $r[0] =~ /[2-9](?<!r18)/; # 0 and 1 aren't interesting, "SELECT 1" is a common pattern and so is "x > 0" return @r; } |