314 lines
9.6 KiB
PHP
314 lines
9.6 KiB
PHP
<?php
|
|
|
|
namespace Animarr;
|
|
|
|
use Animarr\Release\Release;
|
|
use Animarr\Request;
|
|
|
|
class AniDB{
|
|
|
|
const ANIDB_API_ENDPOINT = "http://api.anidb.net:9001/httpapi";
|
|
const ANIDB_API_CLIENT = "animarr";
|
|
const ANIDB_API_CLIENTVER = 2;
|
|
const ANIDB_API_PROTOVER = 1;
|
|
|
|
private static $ANIDB_API_LAST_REQUEST = 0;
|
|
|
|
public function __construct($path, $force = false){
|
|
$this->initCache($path, $force);
|
|
$this->loadExceptions();
|
|
}
|
|
|
|
public function getAnimeAPI($aid, $fetch = true, $force = false){
|
|
if(!$force and file_exists(Database::getConfigKey("database.api.folder", "./api") . "/$aid.xml")){
|
|
$str = file_get_contents(Database::getConfigKey("database.api.folder", "./api") . "/$aid.xml");
|
|
if(trim($str) != "" and trim($str) != '<error code="500">banned</error>'){
|
|
return $str;
|
|
}
|
|
}
|
|
|
|
if(!$fetch){
|
|
return null;
|
|
}
|
|
|
|
if(self::$ANIDB_API_LAST_REQUEST >= time() - 2){
|
|
sleep(2);
|
|
}
|
|
$xml = Request::getURL(self::ANIDB_API_ENDPOINT . "?request=anime&client=" . self::ANIDB_API_CLIENT . "&clientver=" . self::ANIDB_API_CLIENTVER . "&protover=" . self::ANIDB_API_PROTOVER ."&aid=" . $aid);
|
|
file_put_contents(Database::getConfigKey("database.api.folder", "./api") . "/$aid.xml", $xml);
|
|
self::$ANIDB_API_LAST_REQUEST = time();
|
|
return $xml;
|
|
}
|
|
|
|
public function getAnimeData($aid){
|
|
$xml = new \SimpleXMLElement($this->getAnimeAPI($aid, true, false));
|
|
$cacheTime = filemtime(Database::getConfigKey("database.api.folder", "./api") . "/$aid.xml");
|
|
$startTime = \DateTime::createFromFormat('Y-m-d', strval($xml->startdate));
|
|
$endTime = isset($xml->enddate) ? \DateTime::createFromFormat('Y-m-d', strval($xml->enddate)) : PHP_INT_MAX;
|
|
$startTime = $startTime instanceof \DateTime ? $startTime->getTimestamp() : 0;
|
|
$endTime = $endTime instanceof \DateTime ? $endTime->getTimestamp() : PHP_INT_MAX;
|
|
if(
|
|
$endTime >= (time() - (Database::getConfigKey("database.api.enddelay", 336) * 3600))
|
|
and
|
|
(time() - $cacheTime) >= (Database::getConfigKey("database.api.refreshdelay", 48) * 3600)
|
|
){
|
|
$xml = new \SimpleXMLElement($this->getAnimeAPI($aid, true, true));
|
|
}
|
|
return $xml;
|
|
}
|
|
|
|
private function loadExceptions(){
|
|
foreach(Database::getConfigKey("anidb.exceptions", "[]") as $exception){
|
|
$exception = json_decode($exception);
|
|
if(!isset($this->exceptions[$exception->aid])){
|
|
$this->exceptions[$exception->aid] = [];
|
|
}
|
|
|
|
$this->exceptions[$exception->aid][] = $exception;
|
|
}
|
|
}
|
|
|
|
public function matchRelease(Release $release){
|
|
$match = $this->matchTitle($release->getTitle());
|
|
if($match !== null and isset($this->exceptions[$match["aid"]])){
|
|
foreach($this->exceptions[$match["aid"]] as $exception){
|
|
if(isset($exception->group) and $exception->group !== $release->getGroup()){
|
|
continue;
|
|
}
|
|
|
|
if(isset($exception->title) and $exception->title !== $release->getTitle()){
|
|
continue;
|
|
}
|
|
|
|
$match = $this->getAnime($exception->newAid);
|
|
}
|
|
}
|
|
|
|
return $match;
|
|
}
|
|
|
|
public function getAll(){
|
|
return $this->db;
|
|
}
|
|
|
|
public function getAllMatches($title, $limit = 20){
|
|
$ids = [];
|
|
$ids2 = [];
|
|
$cleanTitle = strtolower($title);
|
|
|
|
foreach ($this->titleToId as $t => $aid){
|
|
$lev = levenshtein($cleanTitle, $t);
|
|
if($lev < 0 or isset($ids[$aid]) and $ids[$aid] < $lev){
|
|
continue;
|
|
}
|
|
$ids[$aid] = $lev;
|
|
}
|
|
|
|
$cleanTitle = $this->cleanTitle($cleanTitle);
|
|
|
|
foreach ($this->titleToId as $t => $aid){
|
|
if(strpos($t, $cleanTitle) !== false){
|
|
$lev = levenshtein($cleanTitle, $t);
|
|
if($lev < 0 or isset($ids2[$aid]) and $ids2[$aid] < $lev){
|
|
continue;
|
|
}
|
|
$ids2[$aid] = $lev;
|
|
}
|
|
}
|
|
|
|
asort($ids);
|
|
asort($ids2);
|
|
|
|
$returnIds = [];
|
|
|
|
while((count($returnIds) < $limit or $limit === -1) and (count($ids) > 0 or count($ids2) > 0)){
|
|
foreach ($ids2 as $aid => $lev){
|
|
$returnIds[$aid] = $lev;
|
|
unset($ids2[$aid]);
|
|
break;
|
|
}
|
|
foreach ($ids as $aid => $lev){
|
|
$returnIds[$aid] = $lev;
|
|
unset($ids[$aid]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//asort($returnIds);
|
|
|
|
return $returnIds;
|
|
}
|
|
|
|
public function matchTitle($title){
|
|
if($title === ""){
|
|
return null;
|
|
}
|
|
|
|
$cleanTitle = $title;
|
|
if(isset($this->titleToId[$cleanTitle])){
|
|
return $this->db[$this->titleToId[$cleanTitle]];
|
|
}
|
|
|
|
$cleanTitle = strtolower($cleanTitle);
|
|
if(isset($this->titleToId[$cleanTitle])){
|
|
return $this->db[$this->titleToId[$cleanTitle]];
|
|
}
|
|
|
|
$cleanTitle = $this->cleanTitle($cleanTitle);
|
|
if(isset($this->titleToId[$cleanTitle])){
|
|
return $this->db[$this->titleToId[$cleanTitle]];
|
|
}
|
|
|
|
$cleanTitle = str_replace(" ", "", $cleanTitle);
|
|
|
|
if(isset($this->titleToId[$cleanTitle])){
|
|
return $this->db[$this->titleToId[$cleanTitle]];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function getAnime($aid){
|
|
if(isset($this->db[$aid])){
|
|
return $this->db[$aid];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function getSearchQueries($aid, $langCodes = ["en", "x-jat"]){
|
|
if(!isset($this->db[$aid])){
|
|
return [];
|
|
}
|
|
|
|
$queries = [];
|
|
|
|
foreach($this->db[$aid]["languages"] as $langCode => $titles){
|
|
if(in_array($langCode, $langCodes, true)){
|
|
foreach($titles as $t){
|
|
$baseTitle = strtolower($t["title"]);
|
|
$queries[$baseTitle] = $baseTitle;
|
|
|
|
$t = $this->cleanTitle($baseTitle);
|
|
if(!isset($queries[$t])){
|
|
$queries[$t] = $t;
|
|
}
|
|
|
|
$t2 = $this->cleanTitle($baseTitle, " ");
|
|
if(!isset($queries[$t2])){
|
|
$queries[$t2] = $t2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $queries;
|
|
}
|
|
|
|
private function addTitleToLookup($title, $aid, $priority = false){
|
|
if(!isset($this->titleToId[$title]) or $priority){
|
|
$this->titleToId[$title] = $aid;
|
|
}
|
|
}
|
|
|
|
private function cleanBaseTitle($title, $replace = ""){
|
|
return trim(preg_replace('!\s+!', ' ', str_ireplace(["the animation"], $replace, $title)));
|
|
}
|
|
|
|
private function cleanTitle($title, $replace = ""){
|
|
return trim(preg_replace('!\s+!', ' ', str_replace([" o "], [" wo "], str_replace([",", ":", "~", "-", "'", '"', "`", "/", "&", ".", "!", "the animation", " + "], $replace, strtolower($title)))));
|
|
}
|
|
|
|
private function initCache($path, $force = false){
|
|
if($force !== null and ($force or !file_exists($path) or filemtime($path) < (time() - 3600 * 36))){ //Download every 36 hours
|
|
$contents = file_get_contents(Database::getConfigKey("anidb.titles.url", "http://anidb.net/api/anime-titles.dat.gz"));
|
|
if(strlen($contents) > 4096){
|
|
file_put_contents($path, zlib_decode($contents));
|
|
}
|
|
touch($path);
|
|
}
|
|
|
|
foreach(@file($path) as $line){
|
|
if($line{0} == "#"){
|
|
continue;
|
|
}
|
|
$fields = explode("|", trim($line));
|
|
$aid = (int) $fields[0];
|
|
$entryType = (int) $fields[1];
|
|
$langCode = $fields[2];
|
|
$title = $fields[3];
|
|
|
|
$this->addEntryName($aid, $langCode, $title, $entryType);
|
|
}
|
|
}
|
|
|
|
public function resetCache($path, $force = false){
|
|
$this->db = [];
|
|
$this->initCache($path, $force);
|
|
}
|
|
|
|
public function loadExtra($path){
|
|
@$this->initCache($path, null);
|
|
}
|
|
|
|
public function addEntryName($aid, $langCode, $title, $entryType = 2){
|
|
if(!isset($this->db[$aid])){
|
|
$this->db[$aid] = [
|
|
"aid" => $aid,
|
|
"title" => null,
|
|
"languages" => []
|
|
];
|
|
}
|
|
|
|
if(!isset($this->db[$aid]["languages"][$langCode])){
|
|
$this->db[$aid]["languages"][$langCode] = [];
|
|
}
|
|
|
|
if($title !== $this->cleanBaseTitle($title)){
|
|
$this->db[$aid]["languages"][$langCode][] = ["type" => $entryType, "title" => $title];
|
|
$title = $this->cleanBaseTitle($title);
|
|
}
|
|
|
|
$this->db[$aid]["languages"][$langCode][] = ["type" => $entryType, "title" => $title];
|
|
if($entryType <= 1){
|
|
$this->db[$aid]["title"] = $title;
|
|
}
|
|
|
|
$this->addTitleToLookup($title, $aid, true);
|
|
$this->addTitleToLookup(strtolower($title), $aid, true);
|
|
if(stripos($title, "ka?") !== false){
|
|
$this->addTitleToLookup(rtrim(str_replace("ka?", "ka", strtolower($title)), '?!'), $aid, true);
|
|
}
|
|
|
|
$this->addTitleToLookup($this->cleanTitle($title), $aid);
|
|
$this->addTitleToLookup(str_replace(" ", "", $this->cleanTitle($title)), $aid);
|
|
$this->addTitleToLookup(str_replace(" ", "", $this->cleanTitle($title) . " complete"), $aid);
|
|
$this->addTitleToLookup(str_replace(" ", "", $this->cleanTitle($title) . " complete series"), $aid);
|
|
|
|
if(preg_match("/^(.+)([ \t]+season)[ \t]+([0-9])$/iu", $title, $matches) > 0){
|
|
$title = $matches[1] . " S". $matches[3];
|
|
$this->db[$aid]["languages"][$langCode][] = ["type" => $entryType === $entryType, "title" => $title];
|
|
if($entryType === 1){
|
|
$this->db[$aid]["title"] = $title;
|
|
}
|
|
|
|
$this->addTitleToLookup($title, $aid, true);
|
|
|
|
$this->addTitleToLookup(strtolower($title), $aid, true);
|
|
|
|
$this->addTitleToLookup($this->cleanTitle($title), $aid);
|
|
$this->addTitleToLookup(str_replace(" ", "", $this->cleanTitle($title)), $aid);
|
|
$this->addTitleToLookup(str_replace(" ", "", $this->cleanTitle($title) . " complete"), $aid);
|
|
$this->addTitleToLookup(str_replace(" ", "", $this->cleanTitle($title) . " complete series"), $aid);
|
|
|
|
}
|
|
}
|
|
|
|
private $titleToId = [];
|
|
|
|
private $db = [];
|
|
|
|
private $exceptions = [];
|
|
}
|