Animarr/src/Animarr/Database.php

426 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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);
}
}