Animarr/src/Animarr/AniDB.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 = [];
}