summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2022-11-16 16:55:42 +0100
committerYorhel <git@yorhel.nl>2022-11-16 17:01:25 +0100
commit06a3e3b28bf79d9023423e2a07c0498f99d5eb6c (patch)
treef73636c9b53089715e3b1c6309678ac1d4c7385c
parent33c83c7d94857df03b4872388b570cf3a04dad7c (diff)
API2: Document the release `extlink` filter + add id/url lookup
-rw-r--r--data/api-kana.md19
-rw-r--r--lib/VNWeb/AdvSearch.pm67
2 files changed, 75 insertions, 11 deletions
diff --git a/data/api-kana.md b/data/api-kana.md
index b9a4cf85..90a1b4d3 100644
--- a/data/api-kana.md
+++ b/data/api-kana.md
@@ -600,6 +600,8 @@ Name [F] Description
Otherwise, this matches the `rtype` of any linked
visual novel.
+`extlink` m Match on external links, see below for details.
+
`patch` Integer, only accepts the value `1`.
`freeware` See `patch`.
@@ -617,7 +619,22 @@ Name [F] Description
matching the given [producer filters](#producer-filters).
-----------------------------------------------------------------------------
-*Undocumented: animation, extlinks*
+The `extlink` filter can be used with three types of values:
+
+- Just a site name, e.g. `["extlink","=","steam"]` matches all releases that
+ have a steam ID.
+- A two-element array indicating the site name and the remote identifier, e.g.
+ `["extlink","=",["steam",702050]]` to match the Saya no Uta release on Steam.
+ The second element can be either an int or a string, depending on the site,
+ but integer identifiers are also accepted when formatted as a string.
+- A URL, e.g. `["extlink","=","https://store.steampowered.com/app/702050/"]` is
+ equivalent to the above example.
+
+In all of the above forms, an error is returned if the site is not known in the
+database or if the URL format is not recognized. The list of supported sites
+and URL formats tends to change over time.
+
+*Undocumented: animation*
### Fields {#release-fields}
diff --git a/lib/VNWeb/AdvSearch.pm b/lib/VNWeb/AdvSearch.pm
index 4cd0fcf5..7a3d78a5 100644
--- a/lib/VNWeb/AdvSearch.pm
+++ b/lib/VNWeb/AdvSearch.pm
@@ -392,16 +392,7 @@ f r => 18 => 'rlist', { uint => 1, enum => \%RLIST_STATUS }, sql_list => sub
return '1=0' if !auth;
sql 'r.id', $neg ? 'NOT' : '', 'IN(SELECT rid FROM rlists WHERE uid =', \auth->uid, 'AND status IN', $val, $all && @$val > 1 ? ('GROUP BY rid HAVING COUNT(status) =', \scalar @$val) : (), ')';
};
-f r => 19 => 'extlink', { enum => [map s/^l_//r, keys $VNDB::ExtLinks::LINKS{r}->%*] }, '=' => sub {
- my $arg = $_;
- state $schema = (grep +($_->{dbentry_type}||'') eq 'r', values VNDB::Schema::schema->%*)[0];
- state %L = map {
- my($f, $n, $p) = ($_, s/^l_//r, $VNDB::ExtLinks::LINKS{r}{$_});
- my($s) = grep $_->{name} eq $f, $schema->{cols}->@*;
- +($n, 'r.'.$f.' <> '.($s->{type} =~ /\[\]/ ? "'{}'" : $s->{type} =~ /^(big)?int/ ? 0 : "''"))
- } keys $VNDB::ExtLinks::LINKS{r}->%*;
- $L{$arg} // $L{"l_$arg"};
- };
+f r => 19 => 'extlink', _extlink_filter('r');
f r => 61 => 'patch', { uint => 1, range => [1,1] }, '=' => sub { 'r.patch' };
f r => 62 => 'freeware', { uint => 1, range => [1,1] }, '=' => sub { 'r.freeware' };
f r => 64 => 'uncensored',{uint => 1, range => [1,1] }, '=' => sub { 'r.uncensored' };
@@ -484,6 +475,61 @@ f i => 2 => 'id', { vndbid => 'i' }, sql => sub { sql 't.id', $_[0], \$_
f i => 80 => 'search', {}, '=' => sub { sql 't.c_search LIKE ALL (search_query(', \$_, '))' };
+
+# 'extlink' filter accepts the following values:
+# - $name - Whether the entry has a link of site $name
+# - [ $name, $val ] - Whether the entry has a link of site $name with the given $val
+# - "$name,$val" - Compact version of above (not really *compact* by any means, but this filter isn't common anyway)
+# - "http://..." - Auto-detect version of [$name,$val]
+# TODO: This only handles links defined in %LINKS, but it would be nice to also support links from Wikidata & PlayAsia.
+sub _extlink_filter {
+ my($type) = @_;
+ my $schema = (grep +($_->{dbentry_type}||'') eq $type, values VNDB::Schema::schema->%*)[0];
+ my %links = map {
+ my $n = $_;
+ my $l = $VNDB::ExtLinks::LINKS{$type}{$n};
+ my $s = (grep $_->{name} eq $n, $schema->{cols}->@*)[0];
+ (s/^l_//r, +{ %$l,
+ _col => $n,
+ _schema => $s,
+ _regex => $l->{regex} && VNDB::ExtLinks::full_regex($l->{regex}),
+ _empty => $s->{type} =~ /\[\]/ ? "'{}'" : $s->{type} =~ /^(big)?int/i ? 0 : "''"
+ })
+ } keys $VNDB::ExtLinks::LINKS{$type}->%*;
+
+ my sub _val {
+ return 1 if !ref $_[0] && $links{$_[0]}; # just $name
+ if(!ref $_[0] && $_[0] =~ /^https?:/) { # URL
+ for (keys %links) {
+ if($links{$_}{_regex} && $_[0] =~ $links{$_}{_regex}) {
+ $_[0] = [ $_, $1 ];
+ last;
+ }
+ }
+ return { msg => 'Unrecognized URL format' } if !ref $_[0];
+ }
+ $_[0] = [ split /,/, $_[0], 2 ] if !ref $_[0]; # compact $name,$val form
+
+ # normalized $name,$val form
+ return 0 if ref $_[0] ne 'ARRAY' || $_[0]->@* != 2 || ref $_[0][0] || ref $_[0][1] || !defined $_[0][1];
+ my $l = $links{$_[0][0]};
+ return { msg => "Unknown field '$_[0][0]'" } if !$l;
+ return { msg => "Invalid value '$_[0][1]'" } if $l->{_schema}{type} =~ /^int/i && ($_[0][1] !~ /^-?[0-9]+$/ || $_[0][1] >= (1<<31) || $_[0][1] <= -(1<<31));
+ return { msg => "Invalid value '$_[0][1]'" } if $l->{_schema}{type} =~ /^bigint/i && ($_[0][1] !~ /^-?[0-9]+$/ || $_[0][1] >= (1<<63) || $_[0][1] <= -(1<<63));
+ $_[0][1] *= 1 if $l->{_schema}{type} =~ /^(big)?int/i;
+ 1
+ }
+
+ my sub _sql {
+ return "$type.$links{$_}{_col} <> $links{$_}{_empty}" if !ref; # just name
+ my($l, $v) = ($links{$_->[0]}, $_->[1]);
+ sql "$type.$l->{_col}", $l->{_schema}{type} =~ /\[\]/ ? ('&& ARRAY[', \$v, ']::', $l->{_schema}{type}) : ('=', \$v);
+ }
+ my sub _comp { ref $_ ? $_->[0].','.(my $x=$_->[1]) : $_ }
+ ({ type => 'any', func => \&_val }, '=' => \&_sql, compact => \&_comp)
+}
+
+
# Accepts either:
# - $tag
# - [$tag, $exclude_lies*16*3 + int($minlevel*5)*3 + $maxspoil] (compact form)
@@ -597,6 +643,7 @@ sub _validate_adv {
}
+
# 'advsearch' validation, accepts either a compact encoded string, JSON string or an already decoded array.
TUWF::set('custom_validations')->{advsearch} = sub { my($t) = @_; +{ required => 0, type => 'any', default => bless({type=>$t}, __PACKAGE__), func => sub { _validate_adv $t, @_ } } };