package AniDB::Datastore;
use 5.024;
use Moo;
use experimental 'signatures';
use Try::Tiny;
use JSON::MaybeXS qw(encode_json decode_json);
use namespace::clean;
has database => ( is => 'ro', required => 1 );
has dbh => ( is => 'lazy' );
sub _build_dbh($self) {
require DBI;
my $dbh = DBI->connect(
'dbi:SQLite:dbname='.$self->database,
'', '',
{
AutoCommit => 1,
RaiseError => 1,
PrintError => 0,
},
);
try {
$dbh->selectall_arrayref('SELECT * FROM path_info LIMIT 1');
} catch {
if (/\bno such table\b/) {
my @sql = do { local $/ = ";\n\n"; <DATA> };
$dbh->do($_) for @sql;
}
else {
die $_;
}
};
return $dbh;
}
sub has_changed($self,$path,$stat) {
my $path_info = $self->path_info_for($path);
return 1 unless $path_info;
return not(
$path_info->{size} == $stat->size
&& $path_info->{mtime} == $stat->mtime
);
}
sub rename($self,$path,$new_path) {
$self->dbh->do(
q{UPDATE path_info SET name=? WHERE name=?},
{},
$new_path->stringify,$path->stringify,
);
}
sub full_info_for($self,$path) {
my $path_info = $self->path_info_for($path);
my $episode_info = $self->episode_info_for(
$path_info,
);
my $anime_info = $self->anime_info_for(
$episode_info,
);
return {
path => $path_info,
episode => $episode_info,
anime => $anime_info,
};
}
sub path_info_for($self,$path) {
my $ret = $self->dbh->selectall_arrayref(
q{SELECT * FROM path_info WHERE name=?},
{ Slice => {} },
$path->stringify,
);
return unless $ret && $ret->[0];
return $ret->[0];
}
sub update_path_info($self,$path,$path_info) {
my @binds = (@{$path_info}{qw(size mtime hash)}, $path->stringify);
return 0+$self->dbh->do(
q{UPDATE path_info SET size=?, mtime=?, hash=? WHERE name=?},
{},
@binds,
) || 0+$self->dbh->do(
q{INSERT INTO path_info(size,mtime,hash,name) VALUES (?,?,?,?)},
{},
@binds,
);
}
sub episode_info_for($self,$args) {
my $ret = $self->dbh->selectall_arrayref(
q{SELECT json FROM episode_info WHERE hash=? AND size=?},
{ },
@{$args}{qw(hash size)},
);
return unless $ret && $ret->[0];
return decode_json($ret->[0][0]);
}
sub update_episode_info($self,$path_info, $episode_info) {
my $json = encode_json($episode_info);
my @binds = ($json, @{$path_info}{qw(size hash)});
return 0+$self->dbh->do(
q{UPDATE episode_info SET json=? WHERE size=? AND hash=?},
{},
@binds,
) || 0+$self->dbh->do(
q{INSERT INTO episode_info(json,size,hash) VALUES (?,?,?)},
{},
@binds,
);
}
sub anime_info_for($self,$episode_info) {
my $ret = $self->dbh->selectall_arrayref(
q{SELECT json FROM anime_info WHERE aid=?},
{ },
@{$episode_info}{qw(aid)},
);
return unless $ret && $ret->[0];
return decode_json($ret->[0][0]);
}
sub update_anime_info($self,$episode_info, $anime_info) {
my $json = encode_json($anime_info);
my @binds = ($json, $episode_info->{aid});
return 0+$self->dbh->do(
q{UPDATE anime_info SET json=? WHERE aid=?},
{},
@binds,
) || 0+$self->dbh->do(
q{INSERT INTO anime_info(json,aid) VALUES (?,?)},
{},
@binds,
);
}
1;
__DATA__
CREATE TABLE path_info (
name TEXT PRIMARY KEY,
mtime INT NOT NULL,
size INT NOT NULL,
hash TEXT NOT NULL
);
CREATE TABLE episode_info (
size INT NOT NULL,
hash TEXT NOT NULL,
json TEST NOT NULL,
PRIMARY KEY (size,hash)
);
CREATE TABLE anime_info (
aid INT PRIMARY KEY,
json TEST NOT NULL
);