307 lines
8.9 KiB
PHP
Executable file
307 lines
8.9 KiB
PHP
Executable file
<?php
|
|
|
|
namespace Animarr\Torrent;
|
|
use Animarr\Database;
|
|
use Animarr\Downloader;
|
|
use Animarr\Request;
|
|
|
|
class Torrent{
|
|
|
|
private $announce;
|
|
|
|
private $creationDate;
|
|
|
|
private $info;
|
|
|
|
private $info_hash;
|
|
|
|
private $files = [];
|
|
|
|
public static $urls = [];
|
|
|
|
public static function isTorrentDownloadPath(&$url){
|
|
$data = parse_url($url);
|
|
|
|
if(isset($data["scheme"]) and $data["scheme"] == "magnet"){
|
|
return true;
|
|
}
|
|
|
|
if(isset($data["path"]) and substr($data["path"], -8) === ".torrent"){
|
|
return true;
|
|
}
|
|
|
|
if(isset($data["path"]) and ($data["host"] == "anidex.info" or $data["host"] == "anidex.moe") and preg_match("#^/dl/.*$#", $data["path"]) > 0){
|
|
return true;
|
|
}
|
|
|
|
if(isset($data["path"]) and ($data["host"] == "anirena.com" or $data["host"] == "www.anirena.com") and preg_match("#^/dl/[0-9]+$#", $data["path"]) > 0){
|
|
return true;
|
|
}
|
|
|
|
if(isset($data["path"]) and ($data["host"] == "www.frozen-layer.com" or $data["host"] == "frozen-layer.com") and preg_match("#^/descargas/([0-9]+.*)$#", $data["path"], $matches) > 0){
|
|
$url = "https://www.frozen-layer.com/descargas/".$matches[1] .".torrent";
|
|
return true;
|
|
}
|
|
|
|
|
|
if(isset($data["path"]) and ($data["host"] == "anidex.info" or $data["host"] == "anidex.moe") and preg_match("#page=torrent&id=([0-9]+)$#", $data["path"], $matches) > 0){
|
|
$url = "https://anidex.info/dl/".$matches[1];
|
|
return true;
|
|
}
|
|
|
|
if(isset($data["path"]) and ($data["host"] == "anidex.info" or $data["host"] == "anidex.moe") and preg_match("#/torrent/([0-9]+)$#", $data["path"], $matches) > 0){
|
|
$url = "https://anidex.info/dl/".$matches[1];
|
|
return true;
|
|
}
|
|
|
|
//if(isset($data["path"]) and ($data["host"] == "nyaa.se" or $data["host"] == "www.nyaa.se") and preg_match("#page=download&tid=/.*$#", $data["path"]) > 0){
|
|
//return true;
|
|
//}
|
|
|
|
if(isset($data["path"]) and ($data["host"] == "nyaa.si" or $data["host"] == "www.nyaa.si") and preg_match("#view/[0-9]+/torrent$#", $data["path"]) > 0){
|
|
return true;
|
|
}
|
|
|
|
if(isset($data["path"]) and $data["host"] == "animebytes.tv" and preg_match("#torrent/[0-9]+/download#", $data["path"]) > 0){
|
|
return true;
|
|
}
|
|
|
|
if(isset($data["path"]) and ($data["host"] == "nyaa.se" or $data["host"] == "nyaa.eu" or $data["host"] == "www.nyaa.se") and preg_match("#page=(view|download)&tid=/.*$#", $data["path"], $matches) > 0){
|
|
$url = "https://nyaa.si/view/".$matches[1]."/torrent";
|
|
return true;
|
|
}
|
|
|
|
if(isset($data["path"]) and ($data["host"] == "nyaa.si" or $data["host"] == "www.nyaa.si") and preg_match("#view/([0-9]+)(|/torrent|\\.torrent)$#", $data["path"], $matches) > 0){
|
|
$url = "https://nyaa.si/view/".$matches[1]."/torrent";
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static function isPrivateTorrent($url){
|
|
$data = parse_url($url);
|
|
|
|
if(!isset($data["host"])){
|
|
return false;
|
|
}
|
|
|
|
$domains = Database::getConfigKey("tracker.domain.private", []);
|
|
|
|
return in_array($data["host"], $domains);
|
|
}
|
|
|
|
public static function getMagnetProperties($magnetUrl){
|
|
$data = parse_url($magnetUrl);
|
|
|
|
if(!isset($data["query"]) or $data["scheme"] != "magnet"){
|
|
return [];
|
|
}
|
|
|
|
parse_str($data["query"], $query);
|
|
return $query;
|
|
}
|
|
|
|
public static function getMagnetHash($magnetUrl){
|
|
$props = self::getMagnetProperties($magnetUrl);
|
|
if(isset($props["xt"]) and preg_match('/urn:btih:[ ]*([A-Za-z0-9]{32,40})/', $props["xt"], $matches) > 0){
|
|
$hash = $matches[1];
|
|
if(strlen($hash) == 32){
|
|
$hash = bin2hex(Base32::decode($hash));
|
|
}
|
|
|
|
$hash = strtoupper($hash);
|
|
return $hash;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static function queueMagnetTorrent($magnet, $cachePath = null){
|
|
if(!Database::getConfigKey("magnet.service.enable", false) or Database::getConfigKey("magnet.service.folder", "") === ""){
|
|
return false;
|
|
}
|
|
|
|
$hash = self::getMagnetHash($magnet);
|
|
|
|
if($hash !== null and $cachePath !== null and file_exists($cachePath . "/". $hash . ".torrent")){
|
|
return true;
|
|
}
|
|
|
|
if($hash === null){
|
|
Downloader::log("Failed to magnet ".$hash." to MagnetService ($magnet)", "TORRENT");
|
|
return false;
|
|
}
|
|
|
|
file_put_contents(Database::getConfigKey("magnet.service.folder", "") . "/" . $hash . ".magnet", $magnet);
|
|
#Downloader::log("Added magnet ".$hash." to MagnetService", "TORRENT");
|
|
|
|
return true;
|
|
}
|
|
|
|
public static function getTorrent($url, $cachePath = null){
|
|
$contents = null;
|
|
if(strpos($url, "urn:btih:") !== false){
|
|
$hash = self::getMagnetHash($url);
|
|
if($hash === null){
|
|
return "";
|
|
}
|
|
|
|
$path = $cachePath . "/". $hash . ".torrent";
|
|
if($cachePath !== null and file_exists($path)){
|
|
$contents = file_get_contents($path);
|
|
}else{
|
|
self::queueMagnetTorrent($url, $cachePath);
|
|
return null;
|
|
}
|
|
}else{
|
|
$path = $cachePath . "/url_" . md5($url) . ".torrent";
|
|
if($cachePath !== null and file_exists($path)){
|
|
$contents = file_get_contents($path);
|
|
}
|
|
|
|
if(($contents === "" and !Database::getConfigKey("torrent.cache.addEmpty", false)) or $contents === null){
|
|
$contents = Request::getURL($url);
|
|
if(self::isPrivateTorrent($url)){
|
|
sleep(Database::getConfigKey("tracker.private.fetchDelay", 5));
|
|
}
|
|
if($cachePath !== null and strlen($contents) > 40){
|
|
file_put_contents($path, $contents);
|
|
}elseif($cachePath !== null and Database::getConfigKey("torrent.cache.addEmpty", false)){
|
|
file_put_contents($path, "");
|
|
}
|
|
}
|
|
}
|
|
|
|
return $contents;
|
|
}
|
|
|
|
public static function fromURL($url, $cachePath = null){
|
|
if(isset(self::$urls[$url])){
|
|
return self::$urls[$url];
|
|
}
|
|
|
|
$contents = self::getTorrent($url, $cachePath);
|
|
if($contents === null){
|
|
return null;
|
|
}
|
|
|
|
$t = new Torrent($contents);
|
|
|
|
$hash = $t->getInfoHash();
|
|
|
|
if($contents != "" and $hash != null and $cachePath !== null and !file_exists($cachePath . "/" . $hash . ".torrent")){
|
|
file_put_contents($cachePath . "/" . $hash . ".torrent", $contents);
|
|
}
|
|
|
|
if($contents == ""){
|
|
return $t;
|
|
}
|
|
|
|
return self::$urls[$url] = $t;
|
|
}
|
|
|
|
public function __construct($contents){
|
|
$c = self::bdecode($contents);
|
|
if(!is_array($c)){
|
|
return;
|
|
}
|
|
$this->announce = isset($c["announce-list"]) ? $c["announce-list"] : isset($c["announce"]) ? [[$c["announce"]]] : [];
|
|
$this->creationDate = isset($c["creation-date"]) ? $c["creation-date"] : time();
|
|
|
|
if(isset($c["info"]["files"])){
|
|
foreach($c["info"]["files"] as $file){
|
|
//Damn padding files
|
|
if(strpos($file["path"][0], "_____padding_file") === 0){
|
|
continue;
|
|
}
|
|
array_unshift($file["path"], $c["info"]["name"]);
|
|
$this->files[] = [
|
|
"size" => $file["length"],
|
|
"path" => $file["path"],
|
|
"stringPath" => implode("/", $file["path"]),
|
|
];
|
|
}
|
|
}else{
|
|
$this->files[] = [
|
|
"size" => $c["info"]["length"],
|
|
"path" => [$c["info"]["name"]],
|
|
"stringPath" => $c["info"]["name"],
|
|
];
|
|
}
|
|
|
|
$this->info = $c["info"];
|
|
$this->info_hash = $c["info_hash"];
|
|
}
|
|
|
|
public function getCreationDate(){
|
|
return $this->creationDate;
|
|
}
|
|
|
|
public function getFiles(){
|
|
return $this->files;
|
|
}
|
|
|
|
public function getInfoHash(){
|
|
return $this->info_hash;
|
|
}
|
|
|
|
private static function bdecode($s, &$pos = 0, $depth = 0) {
|
|
if($pos>=strlen($s)) {
|
|
return null;
|
|
}
|
|
switch($s[$pos]){
|
|
case 'd':
|
|
$pos++;
|
|
$retval=array();
|
|
while (isset($s[$pos]) and $s[$pos]!='e'){
|
|
$key=self::bdecode($s, $pos, $depth + 1);
|
|
$origPos = $pos;
|
|
$val=self::bdecode($s, $pos, $depth + 1);
|
|
if ($key===null || $val===null)
|
|
break;
|
|
$retval[$key]=$val;
|
|
if($key === "info" and $depth <= 1){
|
|
$retval["info_hash"] = strtoupper(sha1(substr($s, $origPos, $pos - $origPos)));
|
|
}
|
|
}
|
|
$retval["isDct"]=true;
|
|
$pos++;
|
|
return $retval;
|
|
|
|
case 'l':
|
|
$pos++;
|
|
$retval=array();
|
|
while ($s[$pos]!='e'){
|
|
$val=self::bdecode($s, $pos, $depth + 1);
|
|
if ($val===null)
|
|
break;
|
|
$retval[]=$val;
|
|
}
|
|
$pos++;
|
|
return $retval;
|
|
|
|
case 'i':
|
|
$pos++;
|
|
$digits=strpos($s, 'e', $pos)-$pos;
|
|
$val=round((float)substr($s, $pos, $digits));
|
|
$pos+=$digits+1;
|
|
return $val;
|
|
|
|
// case "0": case "1": case "2": case "3": case "4":
|
|
// case "5": case "6": case "7": case "8": case "9":
|
|
default:
|
|
$digits=strpos($s, ':', $pos)-$pos;
|
|
if ($digits<0 || $digits >20)
|
|
return null;
|
|
$len=(int)substr($s, $pos, $digits);
|
|
$pos+=$digits+1;
|
|
$str=substr($s, $pos, $len);
|
|
$pos+=$len;
|
|
//echo "pos: $pos str: [$str] len: $len digits: $digits\n";
|
|
return (string)$str;
|
|
}
|
|
return null;
|
|
}
|
|
}
|