Initial import
This commit is contained in:
commit
dd492db92e
37 changed files with 1953 additions and 0 deletions
80
lib/Pooru/API/V0.pm
Normal file
80
lib/Pooru/API/V0.pm
Normal file
|
@ -0,0 +1,80 @@
|
|||
package Pooru::API::V0;
|
||||
use Mojo::Base "Mojolicious", -signatures;
|
||||
|
||||
use DBD::SQLite::Constants ":dbd_sqlite_string_mode";
|
||||
use DBIx::Migration;
|
||||
use Pooru::Schema;
|
||||
|
||||
sub _search_opts ()
|
||||
{
|
||||
return {
|
||||
Media => {
|
||||
order_by => {-desc => "id"},
|
||||
rows => 12,
|
||||
},
|
||||
TagCountView => {
|
||||
order_by => [
|
||||
{-desc => [qw(count kind_id)]},
|
||||
{-asc => "name"},
|
||||
],
|
||||
rows => 1000,
|
||||
},
|
||||
TaggedMediaView_media => {
|
||||
order_by => {-desc => "media_id"},
|
||||
rows => 12,
|
||||
},
|
||||
TaggedMediaView_tags => {
|
||||
order_by => [
|
||||
{-desc => "tag_kind_id"},
|
||||
{-asc => "tag_name"},
|
||||
],
|
||||
rows => 1000,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
sub startup ($self)
|
||||
{
|
||||
$self->moniker("pooru-api-v0");
|
||||
|
||||
my $config = $self->plugin("Config");
|
||||
$self->secrets($config->{secrets});
|
||||
|
||||
$self->helper(schema => sub {
|
||||
state $schema = Pooru::Schema->connect(
|
||||
$config->{dsn},
|
||||
undef,
|
||||
undef,
|
||||
{
|
||||
sqlite_string_mode =>
|
||||
DBD_SQLITE_STRING_MODE_UNICODE_STRICT,
|
||||
on_connect_call => "use_foreign_keys",
|
||||
}
|
||||
);
|
||||
});
|
||||
$self->helper(pager => sub ($, $rs) {
|
||||
return map +($_ => ($rs->pager->$_ and int($rs->pager->$_))),
|
||||
qw(first_page previous_page current_page next_page
|
||||
last_page);
|
||||
});
|
||||
|
||||
DBIx::Migration->new({
|
||||
dsn => $config->{dsn},
|
||||
dir => $self->app->home->child("migrations")->to_string,
|
||||
})->migrate;
|
||||
|
||||
my $r = $self->routes;
|
||||
|
||||
$r->get("/meta")->to("meta#index")->name("meta");
|
||||
|
||||
$r->get("/media")->to("media#list")->name("list_media");
|
||||
$r->get("/media/<media_id:num>")->to("media#show")->name("show_media");
|
||||
|
||||
$r->get("/tag")->to("tags#show")->name("show_tag");
|
||||
$r->get("/tags")->to("tags#list")->name("list_tags");
|
||||
|
||||
$r->get("/random/media")->to("random#media")->name("random_media");
|
||||
$r->get("/random/tag")->to("random#tag")->name("random_tag");
|
||||
}
|
||||
|
||||
1;
|
120
lib/Pooru/API/V0/Controller/Media.pm
Normal file
120
lib/Pooru/API/V0/Controller/Media.pm
Normal file
|
@ -0,0 +1,120 @@
|
|||
package Pooru::API::V0::Controller::Media;
|
||||
use Mojo::Base "Mojolicious::Controller", -signatures;
|
||||
|
||||
my %search_opts = Pooru::API::V0::_search_opts->%*;
|
||||
|
||||
sub _list_no_tags ($self, $page)
|
||||
{
|
||||
my $paged_media = $self->schema->resultset("Media")
|
||||
->search(undef, $search_opts{Media})
|
||||
->page($page);
|
||||
my @media = map +{
|
||||
id => $_->id,
|
||||
storage_id => $_->storage_id,
|
||||
filename => $_->filename,
|
||||
content_type => $_->content_type,
|
||||
upload_datetime => $_->upload_datetime,
|
||||
}, $paged_media->all;
|
||||
|
||||
return $self->render(json => {
|
||||
media => [@media],
|
||||
pager => {$self->pager($paged_media)},
|
||||
});
|
||||
}
|
||||
|
||||
# Executes
|
||||
#
|
||||
# SELECT * FROM tagged_media_view
|
||||
# WHERE (tag_display = t_1 OR ... OR tag_display = t_N)
|
||||
# GROUP BY media_id HAVING COUNT(media_id) = N
|
||||
sub _list_with_tags ($self, $page, @tags)
|
||||
{
|
||||
my $attrs = {
|
||||
$search_opts{TaggedMediaView_media}->%*,
|
||||
|
||||
# Use "0 + ?" as otherwise a bare "?" interprets the value as
|
||||
# TEXT, breaking the functionality.
|
||||
having => \["COUNT(media_id) = 0 + ?", scalar @tags],
|
||||
group_by => "media_id",
|
||||
};
|
||||
|
||||
my $paged_media = $self->schema->resultset("TaggedMediaView")
|
||||
->search({tag_display => [@tags]}, $attrs)->page($page);
|
||||
my @media = map +{
|
||||
id => $_->media_id,
|
||||
storage_id => $_->media_storage_id,
|
||||
filename => $_->media_filename,
|
||||
content_type => $_->media_content_type,
|
||||
upload_datetime => $_->media_upload_datetime,
|
||||
}, $paged_media->all;
|
||||
|
||||
return $self->render(json => {
|
||||
media => [@media],
|
||||
pager => {$self->pager($paged_media)},
|
||||
});
|
||||
}
|
||||
|
||||
sub list ($self)
|
||||
{
|
||||
my $v = $self->validation;
|
||||
|
||||
my $page = $v->optional("page")->num(1, undef)->param // 1;
|
||||
return $self->render(
|
||||
json => {error => "Invalid page number."},
|
||||
status => 400,
|
||||
) if $v->has_error;
|
||||
|
||||
my @tags = split(" ", $v->optional("tags")->param // "");
|
||||
return $self->render(
|
||||
json => {error => "Invalid tags."},
|
||||
status => 400,
|
||||
) if $v->has_error;
|
||||
|
||||
return @tags == 0 ?
|
||||
$self->_list_no_tags($page) :
|
||||
$self->_list_with_tags($page, @tags);
|
||||
|
||||
}
|
||||
|
||||
sub show ($self)
|
||||
{
|
||||
my $v = $self->validation;
|
||||
|
||||
my $page = $v->optional("page")->num(1, undef)->param // 1;
|
||||
return $self->render(
|
||||
json => {error => "Invalid page number."},
|
||||
status => 400,
|
||||
) if $v->has_error;
|
||||
|
||||
my $media_id = $self->stash("media_id");
|
||||
my $media = $self->schema->resultset("Media")
|
||||
->single({id => $media_id});
|
||||
|
||||
return $self->render(
|
||||
json => {error => "Media not found"},
|
||||
status => 404,
|
||||
) if !defined($media);
|
||||
|
||||
my $paged_tags = $self->schema->resultset("TaggedMediaView")
|
||||
->search({media_id => $media_id},
|
||||
$search_opts{TaggedMediaView_tags})->page($page);
|
||||
my @tags = map +{
|
||||
id => $_->tag_id,
|
||||
name => $_->tag_name,
|
||||
kind_id => $_->tag_kind_id,
|
||||
display => $_->tag_display,
|
||||
count => $_->tag_count,
|
||||
}, $paged_tags->all;
|
||||
|
||||
return $self->render(json => {
|
||||
id => $media->id,
|
||||
storage_id => $media->storage_id,
|
||||
filename => $media->filename,
|
||||
content_type => $media->content_type,
|
||||
upload_datetime => $media->upload_datetime,
|
||||
tags => [@tags],
|
||||
pager => {$self->pager($paged_tags)},
|
||||
});
|
||||
}
|
||||
|
||||
1;
|
22
lib/Pooru/API/V0/Controller/Meta.pm
Normal file
22
lib/Pooru/API/V0/Controller/Meta.pm
Normal file
|
@ -0,0 +1,22 @@
|
|||
package Pooru::API::V0::Controller::Meta;
|
||||
use Mojo::Base "Mojolicious::Controller", -signatures;
|
||||
|
||||
sub index ($self)
|
||||
{
|
||||
return $self->render(json => {
|
||||
routes => [
|
||||
{ path => "/meta", verb => "GET" },
|
||||
|
||||
{ path => "/media", verb => "GET" },
|
||||
{ path => "/media/<media_id:num>", verb => "GET" },
|
||||
|
||||
{ path => "/tag", verb => "GET" },
|
||||
{ path => "/tags", verb => "GET" },
|
||||
|
||||
{ path => "/random/media", verb => "GET" },
|
||||
{ path => "/random/tag", verb => "GET" },
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
1;
|
35
lib/Pooru/API/V0/Controller/Random.pm
Normal file
35
lib/Pooru/API/V0/Controller/Random.pm
Normal file
|
@ -0,0 +1,35 @@
|
|||
package Pooru::API::V0::Controller::Random;
|
||||
use Mojo::Base "Mojolicious::Controller", -signatures;
|
||||
|
||||
sub _random_entry ($self, $rs)
|
||||
{
|
||||
return $self->schema->resultset($rs)
|
||||
->search(undef, {order_by => 'random()', rows => 1})->single;
|
||||
}
|
||||
|
||||
sub media ($self)
|
||||
{
|
||||
my $media = $self->_random_entry("Media");
|
||||
|
||||
return $self->render(
|
||||
json => {error => "Media not found."},
|
||||
status => 404,
|
||||
) if !defined($media);
|
||||
|
||||
return $self->redirect_to("show_media", media_id => $media->id);
|
||||
}
|
||||
|
||||
sub tag ($self)
|
||||
{
|
||||
my $tag = $self->_random_entry("Tag");
|
||||
|
||||
return $self->render(
|
||||
json => {error => "Tag not found."},
|
||||
status => 404,
|
||||
) if !defined($tag);
|
||||
|
||||
return $self->redirect_to(
|
||||
$self->url_for("show_tag")->query(id => $tag->id));
|
||||
}
|
||||
|
||||
1;
|
69
lib/Pooru/API/V0/Controller/Tags.pm
Normal file
69
lib/Pooru/API/V0/Controller/Tags.pm
Normal file
|
@ -0,0 +1,69 @@
|
|||
package Pooru::API::V0::Controller::Tags;
|
||||
use Mojo::Base "Mojolicious::Controller", -signatures;
|
||||
|
||||
my %search_opts = Pooru::API::V0::_search_opts->%*;
|
||||
|
||||
sub list ($self)
|
||||
{
|
||||
my $v = $self->validation;
|
||||
|
||||
my $page = $v->optional("page")->num(1, undef)->param // 1;
|
||||
return $self->render(
|
||||
json => {error => "Invalid page number."},
|
||||
status => 400,
|
||||
) if $v->has_error;
|
||||
|
||||
my $paged_tags = $self->schema->resultset("TagCountView")
|
||||
->search(undef, $search_opts{TagCountView})->page($page);
|
||||
my @tags = map +{
|
||||
id => $_->id,
|
||||
name => $_->name,
|
||||
kind_id => $_->kind_id,
|
||||
display => $_->display,
|
||||
count => $_->count,
|
||||
}, $paged_tags->all;
|
||||
|
||||
return $self->render(json => {
|
||||
tags => [@tags],
|
||||
pager => {$self->pager($paged_tags)},
|
||||
});
|
||||
}
|
||||
|
||||
sub show ($self)
|
||||
{
|
||||
my $v = $self->validation;
|
||||
|
||||
my $tag_id_or_name = $v->optional("id")->num(1, undef)->param //
|
||||
$v->optional("display")->param //
|
||||
$v->required("name")->param;
|
||||
return $self->render(
|
||||
json => {error => "Invalid tag ID or name."},
|
||||
status => 400,
|
||||
) if $v->has_error;
|
||||
|
||||
my %search = ($v->topic => $tag_id_or_name);
|
||||
|
||||
my $kind_id = $v->optional("kind_id")->param;
|
||||
return $self->render(
|
||||
json => {error => "Invalid kind ID."},
|
||||
status => 400,
|
||||
) if $v->has_error;
|
||||
$search{kind_id} = $kind_id if defined($kind_id);
|
||||
|
||||
my @tags = map +{
|
||||
id => $_->id,
|
||||
name => $_->name,
|
||||
kind_id => $_->kind_id,
|
||||
display => $_->display,
|
||||
count => $_->count,
|
||||
}, $self->schema->resultset("TagCountView")->search(\%search)->all;
|
||||
|
||||
return $self->render(
|
||||
json => {error => "Tag not found."},
|
||||
status => 404,
|
||||
) if @tags == 0;
|
||||
|
||||
return $self->render(json => {tags => [@tags]});
|
||||
}
|
||||
|
||||
1;
|
Loading…
Add table
Add a link
Reference in a new issue