Ability to scan remote files (downloads the whole file; #19) and checking file before scanning
This commit is contained in:
parent
9e9e29fc3f
commit
b53a5e9368
11
bin/mp3scan
11
bin/mp3scan
@ -61,8 +61,8 @@ class Mp3InfoConsoleRunner {
|
|||||||
echo sprintf($this->songRowTempalte, 'File name', 'dur.', 'bitrate', 'sample', 'song', 'artist', 'track',
|
echo sprintf($this->songRowTempalte, 'File name', 'dur.', 'bitrate', 'sample', 'song', 'artist', 'track',
|
||||||
'time').PHP_EOL;
|
'time').PHP_EOL;
|
||||||
|
|
||||||
foreach ($fileNames as $file) {
|
foreach ($fileNames as $originalFile) {
|
||||||
$file = realpath($file);
|
$file = realpath($originalFile);
|
||||||
if (is_dir($file)) {
|
if (is_dir($file)) {
|
||||||
echo $file.':'.PHP_EOL;
|
echo $file.':'.PHP_EOL;
|
||||||
foreach (glob(rtrim($file, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.'*.mp3') as $f) {
|
foreach (glob(rtrim($file, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.'*.mp3') as $f) {
|
||||||
@ -74,6 +74,8 @@ class Mp3InfoConsoleRunner {
|
|||||||
} else if (is_file($file)) {
|
} else if (is_file($file)) {
|
||||||
$this->analyze($file, true, $verbose);
|
$this->analyze($file, true, $verbose);
|
||||||
if ($this->compareWithId3) $this->analyzeId3($file);
|
if ($this->compareWithId3) $this->analyzeId3($file);
|
||||||
|
} else if (strpos($originalFile, '://') !== false) {
|
||||||
|
$this->analyze($originalFile, true, $verbose);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,11 +137,12 @@ class Mp3InfoConsoleRunner {
|
|||||||
* @return null|void
|
* @return null|void
|
||||||
*/
|
*/
|
||||||
protected function analyze($filename, $id3v2 = false, $verbose = false) {
|
protected function analyze($filename, $id3v2 = false, $verbose = false) {
|
||||||
if (!is_readable($filename)) return;
|
if (!is_readable($filename) && strpos($filename, '://') === false) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$audio = new Mp3Info($filename, true);
|
$audio = new Mp3Info($filename, true);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
var_dump("Exception when parsing ".$filename.': '.$e->getMessage());
|
echo "Exception when parsing ".$filename.': '.$e->getMessage().PHP_EOL;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
103
src/Mp3Info.php
103
src/Mp3Info.php
@ -212,11 +212,23 @@ class Mp3Info {
|
|||||||
if (self::$_sampleRateTable === null)
|
if (self::$_sampleRateTable === null)
|
||||||
self::$_sampleRateTable = require dirname(__FILE__).'/../data/sampleRateTable.php';
|
self::$_sampleRateTable = require dirname(__FILE__).'/../data/sampleRateTable.php';
|
||||||
|
|
||||||
if (!file_exists($filename))
|
$this->_fileName = $filename;
|
||||||
throw new \Exception('File '.$filename.' is not present!');
|
|
||||||
|
if (strpos($filename, '://') !== false) {
|
||||||
|
$this->_fileSize = static::getUrlContentLength($filename);
|
||||||
|
} else {
|
||||||
|
if (!file_exists($filename)) {
|
||||||
|
throw new \Exception('File ' . $filename . ' is not present!');
|
||||||
|
}
|
||||||
|
$this->_fileSize = filesize($filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!static::isValidAudio($filename)) {
|
||||||
|
throw new \Exception('File ' . $filename . ' is not mpeg/audio!');
|
||||||
|
}
|
||||||
|
|
||||||
$mode = $parseTags ? self::META | self::TAGS : self::META;
|
$mode = $parseTags ? self::META | self::TAGS : self::META;
|
||||||
$this->audioSize = $this->parseAudio($this->_fileName = $filename, $this->_fileSize = filesize($filename), $mode);
|
$this->audioSize = $this->parseAudio($this->_fileName, $this->_fileSize, $mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -241,15 +253,23 @@ class Mp3Info {
|
|||||||
* ID3V2 TAG - provides a lot of meta data. [optional]
|
* ID3V2 TAG - provides a lot of meta data. [optional]
|
||||||
* MPEG AUDIO FRAMES - contains audio data. A frame consists of a frame header and a frame data. The first frame may contain extra information about mp3 (marked with "Xing" or "Info" string). Rest of frames can contain only audio data.
|
* MPEG AUDIO FRAMES - contains audio data. A frame consists of a frame header and a frame data. The first frame may contain extra information about mp3 (marked with "Xing" or "Info" string). Rest of frames can contain only audio data.
|
||||||
* ID3V1 TAG - provides a few of meta data. [optional]
|
* ID3V1 TAG - provides a few of meta data. [optional]
|
||||||
* @param $filename
|
* @param string $filename
|
||||||
* @param $fileSize
|
* @param int $fileSize
|
||||||
* @param $mode
|
* @param int $mode
|
||||||
* @return float|int
|
* @return float|int
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
private function parseAudio($filename, $fileSize, $mode) {
|
private function parseAudio($filename, $fileSize, $mode) {
|
||||||
$time = microtime(true);
|
$time = microtime(true);
|
||||||
$fp = fopen($filename, 'rb');
|
|
||||||
|
// create temp storage for media
|
||||||
|
if (strpos($filename, '://') !== false) {
|
||||||
|
$fp = fopen('php://memory', 'rwb');
|
||||||
|
fwrite($fp, file_get_contents($filename));
|
||||||
|
rewind($fp);
|
||||||
|
} else {
|
||||||
|
$fp = fopen($filename, 'rb');
|
||||||
|
}
|
||||||
|
|
||||||
/** @var int Size of audio data (exclude tags size) */
|
/** @var int Size of audio data (exclude tags size) */
|
||||||
$audioSize = $fileSize;
|
$audioSize = $fileSize;
|
||||||
@ -937,30 +957,6 @@ class Mp3Info {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* that first 11 bits of file is a frame header sync mark or that 3 bytes on -128 position of file is id3v1 tag.
|
|
||||||
* To perform full test create an instance of Mp3Info with given file.
|
|
||||||
*
|
|
||||||
* @param string $filename File to be tested.
|
|
||||||
* @return boolean True if file looks that correct mpeg audio, False otherwise.
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public static function isValidAudio($filename) {
|
|
||||||
if (!file_exists($filename))
|
|
||||||
throw new Exception('File '.$filename.' is not present!');
|
|
||||||
|
|
||||||
$raw = file_get_contents($filename, false, null, 0, 3);
|
|
||||||
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
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $frameSize
|
* @param $frameSize
|
||||||
* @param $raw
|
* @param $raw
|
||||||
@ -1028,4 +1024,49 @@ class Mp3Info {
|
|||||||
: $this->tags1[$tag];
|
: $this->tags1[$tag];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* that first 11 bits of file is a frame header sync mark or that 3 bytes on -128 position of file is id3v1 tag.
|
||||||
|
* To perform full test create an instance of Mp3Info with given file.
|
||||||
|
*
|
||||||
|
* @param string $filename File to be tested.
|
||||||
|
* @return boolean True if file looks that correct mpeg audio, False otherwise.
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function isValidAudio($filename) {
|
||||||
|
if (!file_exists($filename) && strpos($filename, '://')) {
|
||||||
|
throw new Exception('File ' . $filename . ' is not present!');
|
||||||
|
}
|
||||||
|
|
||||||
|
$filesize = file_exists($filename) ? filesize($filename) : static::getUrlContentLength($filename);
|
||||||
|
|
||||||
|
$raw = file_get_contents($filename, false, null, 0, 3);
|
||||||
|
return $raw === self::TAG2_SYNC // id3v2 tag
|
||||||
|
|| (self::FRAME_SYNC === (unpack('n*', $raw)[1] & self::FRAME_SYNC)) // mpeg header tag
|
||||||
|
|| (
|
||||||
|
$filesize > 128
|
||||||
|
&& file_get_contents($filename, false, null, -128, 3) === self::TAG1_SYNC
|
||||||
|
) // id3v1 tag
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $url
|
||||||
|
* @return int|mixed|string
|
||||||
|
*/
|
||||||
|
public static function getUrlContentLength($url) {
|
||||||
|
$context = stream_context_create(['http' => ['method' => 'HEAD']]);
|
||||||
|
$head = array_change_key_case(get_headers($url, true, $context));
|
||||||
|
// content-length of download (in bytes), read from Content-Length: field
|
||||||
|
$clen = isset($head['content-length']) ? $head['content-length'] : 0;
|
||||||
|
|
||||||
|
// cannot retrieve file size, return "-1"
|
||||||
|
if (!$clen) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $clen; // return size in bytes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user