155 lines
4.7 KiB
PHP
155 lines
4.7 KiB
PHP
|
<?php
|
||
|
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
|
||
|
|
||
|
/**
|
||
|
* Ogg_Comments - Extract comments (ID3 tags) from .ogg files
|
||
|
* Created by Xymph <tm@gamers.org>
|
||
|
* Derived from Ogg.class.php v1.3e by Nicolas Ricquemaque <f1iqf@hotmail.com>
|
||
|
* For more info see: http://opensource.grisambre.net/ogg/
|
||
|
* Ogg format: http://en.wikipedia.org/wiki/Ogg
|
||
|
* Comments : http://www.xiph.org/vorbis/doc/v-comment.html
|
||
|
*
|
||
|
* v1.1: Improved get_1block URL parsing; added User-Agent to the GET request
|
||
|
* v1.0: Initial release
|
||
|
*/
|
||
|
|
||
|
define('BLOCKSIZE', 512);
|
||
|
|
||
|
class Ogg_Comments {
|
||
|
|
||
|
public $comments = array();
|
||
|
|
||
|
/**
|
||
|
* Fetches all comments from a .ogg local file or URL
|
||
|
*
|
||
|
* @param String $path
|
||
|
* The .ogg file or URL
|
||
|
* @param Boolean $utf8
|
||
|
* If true, return fields in UTF-8, otherwise in ASCII/ISO-8859-1
|
||
|
* @return Ogg_Comments
|
||
|
* If $comments is empty, no .ogg file
|
||
|
*/
|
||
|
public function Ogg_Comments($path, $utf8 = false) {
|
||
|
|
||
|
// check for local file or URL
|
||
|
if (strpos($path, '://') === false) {
|
||
|
if (!$fp = @fopen($path, 'rb'))
|
||
|
return false;
|
||
|
$file = fread($fp, BLOCKSIZE);
|
||
|
fclose($fp);
|
||
|
if ($file === false)
|
||
|
return false;
|
||
|
} else {
|
||
|
$file = $this->get_1block($path);
|
||
|
if ($file === false || $file == -1)
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// read OGG pages
|
||
|
for ($pos = 0; ($pos = strpos($file, 'OggS', $pos)) !== false; $pos++) {
|
||
|
// check stream version
|
||
|
if (ord($file[$pos+4]) != 0)
|
||
|
continue;
|
||
|
|
||
|
// compute offset of packet after header
|
||
|
$offset = $pos + 27 + ord($file[$pos+26]);
|
||
|
// check for second (== comments) packet
|
||
|
if ($this->read_intle($file, $pos+18) == 1) {
|
||
|
// check for vorbis comments
|
||
|
if (ord($file[$offset]) != 0x03 ||
|
||
|
substr($file, $offset+1, 6) != 'vorbis')
|
||
|
continue;
|
||
|
|
||
|
// read vendor string
|
||
|
$offset += 7;
|
||
|
$vndlen = $this->read_intle($file, $offset);
|
||
|
$this->comments['VENDOR'] = $this->decode_field(substr($file, $offset+4, $vndlen), $utf8);
|
||
|
// read comments count
|
||
|
$offset += 4 + $vndlen;
|
||
|
$cmtcnt = $this->read_intle($file, $offset);
|
||
|
|
||
|
// read/parse all comment fields
|
||
|
$offset += 4;
|
||
|
for ($i = 0; $i < $cmtcnt; $i++) {
|
||
|
$cmtlen = $this->read_intle($file, $offset);
|
||
|
$comment = substr($file, $offset+4, $cmtlen);
|
||
|
$offset += 4 + $cmtlen;
|
||
|
// store field name=value pair
|
||
|
$comment = explode('=', $comment, 2);
|
||
|
// check for repeated field & append
|
||
|
if (isset($this->comments[strtoupper($comment[0])]))
|
||
|
$this->comments[strtoupper($comment[0])] .= ', ' . $this->decode_field($comment[1], $utf8);
|
||
|
else
|
||
|
$this->comments[strtoupper($comment[0])] = $this->decode_field($comment[1], $utf8);
|
||
|
}
|
||
|
}
|
||
|
// check whether done
|
||
|
if (isset($this->comments['VENDOR']))
|
||
|
break;
|
||
|
}
|
||
|
} // Ogg_Comments
|
||
|
|
||
|
// Read 32-bits Little Endian integer
|
||
|
private function read_intle(&$buf, $pos) {
|
||
|
|
||
|
return (ord($buf[$pos+0]) + (ord($buf[$pos+1]) << 8) +
|
||
|
(ord($buf[$pos+2]) << 16) + (ord($buf[$pos+3]) << 24));
|
||
|
} // read_intle
|
||
|
|
||
|
// Decode comment field into specified charset
|
||
|
private function decode_field($str, $utf8) {
|
||
|
|
||
|
if ($utf8)
|
||
|
// return UTF8 or encode it in UTF8
|
||
|
return ((utf8_encode(utf8_decode($str)) == $str) ?
|
||
|
$str : utf8_encode($str));
|
||
|
else
|
||
|
// decode it to ASCII or return ASCII
|
||
|
return ((utf8_encode(utf8_decode($str)) == $str) ?
|
||
|
utf8_decode($str) : $str);
|
||
|
} // decode_field
|
||
|
|
||
|
// Simple HTTP Get 1 Block function with timeout
|
||
|
// ok: return string || error: return false || timeout: return -1
|
||
|
private function get_1block($url) {
|
||
|
|
||
|
$url = parse_url($url);
|
||
|
$port = isset($url['port']) ? $url['port'] : 80;
|
||
|
$query = isset($url['query']) ? '?' . $url['query'] : '';
|
||
|
|
||
|
$fp = @fsockopen($url['host'], $port, $errno, $errstr, 4);
|
||
|
if (!$fp)
|
||
|
return false;
|
||
|
|
||
|
$uri = '';
|
||
|
foreach (explode('/', $url['path']) as $subpath)
|
||
|
$uri .= rawurlencode($subpath) . '/';
|
||
|
$uri = substr($uri, 0, strlen($uri)-1); // strip trailing '/'
|
||
|
|
||
|
fwrite($fp, 'GET ' . $uri . $query . " HTTP/1.0\r\n" .
|
||
|
'Host: ' . $url['host'] . "\r\n" .
|
||
|
'User-Agent: Ogg_Comments (' . PHP_OS . ")\r\n\r\n");
|
||
|
stream_set_timeout($fp, 2);
|
||
|
$res = '';
|
||
|
$info['timed_out'] = false;
|
||
|
for ($i = 0; $i < 2; $i++)
|
||
|
if (feof($fp) || $info['timed_out']) {
|
||
|
break;
|
||
|
} else {
|
||
|
$res .= fread($fp, BLOCKSIZE);
|
||
|
$info = stream_get_meta_data($fp);
|
||
|
}
|
||
|
fclose($fp);
|
||
|
|
||
|
if ($info['timed_out']) {
|
||
|
return -1;
|
||
|
} else {
|
||
|
if (substr($res, 9, 3) != '200')
|
||
|
return false;
|
||
|
$page = explode("\r\n\r\n", $res, 2);
|
||
|
return trim($page[1]);
|
||
|
}
|
||
|
} // get_1block
|
||
|
} // class Ogg_Comments
|
||
|
?>
|