summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Multi/Wikidata.pm34
-rw-r--r--lib/VNDB/DB/Releases.pm34
-rw-r--r--lib/VNDB/ExtLinks.pm228
-rw-r--r--lib/VNDB/Handler/Producers.pm11
-rw-r--r--lib/VNDB/Handler/Releases.pm20
-rw-r--r--lib/VNDB/Handler/Staff.pm9
-rw-r--r--lib/VNDB/Handler/VNPage.pm13
-rw-r--r--lib/VNDB/Util/Misc.pm85
-rw-r--r--lib/VNWeb/DB.pm2
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;
}