426 lines
15 KiB
PHP
426 lines
15 KiB
PHP
<?php
|
||
|
||
namespace Animarr;
|
||
|
||
|
||
use Animarr\Extractor\SceneExtractor;
|
||
use Animarr\Release\MultiRelease;
|
||
use Animarr\Release\Release;
|
||
|
||
class Database{
|
||
|
||
const FLAG_LOCKED = 0b1 << 0;
|
||
|
||
private $anidb;
|
||
private $db;
|
||
private $extractor;
|
||
|
||
private $cacheExists = [];
|
||
|
||
private static $config = [];
|
||
|
||
public static function parseConfig($path){
|
||
$lines = file($path);
|
||
while(count($lines) and ($line = array_shift($lines)) !== null){
|
||
$line = trim($line);
|
||
if($line === "" or $line{0} === "#"){
|
||
if(preg_match("/#include (.*\\.ini)/", $line, $matches) > 0){
|
||
$lines = array_merge($lines, file($matches[1]));
|
||
}
|
||
continue;
|
||
}
|
||
|
||
$d = explode("=", $line);
|
||
$key = trim(array_shift($d));
|
||
$value = trim(implode("=", $d));
|
||
|
||
switch ($value){
|
||
case "true":
|
||
case "on":
|
||
case "yes":
|
||
$value = true;
|
||
break;
|
||
case "false":
|
||
case "off":
|
||
case "no":
|
||
$value = false;
|
||
break;
|
||
}
|
||
|
||
if(preg_match("/^([^\\[\\]]+)\\[([^\\]=]*)\\]$/", $key, $matches) > 0){
|
||
if(!isset(self::$config[$matches[1]])){
|
||
self::$config[$matches[1]] = [];
|
||
}
|
||
if($matches[2] !== ""){
|
||
self::$config[$matches[1]][$matches[2]] = $value;
|
||
}else{
|
||
self::$config[$matches[1]][] = $value;
|
||
}
|
||
}else{
|
||
self::$config[$key] = $value;
|
||
}
|
||
}
|
||
}
|
||
|
||
public static function getConfigKey($key, $default = null){
|
||
return isset(self::$config[$key]) ? self::$config[$key] : $default;
|
||
}
|
||
|
||
public function __construct(AniDB $anidb){
|
||
$this->anidb = $anidb;
|
||
|
||
$this->extractor = new SceneExtractor($anidb);
|
||
|
||
$user = Database::getConfigKey("database.psql.user");
|
||
$pass = Database::getConfigKey("database.psql.password");
|
||
$host = Database::getConfigKey("database.psql.host");
|
||
$dbname = Database::getConfigKey("database.psql.dbname", "animarr");
|
||
$this->db = pg_connect("host=$host user=$user password=$pass dbname=$dbname");
|
||
|
||
|
||
//$this->isTracked = @json_decode(file_get_contents($this->path . "/track/all.json"), true);
|
||
//if(!is_array($this->isTracked)){
|
||
// $this->isTracked = [];
|
||
//}
|
||
}
|
||
|
||
public function getAniDB(){
|
||
return $this->anidb;
|
||
}
|
||
|
||
public function getTracked(){
|
||
$entries = [];
|
||
$q = pg_query($this->db, "SELECT * FROM tracked;");
|
||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||
$entries[$row["aid"]] = $row;
|
||
}
|
||
return $entries;
|
||
}
|
||
|
||
public function getAlternateTitles($aid = null){
|
||
$entries = [];
|
||
if($aid === null){
|
||
$q = pg_query($this->db, "SELECT * FROM alternate_titles;");
|
||
}else{
|
||
$q = pg_query_params($this->db, 'SELECT * FROM alternate_titles WHERE aid = $1;', [(int) $aid]);
|
||
}
|
||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||
$entries[] = $row;
|
||
}
|
||
|
||
return $entries;
|
||
}
|
||
|
||
public function addAlternateTitle($aid, $langCode, $title, $entryType = 2){
|
||
pg_query_params($this->db, 'INSERT INTO alternate_titles (aid, language, title, type) VALUES ($1, $2, $3, $4);', [$aid, $langCode, $title, $entryType]);
|
||
$this->anidb->addEntryName($aid, $langCode, $title, $entryType);
|
||
}
|
||
|
||
public function matchRelease(Release $release, $force = false){
|
||
$match = $this->anidb->matchRelease($release);
|
||
if($match !== null){
|
||
return ["aid" => $match["aid"], "new" => $this->addReleaseToDatabase($match["aid"], $release, $force)];
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
public function isReleaseInDatabase(Release $release){
|
||
if(isset($this->cacheExists[$release->getId()])){
|
||
return $this->cacheExists[$release->getId()];
|
||
}
|
||
return $this->cacheExists[$release->getId()] = pg_fetch_assoc(pg_query_params($this->db, 'SELECT COUNT(release_id) as count FROM releases WHERE release_id = $1;', [$release->getId()]))["count"] > 0;
|
||
}
|
||
|
||
private function addReleaseToDatabase($aid, Release $release, $force = false){
|
||
if($release->getParent() !== null){
|
||
return false;
|
||
}
|
||
|
||
if($release->getId() === null or $release->getId() === ""){
|
||
return false;
|
||
|
||
}
|
||
|
||
$isInDb = $this->isReleaseInDatabase($release);
|
||
|
||
if($isInDb){
|
||
if(!$force){
|
||
return false;
|
||
}
|
||
|
||
pg_query_params($this->db, 'DELETE FROM episodes_releases WHERE release_id = $1;', [$release->getId()]);
|
||
pg_query_params($this->db, 'INSERT INTO releases (release_id, aid, release_type, release_data, release_date) VALUES ($1, $2, $3, $4, $5) ON CONFLICT ON CONSTRAINT releases_pkey DO UPDATE SET aid = $2, release_type = $3, release_data = $4, release_date = $5;', [$release->getId(), $aid, $release->getType(), $release->encode(), $release->getUploadDate()]);
|
||
}else{
|
||
pg_query_params($this->db, 'INSERT INTO releases (release_id, aid, release_type, release_data, release_date) VALUES ($1, $2, $3, $4, $5) ON CONFLICT ON CONSTRAINT releases_pkey DO UPDATE SET aid = $2, release_type = $3, release_data = $4, release_date = $5;', [$release->getId(), $aid, $release->getType(), $release->encode(), $release->getUploadDate()]);
|
||
}
|
||
|
||
unset($this->cacheExists[$release->getId()]);
|
||
|
||
if($release instanceof MultiRelease){
|
||
foreach ($release->getContents($this->extractor) as $r){
|
||
$m = $this->anidb->matchRelease($r);
|
||
if($r->getType() === Release::TYPE_SINGLE and $r->getNumber() >= 0 and $m !== null/* and $m["aid"] === $aid*/){
|
||
pg_query_params($this->db, 'INSERT INTO episodes_releases (aid, episode, release_id, release_type, release_data) VALUES ($1, $2, $3, $4, $5) ON CONFLICT ON CONSTRAINT episodes_releases_pkey DO UPDATE SET aid = $1, episode = $2, release_id = $3, release_type = $4, release_data = $5;', [$m["aid"], $r->getNumber(), $r->getId(), $r->getType(), $r->encode()]);
|
||
}
|
||
}
|
||
}elseif($release->getType() === Release::TYPE_SINGLE and $release->getNumber() >= 0){
|
||
pg_query_params($this->db, 'INSERT INTO episodes_releases (aid, episode, release_id, release_type, release_data) VALUES ($1, $2, $3, $4, $5);', [$aid, $release->getNumber(), $release->getId(), $release->getType(), $release->encode()]);
|
||
}
|
||
|
||
return !$isInDb;
|
||
}
|
||
|
||
/**
|
||
* @param $aid
|
||
* @return Release[][]
|
||
*/
|
||
public function getAnimeEpisodes($aid){
|
||
$q = pg_query_params($this->db, 'SELECT * FROM episodes_releases WHERE aid = $1 AND release_type = $2;', [$aid, Release::TYPE_SINGLE]);
|
||
|
||
$episodes = [];
|
||
|
||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||
if(!isset($episodes[$row["episode"]])){
|
||
$episodes[$row["episode"]] = [];
|
||
}
|
||
|
||
$episodes[$row["episode"]][$row["release_id"]] = Release::undecode($row["release_data"]);
|
||
}
|
||
|
||
return $episodes;
|
||
}
|
||
|
||
/**
|
||
* @param $aid
|
||
* @return Release[]
|
||
*/
|
||
public function getRecentReleases($limit = 100, $aid = null, $type = null){
|
||
if($aid !== null){
|
||
if($type !== null){
|
||
$q = pg_query_params($this->db, 'SELECT * FROM releases WHERE aid = $1 AND release_type = $2 ORDER BY release_date DESC NULLS LAST LIMIT $3;', [$aid, $type, $limit]);
|
||
}else{
|
||
$q = pg_query_params($this->db, 'SELECT * FROM releases WHERE aid = $1 ORDER BY release_date DESC NULLS LAST LIMIT $2;', [$aid, $limit]);
|
||
}
|
||
}else{
|
||
$q = pg_query_params($this->db, 'SELECT * FROM releases ORDER BY release_date DESC NULLS LAST LIMIT $1;', [$limit]);
|
||
}
|
||
|
||
$releases = [];
|
||
|
||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||
$releases[$row["release_id"]] = Release::undecode($row["release_data"]);
|
||
}
|
||
|
||
return $releases;
|
||
}
|
||
|
||
/**
|
||
* @param $aid
|
||
* @return Release[]
|
||
*/
|
||
public function getAnimeBaseReleases($aid){
|
||
$q = pg_query_params($this->db, 'SELECT * FROM releases WHERE aid = $1;', [$aid]);
|
||
|
||
$releases = [];
|
||
|
||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||
$releases[$row["release_id"]] = Release::undecode($row["release_data"]);
|
||
}
|
||
|
||
return $releases;
|
||
}
|
||
|
||
/**
|
||
* @param $aid
|
||
* @return Release[]
|
||
*/
|
||
public function getAnimeReleases($aid){
|
||
$q = pg_query_params($this->db, 'SELECT * FROM episodes_releases WHERE aid = $1;', [$aid]);
|
||
|
||
$releases = [];
|
||
|
||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||
$releases[$row["release_id"]] = Release::undecode($row["release_data"]);
|
||
}
|
||
|
||
return $releases;
|
||
}
|
||
|
||
public function isTracking($aid){
|
||
return pg_fetch_assoc(pg_query_params($this->db, 'SELECT COUNT(aid) as count FROM tracked WHERE aid = $1;', [$aid]), null)["count"] > 0;
|
||
}
|
||
|
||
public function getTrackInfo($aid){
|
||
return pg_fetch_assoc(pg_query_params($this->db, 'SELECT * FROM tracked WHERE aid = $1;', [$aid]), null);
|
||
}
|
||
|
||
/**
|
||
* @param $aid
|
||
* @param $episode
|
||
* @return MultiRelease|Release|null
|
||
*/
|
||
public function getTrackEpisode($aid, $episode){
|
||
$row = pg_fetch_assoc(pg_query_params($this->db, 'SELECT * FROM tracked_releases WHERE aid = $1 AND episode = $2 AND release_type = $3;', [$aid, $episode, Release::TYPE_SINGLE]), null);
|
||
|
||
return $row !== false ? Release::undecode($row["release_data"]) : null;
|
||
}
|
||
|
||
/**
|
||
* @param $aid
|
||
* @return Release[]
|
||
*/
|
||
public function getTrackEpisodes($aid){
|
||
$q = pg_query_params($this->db, 'SELECT * FROM tracked_releases WHERE aid = $1 AND release_type = $2 ORDER BY episode ASC;', [$aid, Release::TYPE_SINGLE]);
|
||
|
||
$episodes = [];
|
||
|
||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||
if(isset($episodes[$row["episode"]])){
|
||
Downloader::log("Duplicate episode ".$row["episode"]." for tracked anidb-$aid! Some results might be truncated.", "WARNING");
|
||
$episodes[$row["episode"]] = [];
|
||
}
|
||
|
||
$episodes[$row["episode"]] = Release::undecode($row["release_data"]);
|
||
}
|
||
|
||
|
||
return $episodes;
|
||
}
|
||
|
||
/**
|
||
* @param $releaseId
|
||
* @return Release
|
||
*/
|
||
public function getRelease($releaseId){
|
||
$row = pg_fetch_assoc(pg_query_params($this->db, 'SELECT * FROM releases WHERE release_id = $1;', [$releaseId]), null);
|
||
if(!is_array($row)){
|
||
return null;
|
||
}
|
||
|
||
return Release::undecode($row["release_data"]);
|
||
}
|
||
|
||
/**
|
||
* @param $releaseId
|
||
* @param $episode
|
||
* @return Release
|
||
*/
|
||
public function getReleaseEpisode($releaseId, $episode){
|
||
$row = pg_fetch_assoc(pg_query_params($this->db, 'SELECT * FROM episodes_releases WHERE release_id = $1 AND episode = $2;', [$releaseId, $episode]), null);
|
||
if(!is_array($row)){
|
||
return null;
|
||
}
|
||
|
||
return Release::undecode($row["release_data"]);
|
||
}
|
||
|
||
public function clearTrackedEpisodes($aid){
|
||
pg_query_params($this->db, 'DELETE FROM tracked_releases WHERE aid = $1;', [$aid]);
|
||
}
|
||
|
||
public function clearEpisodes($aid){
|
||
pg_query_params($this->db, 'DELETE FROM episodes_releases WHERE aid = $1;', [$aid]);
|
||
pg_query_params($this->db, 'DELETE FROM releases WHERE aid = $1;', [$aid]);
|
||
}
|
||
|
||
public function saveTrackEpisode($aid, $episode, Release $release = null){
|
||
if($release === null){
|
||
pg_query_params($this->db, 'DELETE FROM tracked_releases WHERE aid = $1 AND episode = $2;', [$aid, $episode]);
|
||
}else{
|
||
if($this->getTrackEpisode($aid, $episode) !== null){ //Update
|
||
pg_query_params($this->db, 'UPDATE tracked_releases SET aid = $1, episode = $2, release_id = $3, release_type = $4, release_data = $5 WHERE aid = $1 AND episode = $2;', [$aid, $episode, $release->getId(), $release->getType(), $release->encode()]);
|
||
}else{
|
||
pg_query_params($this->db, 'INSERT INTO tracked_releases (aid, episode, release_id, release_type, release_data) VALUES ($1, $2, $3, $4, $5);', [$aid, $episode, $release->getId(), $release->getType(), $release->encode()]);
|
||
}
|
||
}
|
||
$this->updateTrackEpisodes($aid);
|
||
}
|
||
|
||
public function updateTrackEpisodes($aid, $number = null){
|
||
if($number === null){
|
||
pg_query_params($this->db, "UPDATE tracked SET episode_count = (SELECT COUNT(episode) FROM tracked_releases WHERE aid = $1) WHERE aid = $1;", [$aid]);
|
||
}else{
|
||
pg_query_params($this->db, "UPDATE tracked SET episode_count = $2 WHERE aid = $1;", [$aid, $number]);
|
||
}
|
||
|
||
return $this->getTrackInfo($aid)["episode_count"];
|
||
}
|
||
|
||
public static function cleanNameForFolder($name){
|
||
return str_replace(["../", "/..", "/", "\x00"], ["", "", "⁄", ""], $name);
|
||
}
|
||
|
||
public function addTrack($aid, $download_folder = ""){
|
||
if($download_folder === ""){
|
||
$download_folder = Database::getConfigKey("download.folder", './Download') . "/[Animarr] " . $this->cleanNameForFolder($this->getAniDB()->getAnime($aid)["title"]) . " [anidb-$aid]/";
|
||
}
|
||
if(!$this->isTracking($aid)){
|
||
pg_query_params($this->db, "INSERT INTO tracked (aid, episode_count, prefer_groups, download_folder) VALUES ($1, -1, '', $2);", [$aid, $download_folder]);
|
||
}
|
||
}
|
||
|
||
public function stopTrack($aid){
|
||
if($this->isTracking($aid)){
|
||
pg_query_params($this->db, "DELETE FROM tracked_releases WHERE aid = $1;", [$aid]);
|
||
pg_query_params($this->db, "DELETE FROM tracked WHERE aid = $1;", [$aid]);
|
||
$this->cacheExists = [];
|
||
}
|
||
}
|
||
|
||
public function getTrackGroups($aid){
|
||
if($this->isTracking($aid)){
|
||
return explode(",", base64_decode($this->getTrackInfo($aid)["prefer_groups"]));
|
||
}
|
||
|
||
return [];
|
||
}
|
||
|
||
public function getTrackFolder($aid, $default = ""){
|
||
if($this->isTracking($aid)){
|
||
$folder = $this->getTrackInfo($aid)["download_folder"];
|
||
if($folder != ""){
|
||
return $folder;
|
||
}
|
||
}
|
||
|
||
return $default;
|
||
}
|
||
|
||
public function setTrackFolder($aid, $folder){
|
||
pg_query_params($this->db, "UPDATE tracked SET download_folder = $1 WHERE aid = $2;", [$folder, $aid]);
|
||
}
|
||
|
||
public function getTrackFlag($aid, $flag){
|
||
if($this->isTracking($aid)){
|
||
return ($this->getTrackInfo($aid)["flags"] & $flag) != 0;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public function setTrackFlag($aid, $flag, $value){
|
||
if($this->isTracking($aid)){
|
||
$flags = ($this->getTrackInfo($aid)["flags"] & ~($value));
|
||
if($value){
|
||
$flags |= $flag;
|
||
}
|
||
|
||
pg_query_params($this->db, "UPDATE tracked SET flags = $1 WHERE aid = $2;", [$flags, $aid]);
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public function saveTrackGroups($aid, array $groups){
|
||
if($this->isTracking($aid)){
|
||
pg_query_params($this->db, "UPDATE tracked SET prefer_groups = $1 WHERE aid = $2;", [base64_encode(implode(",", $groups)), $aid]);
|
||
}
|
||
}
|
||
|
||
public function matchTitle($title){
|
||
return $this->anidb->matchTitle($title);
|
||
}
|
||
|
||
}
|