summaryrefslogtreecommitdiff
path: root/lib/VNDB/ExtLinks.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/VNDB/ExtLinks.pm')
-rw-r--r--lib/VNDB/ExtLinks.pm228
1 files changed, 228 insertions, 0 deletions
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;