mirror of
https://github.com/cloudreve/Cloudreve.git
synced 2024-04-21 12:31:40 +00:00
469 lines
14 KiB
PHP
469 lines
14 KiB
PHP
<?php
|
||
namespace app\index\model;
|
||
|
||
use think\Model;
|
||
use think\Db;
|
||
|
||
class Aria2 extends Model{
|
||
|
||
private $authToken;
|
||
private $apiUrl;
|
||
private $savePath;
|
||
private $saveOptions;
|
||
public $reqStatus;
|
||
public $reqMsg;
|
||
public $pathId;
|
||
public $pid;
|
||
private $uid;
|
||
private $policy;
|
||
|
||
public function __construct($options){
|
||
$this->authToken = $options["aria2_token"];
|
||
$this->apiUrl = rtrim($options["aria2_rpcurl"],"/")."/";
|
||
$this->saveOptions = json_decode($options["aria2_options"],true);
|
||
$this->savePath = rtrim(rtrim($options["aria2_tmppath"],"/"),"\\").DS;
|
||
}
|
||
|
||
/**
|
||
* 新建普通URL下载任务
|
||
*
|
||
* @param string $url
|
||
* @return void
|
||
*/
|
||
public function addUrl($url){
|
||
$this->pathId = uniqid();
|
||
$reqFileds = [
|
||
"params" => ["token:".$this->authToken,
|
||
[$url],["dir" => $this->savePath.$this->pathId],
|
||
],
|
||
"jsonrpc" => "2.0",
|
||
"id" => $this->pathId,
|
||
"method" => "aria2.addUri"
|
||
];
|
||
$reqFileds["params"][2] = array_merge($reqFileds["params"][2],$this->saveOptions);
|
||
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
|
||
$respondData = $this->sendReq($reqFileds);
|
||
if(isset($respondData["result"])){
|
||
$this->reqStatus = 1;
|
||
$this->pid = $respondData["result"];
|
||
}else{
|
||
$this->reqStatus = 0;
|
||
$this->reqMsg = isset($respondData["error"]["message"]) ? $respondData["error"]["message"] : $this->reqMsg;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 新建种子下载任务
|
||
*
|
||
* @param string $torrentUrl 种子URL
|
||
* @return void
|
||
*/
|
||
public function addTorrent($torrentUrl){
|
||
$this->pathId = uniqid();
|
||
$reqFileds = [
|
||
"params" => ["token:".$this->authToken,
|
||
[$torrentUrl],["dir" => $this->savePath.$this->pathId],
|
||
],
|
||
"jsonrpc" => "2.0",
|
||
"id" => $this->pathId,
|
||
"method" => "aria2.addUri"
|
||
];
|
||
$reqFileds["params"][2] = array_merge($reqFileds["params"][2],$this->saveOptions);
|
||
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
|
||
$respondData = $this->sendReq($reqFileds);
|
||
if(isset($respondData["result"])){
|
||
$this->reqStatus = 1;
|
||
$this->pid = $respondData["result"];
|
||
}else{
|
||
$this->reqStatus = 0;
|
||
$this->reqMsg = isset($respondData["error"]["message"]) ? $respondData["error"]["message"] : $this->reqMsg;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 刷新下载状态
|
||
*
|
||
* @param int $id 任务ID
|
||
* @param int $uid 用户ID
|
||
* @param array $policy 上传策略
|
||
* @return void
|
||
*/
|
||
public function flushStatus($id,$uid,$policy){
|
||
$this->uid = $uid;
|
||
if(empty($policy)){
|
||
$user = Db::name("users")->where("id",$uid)->find();
|
||
$group = Db::name("groups")->where("id",$user["user_group"])->find();
|
||
$policy = Db::name("policy")->where("id",$group["policy_name"])->find();
|
||
}
|
||
$this->policy = $policy;
|
||
$downloadInfo = Db::name("download")->where("id",$id)->find();
|
||
if(empty($downloadInfo)){
|
||
$this->reqStatus = 0;
|
||
$this->reqMsg = "未找到下载记录";
|
||
return false;
|
||
}
|
||
if(in_array($downloadInfo["status"], ["error","complete"])){
|
||
$this->reqStatus = 1;
|
||
return true;
|
||
}
|
||
if($uid != $downloadInfo["owner"]){
|
||
$this->reqStatus = 0;
|
||
$this->reqMsg = "无权操作";
|
||
return false;
|
||
}
|
||
$reqFileds = [
|
||
"params" => ["token:".$this->authToken,$downloadInfo["pid"]],
|
||
"jsonrpc" => "2.0",
|
||
"id" => uniqid(),
|
||
"method" => "aria2.tellStatus"
|
||
];
|
||
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
|
||
$respondData = $this->sendReq($reqFileds);
|
||
if(isset($respondData["result"])){
|
||
if($this->storageCheck($respondData["result"],$downloadInfo)){
|
||
if($downloadInfo["is_single"] && count($respondData["result"]["files"]) >1){
|
||
$this->updateToMuiltpe($respondData["result"],$downloadInfo);
|
||
return false;
|
||
}
|
||
if(isset($respondData["result"]["followedBy"])){
|
||
Db::name("download")->where("id",$id)
|
||
->update([
|
||
"pid" => $respondData["result"]["followedBy"][0],
|
||
]);
|
||
return false;
|
||
}
|
||
Db::name("download")->where("id",$id)
|
||
->update([
|
||
"status" => $respondData["result"]["status"],
|
||
"last_update" => date("Y-m-d h:i:s"),
|
||
"info" => json_encode([
|
||
"completedLength" => $respondData["result"]["files"][$downloadInfo["file_index"]]["completedLength"],
|
||
"totalLength" => $respondData["result"]["files"][$downloadInfo["file_index"]]["length"],
|
||
"dir" => $respondData["result"]["files"][$downloadInfo["file_index"]]["path"],
|
||
"downloadSpeed" => $respondData["result"]["downloadSpeed"],
|
||
"errorMessage" => isset($respondData["result"]["errorMessage"]) ? $respondData["result"]["errorMessage"] : "",
|
||
]),
|
||
"msg" => isset($respondData["result"]["errorMessage"]) ? $respondData["result"]["errorMessage"] : "",
|
||
"total_size" => $respondData["result"]["files"][$downloadInfo["file_index"]]["length"],
|
||
]);
|
||
switch ($respondData["result"]["status"]) {
|
||
case 'complete':
|
||
$this->setComplete($respondData["result"],$downloadInfo);
|
||
break;
|
||
case 'removed':
|
||
$this->setCanceled($respondData["result"],$downloadInfo);
|
||
break;
|
||
default:
|
||
# code...
|
||
break;
|
||
}
|
||
if(($respondData["result"]["files"][$downloadInfo["file_index"]]["completedLength"] == $respondData["result"]["files"][$downloadInfo["file_index"]]["length"] && ($respondData["result"]["files"][$downloadInfo["file_index"]]["length"] !=0 )) && $respondData["result"]["status"]=="active"){
|
||
$this->setComplete($respondData["result"],$downloadInfo,$downloadInfo["file_index"]);
|
||
Db::name("download")->where("id",$id)
|
||
->update([
|
||
"status" => "complete",
|
||
]);
|
||
}
|
||
}else{
|
||
$this->reqStatus = 0;
|
||
$this->reqMsg = "空间容量不足";
|
||
$this->setError($respondData["result"],$downloadInfo,"空间容量不足");
|
||
return false;
|
||
}
|
||
}else{
|
||
$this->reqStatus = 0;
|
||
$this->reqMsg = $respondData["error"]["message"];
|
||
$this->setError($respondData,$downloadInfo,$respondData["error"]["message"],"error",true);
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 取消任务
|
||
*
|
||
* @param array $quenInfo 任务信息(aria2)
|
||
* @param array $sqlData 任务信息(数据库)
|
||
* @return void
|
||
*/
|
||
private function setCanceled($quenInfo,$sqlData){
|
||
@self::remove_directory($this->savePath.$sqlData["path_id"]);
|
||
if(!is_dir($this->savePath.$sqlData["path_id"])){
|
||
Db::name("download")->where("id",$sqlData["id"])->update([
|
||
"status" => "canceled",
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 移除整个目录
|
||
*
|
||
* @param string $dir
|
||
* @return void
|
||
*/
|
||
static function remove_directory($dir){
|
||
if($handle=opendir("$dir")){
|
||
while(false!==($item=readdir($handle))){
|
||
if($item!="."&&$item!=".."){
|
||
if(is_dir("$dir/$item")){
|
||
self::remove_directory("$dir/$item");
|
||
}else{
|
||
unlink("$dir/$item");
|
||
}
|
||
}
|
||
}
|
||
closedir($handle);
|
||
rmdir($dir);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 将单文件任务升级至多文件任务
|
||
*
|
||
* @param array $quenInfo
|
||
* @param array $sqlData
|
||
* @return void
|
||
*/
|
||
private function updateToMuiltpe($quenInfo,$sqlData){
|
||
foreach ($quenInfo["files"] as $key => $value) {
|
||
Db::name("download")->insert([
|
||
"pid" => $sqlData["pid"],
|
||
"path_id" => $sqlData["path_id"],
|
||
"owner" => $sqlData["owner"],
|
||
"save_dir" => $sqlData["save_dir"],
|
||
"status" => "ready",
|
||
"msg" => "",
|
||
"info"=>"",
|
||
"source" =>$sqlData["source"],
|
||
"file_index" => $key,
|
||
"is_single" => 0,
|
||
"total_size" => 0,
|
||
]);
|
||
}
|
||
Db::name("download")->where("id",$sqlData["id"])->delete();
|
||
}
|
||
|
||
/**
|
||
* 下载完成后续处理
|
||
*
|
||
* @param array $quenInfo
|
||
* @param array $sqlData
|
||
* @param int $fileIndex
|
||
* @return void
|
||
*/
|
||
private function setComplete($quenInfo,$sqlData,$fileIndex=null){
|
||
if($this->policy["policy_type"] != "local" && $this->policy["policy_type"] != "onedrive"){
|
||
$this->setError($quenInfo,$sqlData,"您当前的上传策略无法使用离线下载");
|
||
return false;
|
||
}
|
||
if($fileIndex==null){
|
||
$this->forceRemove($sqlData["pid"]);
|
||
}
|
||
$suffixTmp = explode('.', $quenInfo["dir"]);
|
||
$fileSuffix = array_pop($suffixTmp);
|
||
$uploadHandller = new UploadHandler($this->policy["id"],$this->uid);
|
||
$allowedSuffix = explode(',', $uploadHandller->getAllowedExt(json_decode($this->policy["filetype"],true)));
|
||
$sufficCheck = !in_array($fileSuffix,$allowedSuffix);
|
||
if(empty($uploadHandller->getAllowedExt(json_decode($this->policy["filetype"],true)))){
|
||
$sufficCheck = false;
|
||
}
|
||
if($sufficCheck){
|
||
//取消任务
|
||
$this->setError($quenInfo,$sqlData,"文件类型不被允许");
|
||
return false;
|
||
}
|
||
if($this->policy['autoname']){
|
||
$fileName = $uploadHandller->getObjName($this->policy['namerule'],"local",basename($quenInfo["files"][$sqlData["file_index"]]["path"]));
|
||
}else{
|
||
$fileName = basename($quenInfo["files"][$sqlData["file_index"]]["path"]);
|
||
}
|
||
$generatePath = $uploadHandller->getDirName($this->policy['dirrule']);
|
||
|
||
if($this->policy["policy_type"] == "onedrive"){
|
||
|
||
$savePath = ROOT_PATH . 'public/uploads/'.$generatePath.DS.$fileName;
|
||
$task = new Task();
|
||
$task->taskName = "Upload RemoteDownload File " . $quenInfo["files"][$sqlData["file_index"]]["path"] . " to Onedrive";
|
||
$task->taskType = $quenInfo["files"][$sqlData["file_index"]]["length"]<=4*1024*1024 ? "UploadRegularRemoteDownloadFileToOnedrive" :"UploadLargeRemoteDownloadFileToOnedrive";
|
||
@list($width, $height, $type, $attr) = getimagesize($quenInfo["files"][$sqlData["file_index"]]["path"]);
|
||
$picInfo = empty($width)?"":$width.",".$height;
|
||
$task->taskContent = json_encode([
|
||
"path" => ltrim(str_replace("/", ",", $sqlData["save_dir"]),","),
|
||
"fname" => basename($quenInfo["files"][$sqlData["file_index"]]["path"]),
|
||
"originPath" => $quenInfo["files"][$sqlData["file_index"]]["path"],
|
||
"objname" => $fileName,
|
||
"savePath" => $generatePath,
|
||
"fsize" => (int)$quenInfo["files"][$sqlData["file_index"]]["length"],
|
||
"picInfo" => $picInfo,
|
||
"policyId" => $this->policy["id"],
|
||
]);
|
||
$task->userId = $this->uid;
|
||
$task->saveTask();
|
||
|
||
}else{
|
||
|
||
$savePath = ROOT_PATH . 'public/uploads/'.$generatePath.DS.$fileName;
|
||
is_dir(dirname($savePath))? :mkdir(dirname($savePath),0777,true);
|
||
rename($quenInfo["files"][$sqlData["file_index"]]["path"],$savePath);
|
||
@unlink(dirname($quenInfo["files"][$sqlData["file_index"]]["path"]));
|
||
$jsonData = array(
|
||
"path" => ltrim(str_replace("/", ",", $sqlData["save_dir"]),","),
|
||
"fname" => basename($quenInfo["files"][$sqlData["file_index"]]["path"]),
|
||
"objname" => $generatePath.DS.$fileName,
|
||
"fsize" => $quenInfo["files"][$sqlData["file_index"]]["length"],
|
||
);
|
||
@list($width, $height, $type, $attr) = getimagesize($savePath);
|
||
$picInfo = empty($width)?" ":$width.",".$height;
|
||
$addAction = FileManage::addFile($jsonData,$this->policy,$this->uid,$picInfo);
|
||
if(!$addAction[0]){
|
||
//取消任务
|
||
$this->setError($quenInfo,$sqlData,$addAction[1]);
|
||
return false;
|
||
}
|
||
|
||
}
|
||
|
||
FileManage::storageCheckOut($this->uid,$quenInfo["files"][$sqlData["file_index"]]["length"]);
|
||
}
|
||
|
||
/**
|
||
* 设置任务为失败状态
|
||
*
|
||
* @param array $quenInfo
|
||
* @param array $sqlData
|
||
* @param string $msg 失败消息
|
||
* @param string $status 状态
|
||
* @param boolean $delete 是否删除下载文件
|
||
* @return void
|
||
*/
|
||
private function setError($quenInfo,$sqlData,$msg,$status="error",$delete=true){
|
||
$this->Remove($sqlData["pid"],$sqlData);
|
||
$this->removeDownloadResult($sqlData["pid"],$sqlData);
|
||
if($delete){
|
||
if(isset($quenInfo["files"][$sqlData["file_index"]]["path"]) && file_exists($quenInfo["files"][$sqlData["file_index"]]["path"])){
|
||
$deleteAction = @unlink($quenInfo["files"][$sqlData["file_index"]]["path"]);
|
||
@self::remove_directory(dirname($quenInfo["files"][$sqlData["file_index"]]["path"]));
|
||
if(file_exists(dirname($quenInfo["files"][$sqlData["file_index"]]["path"]))){
|
||
$task = new Task();
|
||
$task->taskName = "Delete remote download temp file";
|
||
$task->taskType = "deleteFolder";
|
||
$task->taskContent = json_encode([
|
||
"folder" => dirname($quenInfo["files"][$sqlData["file_index"]]["path"]),
|
||
]);
|
||
$task->userId = $this->uid;
|
||
$task->saveTask();
|
||
}
|
||
}
|
||
}
|
||
Db::name("download")->where("id",$sqlData["id"])->update([
|
||
"msg" => $msg,
|
||
"status" => $status,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 移除任务
|
||
*
|
||
* @param int $gid
|
||
* @param array $sqlData
|
||
* @return void
|
||
*/
|
||
public function Remove($gid,$sqlData){
|
||
$reqFileds = [
|
||
"params" => ["token:".$this->authToken,$gid],
|
||
"jsonrpc" => "2.0",
|
||
"id" => uniqid(),
|
||
"method" => "aria2.remove"
|
||
];
|
||
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
|
||
$respondData = $this->sendReq($reqFileds);
|
||
if(isset($respondData["result"])){
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 删除下载结果
|
||
*
|
||
* @param int $gid
|
||
* @param array $sqlData
|
||
* @return void
|
||
*/
|
||
public function removeDownloadResult($gid,$sqlData){
|
||
$reqFileds = [
|
||
"params" => ["token:".$this->authToken,$gid],
|
||
"jsonrpc" => "2.0",
|
||
"id" => uniqid(),
|
||
"method" => "aria2.removeDownloadResult"
|
||
];
|
||
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
|
||
$respondData = $this->sendReq($reqFileds);
|
||
if(isset($respondData["result"])){
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 强制移除任务
|
||
*
|
||
* @param int $gid
|
||
* @return void
|
||
*/
|
||
public function forceRemove($gid){
|
||
$reqFileds = [
|
||
"params" => ["token:".$this->authToken,$gid],
|
||
"jsonrpc" => "2.0",
|
||
"id" => uniqid(),
|
||
"method" => "aria2.forceRemove"
|
||
];
|
||
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
|
||
$respondData = $this->sendReq($reqFileds);
|
||
if(isset($respondData["result"])){
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 检查容量
|
||
*
|
||
* @param array $quenInfo
|
||
* @param array $sqlData
|
||
* @return void
|
||
*/
|
||
private function storageCheck($quenInfo,$sqlData){
|
||
if(!FileManage::sotrageCheck($this->uid,$quenInfo["totalLength"])){
|
||
return false;
|
||
}
|
||
if(!FileManage::sotrageCheck($this->uid,$quenInfo["completedLength"])){
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 发送请求
|
||
*
|
||
* @param string $data
|
||
* @return array
|
||
*/
|
||
private function sendReq($data){
|
||
$curl = curl_init();
|
||
curl_setopt($curl, CURLOPT_URL, $this->apiUrl."jsonrpc");
|
||
curl_setopt($curl, CURLOPT_POST, 1);
|
||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
|
||
curl_setopt($curl, CURLOPT_TIMEOUT, 15);
|
||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||
$tmpInfo = curl_exec($curl);
|
||
if (curl_errno($curl)) {
|
||
$this->reqStatus = 0;
|
||
$this->reqMsg = "请求失败,".curl_error($curl);
|
||
}
|
||
curl_close($curl);
|
||
return json_decode($tmpInfo,true);
|
||
}
|
||
|
||
}
|
||
?>
|