From b51533bf1461e8487018dc51a890309441fc51d4 Mon Sep 17 00:00:00 2001 From: dakkar Date: Tue, 18 Oct 2016 18:26:49 +0100 Subject: this may make sense --- lib/AniDB/Datastore.pm | 108 +++++++++++++++++++++++++++++++++++++++---------- lib/AniDB/Manager.pm | 48 +++++++++++++++++++++- 2 files changed, 134 insertions(+), 22 deletions(-) diff --git a/lib/AniDB/Datastore.pm b/lib/AniDB/Datastore.pm index 8db4e16..41602e6 100644 --- a/lib/AniDB/Datastore.pm +++ b/lib/AniDB/Datastore.pm @@ -3,7 +3,8 @@ use 5.024; use experimental 'signatures'; use Moo; use Path::Tiny; -use AniDB::Hashing; +use Try::Tiny; +use JSON::MaybeXS qw(encode_json decode_json); use namespace::clean; has database => ( is => 'ro', required => 1 ); @@ -22,23 +23,25 @@ sub _build_dbh($self) { ); } -sub update($self,$path) { - my $stat = $path->stat; - - return 0 unless $self->_has_changed($path,$stat); - my $hash = AniDB::Hashing::hash_fd($path->openr_raw); - $self->_update_path_info( - $path => { - size => $stat->size, - mtime => $stat->mtime, - hash => $hash, - }, - ); - return 1; +sub BUILD($self) { + try { + $self->dbh->selectall_arrayref('SELECT * FROM path_info LIMIT 1'); + } catch { + if (/\bno such table\b/) { + my @sql = do { local $/ = ";\n\n"; }; + $self->dbh->do($_) for @sql; + } + else { + die $_; + } + }; } -sub _has_changed($self,$path,$stat) { +sub has_changed($self,$path,$stat) { my $path_info = $self->path_info_for($path); + # if we don't know about the file, it's definitely changed! + return 1 unless $path_info; + return ( $path_info->{size} == $stat->size && $path_info->{mtime} == $stat->mtime @@ -76,22 +79,85 @@ sub path_info_for($self,$path) { return $ret->[0]; } +sub update_path_info($self,$path,$path_info) { + my @binds = (@{$path_info}{qw(size mtime hash)}, $path->stringify); + return $self->dbh->do( + qr{UPDATE path_info SET size=?, mtime=?, hash=? WHERE name=?}, + {}, + @binds, + ) or $self->dbh->do( + qr{INSERT INTO path_info(size,mtime,hash,name) VALUES (?,?,?,?)}, + {}, + @binds, + ); +} + sub episode_info_for($self,$args) { my $ret = $self->dbh->selectall_arrayref( - qr{SELECT * FROM episode_info WHERE hash=? AND size=?}, - { Slice => {} }, + qr{SELECT json FROM episode_info WHERE hash=? AND size=?}, + { }, @{$args}{qw(hash size)}, ); - return $ret->[0]; + 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 $self->dbh->do( + qr{UPDATE episode_info SET json=? WHERE size=? AND hash=?}, + {}, + @binds, + ) or $self->dbh->do( + qr{INSERT INTO episode_info(json,size,hash) VALUES (?,?,?)}, + {}, + @binds, + ); } sub anime_info_for($self,$args) { my $ret = $self->dbh->selectall_arrayref( - qr{SELECT * FROM anime_info WHERE aid=?}, - { Slice => {} }, + qr{SELECT json FROM anime_info WHERE aid=?}, + { }, @{$args}{qw(aid)}, ); - return $ret->[0]; + 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->{eid}); + return $self->dbh->do( + qr{UPDATE anime_info SET json=? WHERE eid=?}, + {}, + @binds, + ) or $self->dbh->do( + qr{INSERT INTO anime_info(json,eid) 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 ( + eid INT PRIMARY KEY, + json TEST NOT NULL +); diff --git a/lib/AniDB/Manager.pm b/lib/AniDB/Manager.pm index eeb724f..0539c07 100644 --- a/lib/AniDB/Manager.pm +++ b/lib/AniDB/Manager.pm @@ -4,12 +4,17 @@ use experimental 'signatures'; use Moo; use Log::Any '$log'; use Path::Tiny; +use AniDB::Hashing; use namespace::clean; has datastore => ( is => 'ro', required => 1, - handles => [qw(update)], +); + +has api_client => ( + is => 'ro', + required => 1, ); around BUILDARGS => sub($orig,$class,@args) { @@ -20,6 +25,12 @@ around BUILDARGS => sub($orig,$class,@args) { database => $db_name, }); } + if (my $username = $args->{username}) { + require AniDB::APIClient;; + $args->{api_client} = AniDB::APIClient->new({ + %{$args}{qw(username password)}, + }); + } return $args; }; @@ -35,6 +46,7 @@ sub _build_renamer { sub new_name_for($self,$path) { $path = path($path)->realpath; + $self->update($path); my $full_info = $self->datastore->full_info_for($path); return $self->renamer->new_name_for($path,$full_info); } @@ -61,4 +73,38 @@ sub rename($self,$path,$new_path) { } } +sub update($self,$path) { + $path = path($path)->realpath; + + my $stat = $path->stat; + + return unless $self->datastore->has_changed($path,$stat); + + my $hash = AniDB::Hashing::hash_fd($path->openr_raw); + + $self->datastore->update_path_info( + $path => my $path_info = { + size => $stat->size, + mtime => $stat->mtime, + hash => $hash, + }, + ); + + my $episode_info = $self->datastore->episode_info_for($path_info); + unless ($episode_info) { + $episode_info = $self->api_client->episode_info_for($path_info); + $self->datastore->update_episode_info( + $path_info, $episode_info, + ); + } + + my $anime_info = $self->datastore->anime_info_for($episode_info); + unless ($anime_info) { + $anime_info = $self->api_client->anime_info_for($episode_info); + $self->datastore->update_anime_info( + $episode_info, $anime_info, + ); + } +} + 1; -- cgit v1.2.3