• app.php 管理员 14.27 kB 2022-08-19 13:09
  • Position: app.php

    <?php
    
    class fileThumbPlugin extends PluginBase{
    	function __construct(){
    		parent::__construct();
    	}
    	public function regist(){
    		$this->hookRegist(array(
    			'user.commonJs.insert'  	=> 'fileThumbPlugin.echoJs',
    			'explorer.list.itemParse'	=> 'fileThumbPlugin.itemParse',
    		));
    	}
    	public function echoJs(){
    		$this->echoFile('static/main.js');
    	}
    	public function itemParse($pathInfo){
    		static $supportThumb = false;
    		static $supportView  = false;
    		static $supportBin   = false;
    		if(!$supportThumb){
    			$config 		= $this->getConfig('fileThumb');
    			$supportThumb 	= explode(',',$config['fileThumb']);
    			$supportView  	= explode(',',$config['fileExt']);
    			$supportBin 	= $this->getConvert() && $this->getFFmpeg();
    		}
    		if($supportBin != 'ok') return;
    		if($pathInfo['type'] == 'folder') return;
    		if(isset($pathInfo['fileThumb']) || $pathInfo['size'] < 100) return;
    		if(!in_array($pathInfo['ext'],$supportThumb)) return;
    
    		$param  = '&path='.rawurlencode($pathInfo['path']);
    		$param .= '&etag='.$pathInfo['size'].'_'.$pathInfo['modifyTime'];
    		$pathInfo['fileThumb'] = APP_HOST.'?plugin/fileThumb/cover'.$param.'&size=360';
    		if(in_array($pathInfo['ext'],$supportView)){
    			$pathInfo['fileShowView'] = APP_HOST.'?plugin/fileThumb/cover'.$param.'&size=1200';
    		}
    		return $pathInfo;
    	}
    
    	//linux 注意修改获取bin文件的权限问题;
    	public function check(){
    		Cache::remove('fileThumb.getFFmpeg');
    		Cache::remove('fileThumb.getConvert');
    		if(isset($_GET['action']) && $_GET['action'] == 'stopAll'){
    		    // 清除所有任务;
                @include_once($this->pluginPath.'lib/VideoResize.class.php');
        		@include_once($this->pluginPath.'lib/TaskConvert.class.php');
        		$video = new videoResize();
        		$video->stopAll();
        		$video->log("Success !");
        		return;
    		}
    		if(isset($_GET['check'])){
    			$convert = $this->getConvert();
                $ffmpeg  = $this->getFFmpegFind();
                $ffmpegSupport = $ffmpeg ? $this->ffmpegSupportCheck($ffmpeg) : false;
                $result  = $convert && $ffmpeg && $ffmpegSupport;
                if($ffmpeg && !$this->ffmpegSupportCheck($ffmpeg)){$result = false;}
                $message = $result ? 'ok;': LNG('fileThumb.check.faild').'<br/>';
    			if(!$result){
    				$error = '';
    				$check = array('shell_exec','proc_open','proc_close','exec');
    				foreach ($check as $method) {
    					if(!function_exists($method)){$error .= $method .' ';}
    				}
    				if($error){$message = $message.'['.trim($error).'] is disabled(please allow it)<br/>';}
    			}
    			
                if(!$convert){
                    $message .= "$convert convert ".LNG('fileThumb.check.error').";<br/>";
                }
                if(!$ffmpeg){
                    $message .= "$ffmpeg ffmpeg".LNG('fileThumb.check.error').";<br/>";
                }
                if($ffmpeg && !$ffmpegSupport){
                    $message .= 'ffmpeg not support muxer:image2 or libx264; please install again!';
                }
                show_json($message,$result);
    		}
    		include($this->pluginPath.'static/check.html');
    	}
    	
    	// 标清视频;
    	public function videoSmall(){
    		if(!$this->getFFmpeg()) return;
    		@include_once($this->pluginPath.'lib/VideoResize.class.php');
    		@include_once($this->pluginPath.'lib/TaskConvert.class.php');
    		$video = new videoResize();
    		$video->start($this);
    	}
    	
    	public function videoPreview(){
    		if(!$this->getFFmpeg()) return;
    		@include_once($this->pluginPath.'lib/VideoResize.class.php');
    		$video = new videoResize();
    		$video->videoPreview($this);
    	}
    
    	// 本地文件:local,io-local, {{kod-local}}全量生成;
    	// 远端:(ftp,oss等对象存储)
    	public function cover(){
    		$path = $this->filePath($this->in['path']);
    		$size = $this->getSize();
    		$info = IO::info($path);
    		$ext  = $info['ext'];
    		
    		// io缩略图已存在,直接输出
    		$fileHash  = KodIO::hashPath($info);
    		$coverName = "cover_{$ext}_{$fileHash}_{$size}.jpg";
    		$thumbFile = TEMP_FILES . $coverName;
    		if($sourceID = IO::fileNameExist($this->cachePath, $coverName)){
    			return IO::fileOut(KodIO::make($sourceID));
    		}
    		if(Cache::get('fileCover_'.$fileHash) == 'error'){return;}; //是否有封面处理;
    		
    		$localFile = $this->localFile($path);
    		$movie = '3gp,avi,mp4,m4v,mov,mpg,mpeg,mpe,mts,m2ts,wmv,ogv,webm,vob,flv,f4v,mkv,rmvb,rm';
    		$isVideo = in_array($ext,explode(',',$movie));
    		
    		// 过短的视频封面图,不指定时间;
    		$videoThumbTime = true;
    		if( $isVideo && is_array($info['fileInfoMore']) && 
    			isset($info['fileInfoMore']['playtime']) &&
    			floatval($info['fileInfoMore']['playtime']) <= 3 ){
    			$videoThumbTime = false;
    		}
    
    		// del_file($thumbFile);
    		if( $isVideo ){
    			// 不是本地文件; 切片后获取:mp4,mov,mpg,webm,f4v,ogv,avi,mkv,wmv;(部分失败)
    			if(!$localFile){
    				$localTemp = $thumbFile.'.'.$ext;
    				$localFile = $localTemp;
    				file_put_contents($localTemp,IO::fileSubstr($path,0,1024*600));
    			}
    			$this->thumbVideo($localFile,$thumbFile,$videoThumbTime);
    		} else {
    			if(!$localFile){
    				// $localTemp = $localFile,可能会转换失败,为避免重新下载,不删除临时文件
    				$localFile = $this->pluginLocalFile($path);	// 下载到本地文件
    			}
    			if($ext == 'ttf'){
    				$this->thumbFont($localFile,$thumbFile,$size);
    			}else{
    				$this->thumbImage($localFile,$thumbFile,$size,$ext);
    			}
    		}
    
    		// pr(file_exists($thumbFile),$localFile,$thumbFile);exit;
    		if($localTemp){ @unlink($localTemp); }
    		if(@file_exists($thumbFile)){
    			$cachePath  = IO::move($thumbFile,$this->cachePath);
    			return IO::fileOutServer($cachePath);
    		}
    		Cache::set('fileCover_'.$fileHash,'error',60);
    		del_file($thumbFile);
    	}
    	
    	private function getSize(){
    		$size = intval($this->in['size']);		
    		$sizeAllow = array(250,1200,3000);
    		for ($i=0;$i<count($sizeAllow);$i++){
    			if($i == 0 && $size <= $sizeAllow[$i]){
    				$size = $sizeAllow[$i];break;
    			}else if($size > $sizeAllow[$i - 1] && $size <= $sizeAllow[$i]){
    				$size = $sizeAllow[$i];break;
    			}else if($i == count($sizeAllow) - 1 && $size > $sizeAllow[$i]){
    				$size = $sizeAllow[$i];break;
    			}
    		}
    		return $size;
    	}
    	
    	// 获取文件 hash
    	public function localFile($path){
    		$io = IO::init($path);
    		$pathParse = KodIO::parse($path);
    		if(!$pathParse['type']) return $path;
    		if(is_array($io->pathParse) && isset($io->pathParse['truePath'])){ //协作分享处理;
    			if(file_exists($io->pathParse['truePath'])) return $io->pathParse['truePath'];
    			return false;
    		}
    		
    		$fileInfo = IO::info($path);
    		if($fileInfo['fileID']){
    			$tempInfo 	= Model('File')->fileInfo($fileInfo['fileID']);
    			$fileInfo 	= IO::info($tempInfo['path']);
    			$pathParse 	= KodIO::parse($tempInfo['path']);
    		}
    		$parent = array('path'=>'{userFav}/');
    		$fileInfo = Action('explorer.listDriver')->parsePathIO($fileInfo,$parent);
    		if($fileInfo['ioDriver'] == 'Local' && $fileInfo['ioBasePath']){
    			$base = rtrim($fileInfo['ioBasePath'],'/');
    			if(substr($base,0,2) == './') {
    				$base = substr_replace($base, BASIC_PATH, 0, 2);
    			}
    			return $base . '/' . ltrim($pathParse['param'], '/');
    		}
    		return false;
    	}
    	
    	private function thumbFont($fontFile,$cacheFile,$maxSize){
    		$textMore = '
    		可道云在线云盘
    		汉体书写信息技术标准相容
    		档案下载使用界面简单
    		支援服务升级资讯专业制作
    		创意空间快速无线上网
    		AaBbCc 0123456789AaBbCc
    		 ㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩';
    		$textMore = str_replace("\t",' ',$textMore);
    		$text = "字体ABC";$size = 200;
    		if($maxSize >= 500){
    			$text = $textMore;
    			$size = 800;
    		}
    		$im = imagecreatetruecolor($size,$size);
    		imagefill($im,0,0,imagecolorallocate($im,255,255,255));
    		$color = imagecolorallocate($im,10,10,10);
    		imagefttext($im,32,0,10,100,$color,$fontFile,$text);
    		imagejpeg($im,$cacheFile);//生成图片     
    		imagedestroy($im);
    	}
    
    	private function thumbVideo($file,$cacheFile,$videoThumbTime){
    		$command = $this->getFFmpeg();
    		if(!$command){
    			echo "Ffmpeg ".LNG("fileThumb.check.notFound");
    			return false;
    		}
    		$tempPath = $cacheFile;
    		if($GLOBALS['config']['systemOS'] == 'linux' && is_writable('/tmp/')){
    			mk_dir('/tmp/fileThumb');
    			$tempPath = '/tmp/fileThumb/'.rand_string(15).'.jpg';
    		}
    
    		$maxWidth = 800;
    		$timeAt   = $videoThumbTime ? '-ss 00:00:03' : '';
    		$script   = $command.' -i "'.$file.'" -y -f image2 '.$timeAt.' -vframes 1 '.$tempPath;
    		shell_exec($script);
    		if(!file_exists($tempPath)) return;
    
    		move_path($tempPath,$cacheFile);
    		$cm = new ImageThumb($cacheFile,'file');
    		$cm->prorate($cacheFile,$maxWidth,$maxWidth);
    	}
    
    	// imagemagick  -density 100 //耗时间,暂时去除
    	// convert -density 900 banner.psd -colorspace RGB -resample 300 -trim -thumbnail 200x200 test.jpg
    	// convert -colorspace rgb simple.pdf[0] -density 100 -sample 200x200 sample.jpg
    	private function thumbImage($file,$cacheFile,$maxSize,$ext){
    		$this->thumbImageCreate($file,$cacheFile,$maxSize,$ext);
    		$isResize = explode(',','gif,png,bmp,jpe,jpeg,jpg,heic');
    		if(in_array($ext,$isResize)) return;
    
    		ImageThumb::createThumb($cacheFile,$cacheFile,$maxSize,$maxSize);
    	}
    	
    	public function thumbImageCreate($file,$cacheFile,$maxSize,$ext){
    		$command = $this->getConvert();
    		if(!$command){
    			echo "ImageMagick ".LNG("fileThumb.check.notFound");
    			return false;
    		}
    		$size  = $maxSize.'x'.$maxSize;
    		$param = "-auto-orient -alpha off -quality 90 -size ".$size;
    		switch ($ext){
    			case 'eps':
    			case 'psb':
    			case 'psd':
    			case 'ps'://ps,ai,pdf; ==> window:缺少组件;mac:命令行可以执行,但php执行有问题
    			case 'ai':$file.= '[0]';break;
    			case 'pdf':$file.= '[0]';
    				$param = "-auto-orient -alpha remove -alpha off -quality 90 -size ".$size." -background white";
    				break; // pdf 生成缩略图透明背景变为黑色问题处理;
    			
    			/**
    			 * 生成doc/docx封面; or转图片;
    			 * 
    			 * mac: 使用liboffice=>soffice 关联convert的delegate;
    			 * centos : unoconv (yum install unoconv); 
    			 * 		转doc/docx/ppt/pptx/xls/xlsx/odt/odf为pdf; 再用convert提取某页为图片;
    			 * 		实现office预览方案之一;(中文字体copy)
    			 * 		unoconv -f pdf /data/from.docx /data/toxx.pdf;
    			 * 		// https://github.com/ScoutsGidsenVL/Pydio/blob/master/plugins/editor.imagick/class.IMagickPreviewer.php
    			 */
    			case 'ppt':
    			case 'pptx':
    			case 'doc':
    			case 'docx':$file.= '[0]';break;
    
    			// https://legacy.imagemagick.org/Usage/thumbnails/
    			case 'tif':$file.= '[0]';$param = " -flatten ";break;
    			case 'gif':
    			case 'png':
    			case 'bmp':
    			case 'jpe':
    			case 'jpeg':
    			case 'jpg':$param   = "-resize ".$size;break;
    			case 'heic':;$param = "-resize ".$size;break;
    			
    			default:
    				$dng = 'dng,cr2,erf,raf,kdc,dcr,mrw,nrw,nef,orf,pef,x3f,srf,arw,sr2';
    				$dng = $dng.',3fr,crw,dcm,fff,iiq,mdc,mef,mos,plt,ppm,raw,rw2,srw,tst';
    				if(in_array($ext,explode(',',$dng))){
    					$param = "-resize ".$size;
    					$file = 'rgb:'.$file.'[0]';
    				}
    				break;
    		}
    
    		//linux下$cacheFile不可写问题,先生成到/tmp下;再复制出来
    		$tempPath = $cacheFile;
    		if($GLOBALS['config']['systemOS'] == 'linux' && is_writable('/tmp/')){
    			mk_dir('/tmp/fileThumb');
    			$tempPath = '/tmp/fileThumb/'.rand_string(15).'.jpg';
    		}
    
    		$script = $command.' '.$param.' "'.$file.'" '.$tempPath;
    		shell_exec($script);
    		// pr($script,file_exists($tempPath));exit;
    		if(!file_exists($tempPath)) return;
    
    		move_path($tempPath,$cacheFile);
    		return true;
    	}
    	
    	
    	public function getFFmpeg(){
    		return $this->getCall('fileThumb.getFFmpeg',60,array($this,'getFFmpegNow'));
    	}
    	public function getConvert(){
    		return $this->getCall('fileThumb.getConvert',60,array($this,'getConvertNow'));
    	}
    	// Cache::getCall
    	private function getCall($key,$timeout,$call,$args = array()){
    		$result = Cache::get($key);
    		if($result || $result === '') return $result;
    		
    		$result = call_user_func_array($call,$args);
    		$result = $result ? $result : '';
    		Cache::set($key,$result,$timeout);
    		return $result;
    	}
    	
    	public function ffmpegSupportCheck($ffmpeg){
            $out = shell_exec($ffmpeg.' -v 2>&1');
            if(strstr($out,'--disable-muxer=image2')){return false;}
            return true;
        }
        public function getFFmpegNow(){
            $ffmpeg = $this->getFFmpegFind();
            if(!$ffmpeg) return false;
            return $this->ffmpegSupportCheck($ffmpeg) ? $ffmpeg:false;
    	}
    	public function getFFmpegFind(){
    		$check  = 'options';
    		$config = $this->getConfig();
    		if( $this->checkBin($config['ffmpegBin'],$check) ){
    			return $config['ffmpegBin'];
    		}
    		$result = $this->guessBinPath('ffmpeg',$check);
    		if($result){return $result;}
    		$findMore = array(
    			'/imagemagick/ffmpeg',
    			'/imagemagick/bin/ffmpeg',
    			'/ImageMagick/ffmpeg.exe',
    			'/ImageMagick-7.0.7-Q8/ffmpeg.exe',
    		);
    		foreach ($findMore as $value) {
    			$result = $this->guessBinPath($value,$check);
    			if($result){return $result;}
    		}
    		return false;
    	}
    	public function getConvertNow(){
    		$check  = 'options';
    		$config = $this->getConfig();
    		if( $this->checkBin($config['imagickBin'],$check) ){
    			return $config['imagickBin'];
    		}
    		$result = $this->guessBinPath('convert',$check);
    		if($result){
    			return $result;
    		}
    		$findMore = array(
    			'/imagemagick/convert',
    			'/imagemagick/bin/convert',
    			'/ImageMagick/convert.exe',
    			'/ImageMagick-7.0.7-Q8/convert.exe',
    		);
    		foreach ($findMore as $value) {
    			$result = $this->guessBinPath($value,$check);
    			if($result){
    				return $result;
    			}
    		}
    		return false;
    	}
    
    	/**
    	 * 查找可执行文件命令
    	 * https://github.com/taligentx/ee204/blob/master/admin/SCRIPT_server_tools_pathguess.php
    	 * @param  [type] $bin   命令文件
    	 * @param  [type] $check 找到文件后执行,结果中匹配的字符串
    	 * @return [type]        可执行命令路径
    	 */
    	private function guessBinPath($bin,$check){
    		$array = array(
    			"/bin/",
    			"/usr/local/bin/",
    			"/usr/bin/",
    			"/usr/sbin/",
    			"/usr/local/",
    			"/local/bin/",
    			"C:/Program Files/",
    			"C:/Program Files (x86)/",
    			"C:/",
    		);
    		$findArray = array();
    		foreach ($array as $value) {
    			if(file_exists($value.$bin)){
    				$file = $value.$bin;
    				if(strstr($file,' ')){
    					$file = '"'.$file.'"';
    				}
    				$findArray[] = $file;
    			}
    		}
    		if(!strstr($bin,'/')){
    			$findArray[] = $bin;
    		}
    		if(count($findArray) > 0){
    			foreach ($findArray as $file) {
    				if( $this->checkBin($file,$check) ){
    					//var_dump($file,$findArray,$check,shell_exec($file.' --help'));exit;
    					return $file;
    				}
    			}
    		}
    		return false;
    	}
    	private function checkBin($bin,$check){
    		if(!function_exists('shell_exec')) return false;
    		$result = shell_exec($bin.' --help');
    		return stripos($result,$check) > 0 ? true : false;
    	}
    }

    Powered by kodbox V1.37

    Copyright © kodcloud.com.

    Files