Add $tags list with combined tags. Improve isValidAudio()
This commit is contained in:
parent
9049767016
commit
d21f67a9e0
@ -147,13 +147,8 @@ class Mp3InfoConsoleRunner {
|
|||||||
.PHP_EOL;
|
.PHP_EOL;
|
||||||
|
|
||||||
if ($id3v2 && !empty($audio->tags2)) {
|
if ($id3v2 && !empty($audio->tags2)) {
|
||||||
foreach ($audio->tags2 as $tag=>$value) {
|
foreach ($audio->tags as $tag => $value) {
|
||||||
echo ' '.$tag.': ';
|
echo ' '.$tag.': ';
|
||||||
if ($tag == 'COMM') {
|
|
||||||
foreach ($value as $lang => $comment) {
|
|
||||||
echo '['.$lang.'] '.$comment['short'].'; '.$comment['actual'].PHP_EOL;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
echo self::convertToNativeEncoding($value).PHP_EOL;
|
echo self::convertToNativeEncoding($value).PHP_EOL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
115
src/Mp3Info.php
115
src/Mp3Info.php
@ -84,18 +84,6 @@ class Mp3Info {
|
|||||||
*/
|
*/
|
||||||
public $audioSize;
|
public $audioSize;
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains audio file name
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $_fileName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains file size
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $_fileSize;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Audio duration in seconds.microseconds (e.g. 3603.0171428571)
|
* Audio duration in seconds.microseconds (e.g. 3603.0171428571)
|
||||||
* @var float
|
* @var float
|
||||||
@ -132,10 +120,9 @@ class Mp3Info {
|
|||||||
public $channel;
|
public $channel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of audio frames in file
|
* @var array Unified list of tags (id3v1 and id3v2 united)
|
||||||
* @var int
|
|
||||||
*/
|
*/
|
||||||
public $framesCount = 0;
|
public $tags = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Audio tags ver. 1 (aka id3v1)
|
* Audio tags ver. 1 (aka id3v1)
|
||||||
@ -149,16 +136,12 @@ class Mp3Info {
|
|||||||
*/
|
*/
|
||||||
public $tags2 = [];
|
public $tags2 = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int|null Size of id3-block
|
|
||||||
*/
|
|
||||||
public $id3Size;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Major version of id3v2 tag (if id3v2 present) (2 or 3 or 4)
|
* Major version of id3v2 tag (if id3v2 present) (2 or 3 or 4)
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
public $id3v2MajorVersion;
|
public $id3v2MajorVersion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minor version of id3v2 tag (if id3v2 present)
|
* Minor version of id3v2 tag (if id3v2 present)
|
||||||
* @var int
|
* @var int
|
||||||
@ -177,6 +160,23 @@ class Mp3Info {
|
|||||||
*/
|
*/
|
||||||
public $id3v2TagsFlags = [];
|
public $id3v2TagsFlags = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains audio file name
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $_fileName;
|
||||||
|
/**
|
||||||
|
* Contains file size
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $_fileSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of audio frames in file
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $_framesCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains time spent to read&extract audio information.
|
* Contains time spent to read&extract audio information.
|
||||||
* @var float
|
* @var float
|
||||||
@ -187,13 +187,12 @@ class Mp3Info {
|
|||||||
* Calculated frame size for Constant Bit Rate
|
* Calculated frame size for Constant Bit Rate
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
private $__cbrFrameSize;
|
private $_cbrFrameSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frame size for Variable Bit Rate
|
* @var int|null Size of id3v2-data
|
||||||
* @var int
|
|
||||||
*/
|
*/
|
||||||
private $__vbrFrameSize;
|
public $_id3Size;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* $mode is self::META, self::TAGS or their combination.
|
* $mode is self::META, self::TAGS or their combination.
|
||||||
@ -232,13 +231,12 @@ class Mp3Info {
|
|||||||
$time = microtime(true);
|
$time = microtime(true);
|
||||||
$fp = fopen($filename, 'rb');
|
$fp = fopen($filename, 'rb');
|
||||||
|
|
||||||
/** Size of audio data (exclude tags size)
|
/** @var int Size of audio data (exclude tags size) */
|
||||||
* @var int */
|
|
||||||
$audioSize = $fileSize;
|
$audioSize = $fileSize;
|
||||||
|
|
||||||
// parse tags
|
// parse tags
|
||||||
if (fread($fp, 3) == self::TAG2_SYNC) {
|
if (fread($fp, 3) == self::TAG2_SYNC) {
|
||||||
if ($mode & self::TAGS) $audioSize -= ($this->id3Size = $this->readId3v2Body($fp));
|
if ($mode & self::TAGS) $audioSize -= ($this->_id3Size = $this->readId3v2Body($fp));
|
||||||
else {
|
else {
|
||||||
fseek($fp, 2, SEEK_CUR); // 2 bytes of tag version
|
fseek($fp, 2, SEEK_CUR); // 2 bytes of tag version
|
||||||
fseek($fp, 1, SEEK_CUR); // 1 byte of tag flags
|
fseek($fp, 1, SEEK_CUR); // 1 byte of tag flags
|
||||||
@ -247,7 +245,7 @@ class Mp3Info {
|
|||||||
$value = substr(str_pad(base_convert($value, 10, 2), 8, 0, STR_PAD_LEFT), 1);
|
$value = substr(str_pad(base_convert($value, 10, 2), 8, 0, STR_PAD_LEFT), 1);
|
||||||
});
|
});
|
||||||
$size = bindec(implode(null, $sizeBytes)) + 10;
|
$size = bindec(implode(null, $sizeBytes)) + 10;
|
||||||
$audioSize -= ($this->id3Size = $size);
|
$audioSize -= ($this->_id3Size = $size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,24 +255,28 @@ class Mp3Info {
|
|||||||
else $audioSize -= 128;
|
else $audioSize -= 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($mode & self::TAGS) {
|
||||||
|
$this->fillTags();
|
||||||
|
}
|
||||||
|
|
||||||
fseek($fp, 0);
|
fseek($fp, 0);
|
||||||
// audio meta
|
// audio meta
|
||||||
if ($mode & self::META) {
|
if ($mode & self::META) {
|
||||||
if ($this->id3Size !== null) fseek($fp, $this->id3Size);
|
if ($this->_id3Size !== null) fseek($fp, $this->_id3Size);
|
||||||
/**
|
/**
|
||||||
* First frame can lie. Need to fix in future.
|
* First frame can lie. Need to fix in future.
|
||||||
* @link https://github.com/wapmorgan/Mp3Info/issues/13#issuecomment-447470813
|
* @link https://github.com/wapmorgan/Mp3Info/issues/13#issuecomment-447470813
|
||||||
*/
|
*/
|
||||||
$framesCount = $this->readMpegFrame($fp);
|
$framesCount = $this->readMpegFrame($fp);
|
||||||
|
|
||||||
$this->framesCount = $framesCount !== null
|
$this->_framesCount = $framesCount !== null
|
||||||
? $framesCount
|
? $framesCount
|
||||||
: ceil($audioSize / $this->__cbrFrameSize);
|
: ceil($audioSize / $this->_cbrFrameSize);
|
||||||
|
|
||||||
// recalculate average bit rate in vbr case
|
// recalculate average bit rate in vbr case
|
||||||
if ($this->isVbr && !is_null($framesCount)) {
|
if ($this->isVbr && $framesCount !== null) {
|
||||||
$avgFrameSize = $audioSize / $framesCount;
|
$avgFrameSize = $audioSize / $framesCount;
|
||||||
$this->bitRate = $avgFrameSize * $this->sampleRate / (1000 * $this->layerVersion == 3 ? 12 : 144);
|
$this->bitRate = $avgFrameSize * $this->sampleRate / (1000 * $this->layerVersion == self::LAYER_3 ? 12 : 144);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The faster way to detect audio duration:
|
// The faster way to detect audio duration:
|
||||||
@ -284,7 +286,7 @@ class Mp3Info {
|
|||||||
$samples_in_second = floor($samples_in_second * $this->vbrProperties['quality'] / 100);
|
$samples_in_second = floor($samples_in_second * $this->vbrProperties['quality'] / 100);
|
||||||
}
|
}
|
||||||
// Calculate total number of audio samples (framesCount * sampleInFrameCount) / samplesInSecondCount
|
// Calculate total number of audio samples (framesCount * sampleInFrameCount) / samplesInSecondCount
|
||||||
$this->duration = ($this->framesCount - 1) * $samples_in_second / $this->sampleRate;
|
$this->duration = ($this->_framesCount - 1) * $samples_in_second / $this->sampleRate;
|
||||||
}
|
}
|
||||||
fclose($fp);
|
fclose($fp);
|
||||||
|
|
||||||
@ -369,12 +371,12 @@ class Mp3Info {
|
|||||||
|
|
||||||
// go to the end of frame
|
// go to the end of frame
|
||||||
if ($this->layerVersion == self::LAYER_1) {
|
if ($this->layerVersion == self::LAYER_1) {
|
||||||
$this->__cbrFrameSize = floor((12 * $this->bitRate / $this->sampleRate + ($header_bytes[2] >> 1 & 0b1)) * 4);
|
$this->_cbrFrameSize = floor((12 * $this->bitRate / $this->sampleRate + ($header_bytes[2] >> 1 & 0b1)) * 4);
|
||||||
} else {
|
} else {
|
||||||
$this->__cbrFrameSize = floor(144 * $this->bitRate / $this->sampleRate + ($header_bytes[2] >> 1 & 0b1));
|
$this->_cbrFrameSize = floor(144 * $this->bitRate / $this->sampleRate + ($header_bytes[2] >> 1 & 0b1));
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek($fp, $pos + $this->__cbrFrameSize);
|
fseek($fp, $pos + $this->_cbrFrameSize);
|
||||||
|
|
||||||
return isset($this->vbrProperties['frames']) ? $this->vbrProperties['frames'] : null;
|
return isset($this->vbrProperties['frames']) ? $this->vbrProperties['frames'] : null;
|
||||||
}
|
}
|
||||||
@ -693,19 +695,25 @@ class Mp3Info {
|
|||||||
/**
|
/**
|
||||||
* Simple function that checks mpeg-audio correctness of given file.
|
* Simple function that checks mpeg-audio correctness of given file.
|
||||||
* Actually it checks that first 3 bytes of file is a id3v2 tag mark or
|
* Actually it checks that first 3 bytes of file is a id3v2 tag mark or
|
||||||
* that first 11 bits of file is a frame header sync mark. To perform full
|
* that first 11 bits of file is a frame header sync mark or that 3 bytes on -128 position of file is id3v1 tag.
|
||||||
* test create an instance of Mp3Info with given file.
|
* To perform full test create an instance of Mp3Info with given file.
|
||||||
*
|
*
|
||||||
* @param string $filename File to be tested.
|
* @param string $filename File to be tested.
|
||||||
*
|
* @return boolean True if file looks that correct mpeg audio, False otherwise.
|
||||||
* @return boolean True if file is looks correct, False otherwise.
|
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function isValidAudio($filename) {
|
public static function isValidAudio($filename) {
|
||||||
if (!file_exists($filename))
|
if (!file_exists($filename))
|
||||||
throw new Exception('File '.$filename.' is not present!');
|
throw new Exception('File '.$filename.' is not present!');
|
||||||
|
|
||||||
$raw = file_get_contents($filename, false, null, 0, 3);
|
$raw = file_get_contents($filename, false, null, 0, 3);
|
||||||
return ($raw == self::TAG2_SYNC || (self::FRAME_SYNC == (unpack('n*', $raw)[1] & self::FRAME_SYNC)));
|
return $raw == self::TAG2_SYNC // id3v2 tag
|
||||||
|
|| (self::FRAME_SYNC == (unpack('n*', $raw)[1] & self::FRAME_SYNC)) // mpeg header tag
|
||||||
|
|| (
|
||||||
|
filesize($filename) > 128
|
||||||
|
&& file_get_contents($filename, false, null, -128, 3) === self::TAG1_SYNC
|
||||||
|
) // id3v1 tag
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -723,4 +731,27 @@ class Mp3Info {
|
|||||||
else # utf-16
|
else # utf-16
|
||||||
return mb_convert_encoding($data['information']."\00", 'utf-8', 'utf-16');
|
return mb_convert_encoding($data['information']."\00", 'utf-8', 'utf-16');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills `tags` property with values id3v2 and id3v1 tags.
|
||||||
|
*/
|
||||||
|
protected function fillTags()
|
||||||
|
{
|
||||||
|
foreach ([
|
||||||
|
'song' => 'TIT2',
|
||||||
|
'artist' => 'TPE1',
|
||||||
|
'album' => 'TALB',
|
||||||
|
'year' => 'TYER',
|
||||||
|
'comment' => 'COMM',
|
||||||
|
'track' => 'TRCK',
|
||||||
|
'genre' => 'TCON',
|
||||||
|
] as $tag => $id3v2_tag) {
|
||||||
|
if (!isset($this->tags2[$id3v2_tag]) && (!isset($this->tags1[$tag]) || empty($this->tags1[$tag])))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$this->tags[$tag] = isset($this->tags2[$id3v2_tag])
|
||||||
|
? ($id3v2_tag === 'COMM' ? current($this->tags2[$id3v2_tag])['actual'] : $this->tags2[$id3v2_tag])
|
||||||
|
: $this->tags1[$tag];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user