info['filepath']='';$this->info['filenamepath'] = $filename; //add by warlee; 1. getid3/module.audio-video.flv.php Analyze方法311行后加入 if($found_video && $found_audio && $found_meta){break;}//add by warlee; 找到后停止; 搜索:$info['playtime_seconds'] = $Duration / 1000; 2. getid3/module.graphic.gif.php Analyze方法93行后加入: return; // add by warlee; */ class GetInfo{ private static $fileTypeArray; public static function get($file){ $info = IO::info($file); return self::infoAdd($info); } public static function check(){} public static function infoAdd(&$info){ static $obj; if (!$obj) { require SDK_DIR.'/getID3/getid3/getid3.php'; $obj = new getID3(); } if(!$info || $info['type'] != 'file') return; if(!self::support($info['ext'])) return; $theFile = 'kodio://'.$info['path']; // 地址处理; $fileType = $info['ext']; try { if($info['ext'] == 'psd'){ $fileInfo = self::psdParse($theFile); $fileType = 'image'; }else if($info['ext'] == 'pdf'){ $fileInfo = self::pdfParse($theFile); }else{ $fileType = self::$fileTypeArray['extType'][$info['ext']]; $keyArray = self::$fileTypeArray['support'][$fileType]['keyMap']; $getInfo = @$obj->analyze($theFile,$info['size'],$info['name']); $fileInfo = self::parseData($getInfo,$keyArray,$info); // pr($theFile,$getInfo,$fileInfo,exif_read_data($info['path']),$fileType); // $filePath = 'data://image/jpeg;base64,'.base64_encode(StreamWrapperIO::read($theFile,0,1024*64)); // pr(exif_read_data($filePath),exif_read_data($info['path']),exif_read_data($theFile),$getInfo);exit; } } catch (Exception $e){ $fileInfo = array('fileType'=>$fileType); } if(!$fileInfo) return; $fileInfo = self::arrayValueLimit($fileInfo);//长度限制处理; $fileInfo['fileType'] = $fileType; $info['fileInfoMore'] = $fileInfo; return $info; } public static function support($ext){ if(!self::$fileTypeArray){ self::$fileTypeArray = self::fileTypeParse(); } $ext = strtolower($ext); $support = array('pdf','psd'); if(in_array($ext,$support)) return true; if(isset(self::$fileTypeArray['extType'][$ext])) return true; return false; } // 长度限制处理; public static function arrayValueLimit($fileInfo,$maxLength=5000){ if(!is_array($fileInfo)) return $fileInfo; foreach($fileInfo as $key=>$value){ if(is_string($value) && strlen($value) > $maxLength){ $fileInfo[$key] = substr($value, 0, $maxLength); }else if(is_array($value)){ $fileInfo[$key] = self::arrayValueLimit($value,$maxLength); } } return $fileInfo; } // 数据解析处理; private static function parseData($getInfo,$keyArray,$fileInfo){ $ext = $getInfo['fileformat']; $result = array(); foreach ($keyArray as $key => $matchKeyArray){ foreach($matchKeyArray as $matchKey){ if(is_array($matchKey)){ $value = self::parseData($getInfo,$matchKeyArray,$fileInfo); if($value){$result[$key] = $value;} break; } $matchKeyTrue = str_replace('@',$ext,$matchKey); $matchKeyIngore = str_replace('@.','',$matchKey); $value1 = _get($getInfo,$matchKeyTrue); $value2 = _get($getInfo,$matchKeyIngore); $value = $value1 ? $value1 : ($value2 ? $value2 : $value1); $value = is_array($value) ? $value[0] : $value; if($value){break;} } if($value || $value === 0 || $value === false){ $value = self::valueReset($matchKey,$value,$key); if($value !== null){ $result[$key] = $value; } } } $result = self::valueResetAll($result,$fileInfo); return $result; } // psd文件信息;(尺寸获取) // 其他信息: https://github.com/hasokeric/php-psd/blob/master/PSDReader.php // getimagesize : https://www.runoob.com/php/php-getimagesize.html private static function psdParse($file){ if(IO::fileSubstr($file,0,4)!='8BPS') return false; $info = getImageSize($file,$imageinfo); if(!$info) return; $result = array( 'sizeWidth' => $info[0], 'sizeHeight' => $info[1], // 'info' => $info, ); return $result; } private static function pdfParse($file){ return FileParsePdf::parse($file); } private static function fileTypeParse(){ $support = self::fileTypeArray(); $extType = array(); foreach ($support as $type => $item){ $ext = _get($item,'ext',''); $extArray = is_string($ext) ? explode(',',$ext) :$ext; if(!$extArray){continue;} foreach ($extArray as $ext){ $ext = strtolower(trim($ext)); if(!$ext) continue; $extType[$ext] = $type; } $support[$type]['ext'] = array_keys($extType); $support[$type]['keyMap'] = self::parseKeyMap($item['keyMap']); } return array('support'=>$support,'extType'=>$extType); } private static function parseKeyMap($keyMap){ foreach ($keyMap as $theKey => $matchKeys) { if(is_array($matchKeys)){ $value = self::parseKeyMap($matchKeys); if($value){$keyMap[$theKey] = $value;} continue; } $keyArray = explode(',',$matchKeys); $keyArrayResult = array(); foreach ($keyArray as $matchKey){ $matchKey = trim($matchKey); if(!$matchKey) continue; // 组合key自动转为数组处理; tags.[id3v1|id3v2|ape].title if(preg_match('/\[(.*)\]/',$matchKey,$matchs)){ // 组合key; $replaceArray = explode('|',$matchs[1]); foreach ($replaceArray as $addKey) { $keyArrayResult[] = str_replace($matchs[0],$addKey,$matchKey); } }else{ $keyArrayResult[] = $matchKey; } } $keyMap[$theKey] = $keyArrayResult; } return $keyMap; } private static function valueResetAll($info,$fileInfo){ if(isset($info['playtime'])){ $time = ceil($info['playtime']); $hour = intval($time / 3600); $minute = intval(($time - $hour*3600) / 60); $second = $time % 60; $info['playtimeShow'] = sprintf('%02d:%02d:%02d',$hour,$minute,$second); if($hour == 0){ $info['playtimeShow'] = sprintf('%02d:%02d',$minute,$second); } } // 音乐专辑封面处理; $audioImage = _get($info,'tags.image'); if($audioImage){ $cacheFile = IO_PATH_SYSTEM_TEMP . 'thumb/audio/'.KodIO::hashPath($fileInfo).'.jpg'; $info['fileThumb'] = Action('toolsCommonPlugin')->pluginCacheFileSet($cacheFile,$audioImage); unset($info['tags']['image']); } // 编码处理; $audioTitle = _get($info,'tags.title',''); $audioArtist= _get($info,'tags.artist',''); $audioAlbum = _get($info,'tags.album',''); $tagInfo = $audioTitle.$audioArtist.$audioAlbum; if($tagInfo && get_charset($tagInfo) != 'utf-8'){ $info['tags']['title'] = iconv_to($info['tags']['title'],get_charset($tagInfo),'utf-8'); $info['tags']['artist'] = iconv_to($info['tags']['artist'],get_charset($tagInfo),'utf-8'); $info['tags']['album'] = iconv_to($info['tags']['album'],get_charset($tagInfo),'utf-8'); } return $info; } /** * 数值处理 * 时间处理; 时间戳:大圣归来.mp4; 负数:3G2 Video.3g2; * 音乐封面图片加入系统缓存处理; hashSimple=> img=>url; */ private static function valueReset($key,$value,$newKey){ $timeFormate = 'Y-m-d H:i:s'; switch ($key) { case '@.exif.EXIF.ColorSpace':$value = $value == '1'?'sRGB':'RGB';break; case 'audio.channels':$value = $value ? $value:null;break; // case 'audio.bitrate': // case 'video.bitrate':$value = round($value / 1000) .' kbps';break; default:break; } switch ($newKey) { case 'frameRate':$value = round($value,2);break; case 'createTime': case 'modifyTime': if($value < 0){ $value = null; }else if(is_numeric($value)){ $value = date($timeFormate,$value); } break; default:break; } return $value; } // 数据整理; private static function fileTypeArray(){ $gps = array(//经度,东西N/E;纬度,南北纬S/N;海拔 'longitude' => '@.exif.GPS.computed.longitude,tags.quicktime.gps_longitude', 'latitude' => '@.exif.GPS.computed.latitude,tags.quicktime.gps_latitude', 'altitude' => '@.exif.GPS.computed.altitude,tags.quicktime.gps_altitude', ); return array( // @:代表替换为扩展名; eg: @.exif.IFD0.Software 在png下代表 png.exif.IFD0.Software // , 冒号代表多个取值时从前到后依次获取直到满足; eg: // [aa|bb] 自动展开占位; eg: tags.[id3v1|id3v2].title => tags.id3v1.title,tags.id3v2.title // 没有key则不取该值; 冒号依次向后取到key为止; 取得值为数组则取第一个值; // exif详解: https://www.cnblogs.com/billgore/p/4301622.html // https://m.sojson.com/image/exif.html 'image' => array( 'ext' => 'jpg,jpeg,png,gif,bmp,tiff,tif,psd,pcd,svg,efax,webp,'. // exif解析处理;TODO; 'cr2,erf,kdc,dcr,dng,nrw,nef,orf,rw2,pef,srw,arw,sr2,heic,heif,hevc', // heic,heif,hevc 'keyMap' => array( 'sizeWidth' => 'video.resolution_x', 'sizeHeight' => 'video.resolution_y', 'resolutionX' => '@.exif.IFD0.XResolutionx,@.exif.IFD0.XResolution', // 分辨率 dpi 'resolutionY' => '@.exif.IFD0.XResolutiony,@.exif.IFD0.YResolution', // 分辨率 dpi 'createTime' => '@.exif.IFD0.DateTime,@.EXIF.DateTimeOriginal, @.exif.EXIF.DateTimeOriginal, xmp.xmp.CreateDate,tags.@.datetime', // 拍摄日期 'modifyTime' => '.xmp.xmp.ModifyDate', // 修改日期 'orientation' => '@.exif.IFD0.Orientation', // 方向 'software' => '@.exif.IFD0.Software, tags.@.Software,tags.@.software, xmp.xmp.CreatorTool', // 内容创建者 'bitPerSample' => 'video.bits_per_sample', // 位深度 'bitsPerPixel' => '@.header.bits_per_pixel,@.header.raw.bits_per_pixel',//位深度 // 色彩空间;色彩描述文件; 'colorSpace' => '@.exif.EXIF.ColorSpace,@.sRGB.header.type_text, @.IHDR.color_type,@.header.compression', // gif; https://blog.csdn.net/yuejisuo1948/article/details/83617359 'colorSize' => '@.header.global_color_size', 'animated' => '@.animation.animated', // gps 'gps' => $gps, 'deviceMake' => '@.exif.IFD0.Make,tags.@.make', // 设备制造商 'deviceType' => '@.exif.IFD0.Model,tags.@.model', // 设备型号 'imageDesc' => '@.exif.IFD0.ImageDescription,tags.@.imagedescription', // 图片说明 'imageArtist' => '@.exif.IFD0.artist,tags.@.artist', // 图片作者 // 拍摄信息; https://blog.csdn.net/dreamboycx/article/details/40591875 'camera' => array( 'ApertureFNumber' =>'@.exif.COMPUTED.ApertureFNumber,exif.EXIF.FNumber', // 光圈数; f/2.2 'ApertureValue' =>'@.exif.EXIF.ApertureValue', // 光圈值; 2.275 'ShutterSpeedValue' =>'@.exif.EXIF.ShutterSpeedValue', // 快门速度; 5.05 'ExposureTime' =>'@.exif.EXIF.ExposureTime', // 曝光时间s; 0.033 转换为:1/33 'FocalLength' =>'@.exif.EXIF.FocalLength', // 焦距 mm; eg:4.15 'FocalLengthIn35mmFilm' =>'@.exif.EXIF.FocalLengthIn35mmFilm', // 等价35mm焦距 mm; eg:29 'FocusDistance' =>'@.exif.COMPUTED.FocusDistance', // 对焦距离 'ISOSpeedRatings' =>'@.exif.EXIF.ISOSpeedRatings', // ISO感光度 'WhiteBalance' =>'@.exif.EXIF.WhiteBalance', // 白平衡 1-手动;0-自动 'ExposureMode' =>'@.exif.EXIF.ExposureMode', // 曝光模式 1-手动;0-自动 'ExposureBiasValue' =>'@.exif.EXIF.ExposureBiasValueEV', // 曝光补偿 ), ), ), 'audio' => array( 'ext' => 'aac,adts,au,amr,avr,bonk,dsf,dss,ff,dts,flac,la,lpac,midi,mac,it,xm,s3m,'. 'mpc,mp3,ofr,rkau,shn,tak,wav,wma,m4a,ogg,vqf,wv,voc,tta', 'keyMap' => array( 'playtime' => 'playtime_seconds,@.pageheader.0.page_length', // 时长,向上取整; 'createTime' => 'quicktime.timestamps_unix.create.moov mvhd', // 内容创建时间 'modifyTime' => 'quicktime.timestamps_unix.modify.moov mvhd', // 内容修改时间 'software' => 'audio.matroska.encoder, tags.matroska.[encoder|writingapp,handler_name], tags.quicktime.[software|encoding_tool], audio.encoder', // 编码软件 'rate' => 'audio.sample_rate', // 采样速率 'channel' => 'audio.channels', // 音频声道; 为1则不显示; 'dataformat' => 'audio.codec', // 编解码器 h264 'bitPerSimple' => 'audio.bits_per_sample', // 每个样本的位数 'bitrate' => 'audio.bitrate,@.pageheader.theora.nominal_bitrate', // 比特率 kbps // 'channelmode' => 'audio.channelmode', // 'tags' => array( 'title' => '[id3v1|id3v2|ape|vorbiscomment|quicktime].title', // 标题 'artist' => '[id3v1|id3v2|ape|vorbiscomment|quicktime].artist', // 作者/艺术家 'album' => '[id3v1|id3v2|ape|vorbiscomment|quicktime].album', // 专辑 'genre' => '[id3v1|id3v2|ape|vorbiscomment|quicktime].genre', // 风格 'year' => '[id3v1|id3v2|ape|vorbiscomment|quicktime].year', // 年代 'track' => '[id3v1|id3v2|ape|vorbiscomment|quicktime].track_number', // 音轨 'image' => 'comments.picture.0.data', // 专辑封面 ), ) ), 'video' => array( 'ext' => 'mp4,flv,f4v,wmv,ogv,mov,avi,rmvb,mkv,rm,webm,m4v,real,swf,'. '3g2,3gp,asf,bink,ivf,nsv,wtv,mts,mpe,mpeg,mpg,vob', //m2ts ts 'keyMap' => array( //TODO; 编解码器; 'sizeWidth' => '@.meta.onMetaData.width,video.resolution_x', 'sizeHeight' => '@.meta.onMetaData.height,video.resolution_y', 'playtime' => 'playtime_seconds', // 时长,向上取整; 'createTime' => 'quicktime.timestamps_unix.create.moov mvhd,tags.matroska.creation_time',// 内容创建时间 'modifyTime' => 'quicktime.timestamps_unix.modify.moov mvhd,tags.matroska.modify_time', // 内容修改时间 'frameRate' => 'video.frame_rate', // 帧率 向上取整; 'bitrate' => '@.video.raw.bitrate,video.bitrate', // 数据比特率 kbps 'gps' => $gps, 'dataformat' => 'video.fourcc_lookup, video.streams.[01|1].dataformat, video.streams.[01|1].fourcc, video.streams.[01|1].codec, quicktime.video.codec_fourcc_lookup, video.fourcc,video.codec', // 编解码器 h264 'software' => 'tags.matroska.[writingapp|handler_name|encoder], @.meta.onMetaData.[encoder|metadatacreator|creator], tags.[riff|quicktime].software, tags.@.encodingsettings, @.comments.encodingsettings, tags.quicktime.encoding_tool', // 编码软件 'audio' => array( 'channel' => 'audio.channels', // 音频声道; 1 (单声道),2 (立体声),3+ (多声道) 'channelmode' => 'audio.channelmode', // 音频模式 'rate' => 'audio.sample_rate', // 声音.采样速率 'bitrate' => 'audio.bitrate', // 声音.比特率 kbps 'dataformat' => 'audio.codec,audio.dataformat', // 音频 编码译码器; 'bitPerSimple' => 'audio.bits_per_sample', // 每个样本的位数 ), ), ), 'archive' => array( // 'ext' => 'zip,tar,rar,gz,7z,tar,xz,par2,szip,cue,iso,hpk', 'keyMap' => array( 'unzipSize' => 'uncompressed_size', // 解压后大小 'childrenCount' => 'entries_count', // 字内容数 ) ), 'torrent' => array( // 'ext' => 'torrent', 'keyMap' => array() )); } } // exif_read_data 读取streamwrapper的文件会异常; // 参考: https://github.com/avalanche123/Imagine/issues/728 function exif_read_data_io($file,$sections = null,$asArray=false,$readThumbnail=false){ $fileStr = StreamWrapperIO::read($file,0,1024*256); $fileData = 'data://image/jpeg;base64,'.base64_encode($fileStr); $result = exif_read_data($fileData,$sections,$asArray,$readThumbnail); return $result; } function getimagesize_io($filePath,&$info=false){ $fileStr = StreamWrapperIO::read($filePath,0,1024*2560); $fileData = 'data://image/jpeg;base64,'.base64_encode($fileStr); $result = getimagesize($fileData,$info); return $result; }