2024-08-31 01:03:37 +08:00

395 lines
16 KiB
PHP
Executable File

<?php
/**
getID3 获取图片,音频,视频信息 https://www.getid3.org/
修改:
0. getid3/getid3.php openfile方法474行后面,加入:
$this->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;
}