From fa507390a3880eaf3e43fc60e413b64b258f5707 Mon Sep 17 00:00:00 2001 From: svollbehr Date: Sun, 26 Apr 2009 18:27:54 +0000 Subject: [PATCH] Add Zend_Media_Id3v1 class proposal git-svn-id: http://php-reader.googlecode.com/svn/branches/zend@152 51a70ab9-7547-0410-9469-37e369ee0574 --- src/ID3v1.php | 361 --------------------------- src/Zend/Media/Exception.php | 39 +++ src/Zend/Media/Id3/Exception.php | 40 +++ src/Zend/Media/Id3v1.php | 405 +++++++++++++++++++++++++++++++ 4 files changed, 484 insertions(+), 361 deletions(-) delete mode 100644 src/ID3v1.php create mode 100644 src/Zend/Media/Exception.php create mode 100644 src/Zend/Media/Id3/Exception.php create mode 100644 src/Zend/Media/Id3v1.php diff --git a/src/ID3v1.php b/src/ID3v1.php deleted file mode 100644 index b2263cd..0000000 --- a/src/ID3v1.php +++ /dev/null @@ -1,361 +0,0 @@ - - * @author Ryan Butterfield - * @copyright Copyright (c) 2008-2009 The PHP Reader Project Workgroup - * @license http://code.google.com/p/php-reader/wiki/License New BSD License - * @version $Rev$ - */ -final class ID3v1 -{ - /** @var string */ - private $_title; - - /** @var string */ - private $_artist; - - /** @var string */ - private $_album; - - /** @var string */ - private $_year; - - /** @var string */ - private $_comment; - - /** @var integer */ - private $_track; - - /** @var integer */ - private $_genre = 255; - - /** - * The genre list. - * - * @var Array - */ - public static $genres = array - ("Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", - "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", - "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", - "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", - "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", - "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", - "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", - "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", - "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", - "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40", "Christian Rap", - "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", - "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", - "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", - "Hard Rock", "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", - "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", - "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", - "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", - "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", - "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", - "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", - "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", - "Dance Hall", 255 => "Unknown"); - - /** @var Reader */ - private $_reader; - - /** @var string */ - private $_filename = false; - - /** - * Constructs the ID3v1 class with given file. The file is not mandatory - * argument and may be omitted. A new tag can be written to a file also by - * giving the filename to the {@link #write} method of this class. - * - * @param string|Reader $filename The path to the file, file descriptor of an - * opened file, or {@link Reader} instance. - */ - public function __construct($filename = false) - { - if ($filename instanceof Reader) - $this->_reader = &$filename; - else if ((is_string($filename) && ($this->_filename = $filename) !== false && - file_exists($filename) !== false) || - (is_resource($filename) && - in_array(get_resource_type($filename), array("file", "stream")))) - $this->_reader = new Reader($filename); - else - return; - - if ($this->_reader->getSize() < 128) - throw new ID3_Exception("File does not contain ID3v1 tag"); - $this->_reader->setOffset(-128); - if ($this->_reader->read(3) != "TAG") { - $this->_reader = false; // reset reader, see write - throw new ID3_Exception("File does not contain ID3v1 tag"); - } - - $this->_title = rtrim($this->_reader->readString8(30), " \0"); - $this->_artist = rtrim($this->_reader->readString8(30), " \0"); - $this->_album = rtrim($this->_reader->readString8(30), " \0"); - $this->_year = $this->_reader->readString8(4); - $this->_comment = rtrim($this->_reader->readString8(28), " \0"); - - /* ID3v1.1 support for tracks */ - $v11_null = $this->_reader->read(1); - $v11_track = $this->_reader->read(1); - if (ord($v11_null) == 0 && ord($v11_track) != 0) - $this->_track = ord($v11_track); - else - $this->_comment = rtrim($this->_comment . $v11_null . $v11_track, " \0"); - - $this->_genre = $this->_reader->readInt8(); - } - - /** - * Returns the title field. - * - * @return string - */ - public function getTitle() { return $this->_title; } - - /** - * Sets a new value for the title field. The field cannot exceed 30 - * characters in length. - * - * @param string $title The title. - */ - public function setTitle($title) { $this->_title = $title; } - - /** - * Returns the artist field. - * - * @return string - */ - public function getArtist() { return $this->_artist; } - - /** - * Sets a new value for the artist field. The field cannot exceed 30 - * characters in length. - * - * @param string $artist The artist. - */ - public function setArtist($artist) { $this->_artist = $artist; } - - /** - * Returns the album field. - * - * @return string - */ - public function getAlbum() { return $this->_album; } - - /** - * Sets a new value for the album field. The field cannot exceed 30 - * characters in length. - * - * @param string $album The album. - */ - public function setAlbum($album) { $this->_album = $album; } - - /** - * Returns the year field. - * - * @return string - */ - public function getYear() { return $this->_year; } - - /** - * Sets a new value for the year field. The field cannot exceed 4 - * characters in length. - * - * @param string $year The year. - */ - public function setYear($year) { $this->_year = $year; } - - /** - * Returns the comment field. - * - * @return string - */ - public function getComment() { return $this->_comment; } - - /** - * Sets a new value for the comment field. The field cannot exceed 30 - * characters in length. - * - * @param string $comment The comment. - */ - public function setComment($comment) { $this->_comment = $comment; } - - /** - * Returns the track field. - * - * @since ID3v1.1 - * @return integer - */ - public function getTrack() { return $this->_track; } - - /** - * Sets a new value for the track field. By setting this field you enforce the - * 1.1 version to be used. - * - * @since ID3v1.1 - * @param integer $track The track number. - */ - public function setTrack($track) { $this->_track = $track; } - - /** - * Returns the genre. - * - * @return string - */ - public function getGenre() - { - if (isset(self::$genres[$this->_genre])) - return self::$genres[$this->_genre]; - else - return self::$genres[255]; // unknown - } - - /** - * Sets a new value for the genre field. The value may either be a numerical - * code representing one of the genres, or its string variant. - * - * The genre is set to unknown (code 255) in case the string is not found from - * the static {@link $genres} array of this class. - * - * @param integer $genre The genre. - */ - public function setGenre($genre) - { - if ((is_numeric($genre) && $genre >= 0 && $genre <= 255) || - ($genre = array_search($genre, self::$genres)) !== false) - $this->_genre = $genre; - else - $this->_genre = 255; // unknown - } - - /** - * Writes the possibly altered ID3v1 tag back to the file where it was read. - * If the class was constructed without a file name, one can be provided here - * as an argument. Regardless, the write operation will override previous - * tag information, if found. - * - * @param string $filename The optional path to the file. - */ - public function write($filename = false) - { - if ($filename === false && ($filename = $this->_filename) === false) - throw new ID3_Exception("No file given to write the tag to"); - - if (($fd = fopen - ($filename, file_exists($filename) ? "r+b" : "wb")) === false) - throw new ID3_Exception("Unable to open file for writing: " . $filename); - - fseek($fd, $this->_reader !== false ? -128 : 0, SEEK_END); - fwrite($fd, $this->__toString(), 128); - - $this->_filename = $filename; - } - - /** - * Magic function so that $obj->value will work. - * - * @param string $name The field name. - * @return mixed - */ - public function __get($name) - { - // To keep compatibility with PHP 5.0.0 we use a static array instead of - // method_exists to check if a method can be called. - static $names = array( - "title", "artist", "album", "year", "comment", "track", "genre" - ); - if (in_array($name, $names)) - return call_user_func(array($this, "get" . ucfirst(strtolower($name)))); - else throw new ID3_Exception("Unknown field: " . $name); - } - - /** - * Magic function so that assignments with $obj->value will work. - * - * @param string $name The field name. - * @param string $value The field value. - * @return mixed - */ - public function __set($name, $value) - { - // To keep compatibility with PHP 5.0.0 we use a static array instead of - // method_exists to check if a method can be called. - static $names = array( - "title", "artist", "album", "year", "comment", "track", "genre" - ); - if (in_array($name, $names)) - call_user_func - (array($this, "set" . ucfirst(strtolower($name))), $value); - else throw new ID3_Exception("Unknown field: " . $name); - } - - /** - * Returns the tag raw data. - * - * @return string - */ - private function __toString() - { - return "TAG" . - Transform::toString8(substr($this->_title, 0, 30), 30) . - Transform::toString8(substr($this->_artist, 0, 30), 30) . - Transform::toString8(substr($this->_album, 0, 30), 30) . - Transform::toString8(substr($this->_year, 0, 4), 4) . - ($this->_track ? - Transform::toString8(substr($this->_comment, 0, 28), 28) . - "\0" . Transform::toInt8($this->_track) : - Transform::toString8(substr($this->_comment, 0, 30), 30)) . - Transform::toInt8($this->_genre); - } -} diff --git a/src/Zend/Media/Exception.php b/src/Zend/Media/Exception.php new file mode 100644 index 0000000..040ad18 --- /dev/null +++ b/src/Zend/Media/Exception.php @@ -0,0 +1,39 @@ + + * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +class Zend_Media_Exception extends Zend_Exception +{} diff --git a/src/Zend/Media/Id3/Exception.php b/src/Zend/Media/Id3/Exception.php new file mode 100644 index 0000000..a6be32f --- /dev/null +++ b/src/Zend/Media/Id3/Exception.php @@ -0,0 +1,40 @@ + + * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +class Zend_Media_Id3_Exception extends Zend_Media_Exception +{} diff --git a/src/Zend/Media/Id3v1.php b/src/Zend/Media/Id3v1.php new file mode 100644 index 0000000..505d283 --- /dev/null +++ b/src/Zend/Media/Id3v1.php @@ -0,0 +1,405 @@ + + * @author Ryan Butterfield + * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id$ + */ +final class Zend_Media_Id3v1 +{ + /** @var string */ + private $_title; + + /** @var string */ + private $_artist; + + /** @var string */ + private $_album; + + /** @var string */ + private $_year; + + /** @var string */ + private $_comment; + + /** @var integer */ + private $_track; + + /** @var integer */ + private $_genre = 255; + + /** + * The genre list. + * + * @var Array + */ + public static $genres = array + ('Blues', 'Classic Rock', 'Country', 'Dance', 'Disco', 'Funk', 'Grunge', + 'Hip-Hop', 'Jazz', 'Metal', 'New Age', 'Oldies', 'Other', 'Pop', 'R&B', + 'Rap', 'Reggae', 'Rock', 'Techno', 'Industrial', 'Alternative', 'Ska', + 'Death Metal', 'Pranks', 'Soundtrack', 'Euro-Techno', 'Ambient', + 'Trip-Hop', 'Vocal', 'Jazz+Funk', 'Fusion', 'Trance', 'Classical', + 'Instrumental', 'Acid', 'House', 'Game', 'Sound Clip', 'Gospel', + 'Noise', 'AlternRock', 'Bass', 'Soul', 'Punk', 'Space', 'Meditative', + 'Instrumental Pop', 'Instrumental Rock', 'Ethnic', 'Gothic', + 'Darkwave', 'Techno-Industrial', 'Electronic', 'Pop-Folk', 'Eurodance', + 'Dream', 'Southern Rock', 'Comedy', 'Cult', 'Gangsta', 'Top 40', + 'Christian Rap', 'Pop/Funk', 'Jungle', 'Native American', 'Cabaret', + 'New Wave', 'Psychadelic', 'Rave', 'Showtunes', 'Trailer', 'Lo-Fi', + 'Tribal', 'Acid Punk', 'Acid Jazz', 'Polka', 'Retro', 'Musical', + 'Rock & Roll', 'Hard Rock', 'Folk', 'Folk-Rock', 'National Folk', + 'Swing', 'Fast Fusion', 'Bebob', 'Latin', 'Revival', 'Celtic', + 'Bluegrass', 'Avantgarde', 'Gothic Rock', 'Progressive Rock', + 'Psychedelic Rock', 'Symphonic Rock', 'Slow Rock', 'Big Band', + 'Chorus', 'Easy Listening', 'Acoustic', 'Humour', 'Speech', 'Chanson', + 'Opera', 'Chamber Music', 'Sonata', 'Symphony', 'Booty Bass', 'Primus', + 'Porn Groove', 'Satire', 'Slow Jam', 'Club', 'Tango', 'Samba', + 'Folklore', 'Ballad', 'Power Ballad', 'Rhythmic Soul', 'Freestyle', + 'Duet', 'Punk Rock', 'Drum Solo', 'A capella', 'Euro-House', + 'Dance Hall', 255 => 'Unknown'); + + /** @var Zend_Io_Reader */ + private $_reader; + + /** @var string */ + private $_filename = null; + + /** + * Constructs the Id3v1 class with given file. The file is not mandatory + * argument and may be omitted as a new tag can be written to a file also by + * giving the filename to the {@link #write} method of this class. + * + * @param string|resource|Zend_Io_Reader $filename The path to the file, + * file descriptor of an opened file, or a {@link Zend_Io_Reader} instance. + * @throws Zend_Media_Id3_Exception if given file descriptor is not valid + */ + public function __construct($filename = null) + { + if ($filename === null) { + return; + } + if ($filename instanceof Zend_Io_Reader) { + $this->_reader = &$filename; + } else { + $this->_filename = $filename; + require_once('Zend/Io/FileReader.php'); + try { + $this->_reader = new Zend_Io_FileReader($filename); + } catch (Zend_Io_Exception $e) { + $this->_reader = null; + require_once 'Zend/Media/Id3/Exception.php'; + throw new Zend_Media_Id3_Exception($e->getMessage()); + } + } + + if ($this->_reader->getSize() < 128) { + $this->_reader = null; + require_once 'Zend/Media/Id3/Exception.php'; + throw new Zend_Media_Id3_Exception + ('File does not contain ID3v1 tag'); + } + $this->_reader->setOffset(-128); + if ($this->_reader->read(3) != 'TAG') { + $this->_reader = null; + require_once 'Zend/Media/Id3/Exception.php'; + throw new Zend_Media_Id3_Exception + ('File does not contain ID3v1 tag'); + } + + $this->_title = $this->_reader->readString8(30, " \0"); + $this->_artist = $this->_reader->readString8(30, " \0"); + $this->_album = $this->_reader->readString8(30, " \0"); + $this->_year = $this->_reader->readString8(4); + $this->_comment = $this->_reader->readString8(28); + + /* ID3v1.1 support for tracks */ + $v11_null = $this->_reader->read(1); + $v11_track = $this->_reader->read(1); + if (ord($v11_null) == 0 && ord($v11_track) != 0) { + $this->_track = ord($v11_track); + } else { + $this->_comment = rtrim + ($this->_comment . $v11_null . $v11_track, " \0"); + } + + $this->_genre = $this->_reader->readInt8(); + } + + /** + * Returns the title field. + * + * @return string + */ + public function getTitle() + { + return $this->_title; + } + + /** + * Sets a new value for the title field. The field cannot exceed 30 + * characters in length. + * + * @param string $title The title. + */ + public function setTitle($title) + { + $this->_title = $title; + } + + /** + * Returns the artist field. + * + * @return string + */ + public function getArtist() + { + return $this->_artist; + } + + /** + * Sets a new value for the artist field. The field cannot exceed 30 + * characters in length. + * + * @param string $artist The artist. + */ + public function setArtist($artist) + { + $this->_artist = $artist; + } + + /** + * Returns the album field. + * + * @return string + */ + public function getAlbum() + { + return $this->_album; + } + + /** + * Sets a new value for the album field. The field cannot exceed 30 + * characters in length. + * + * @param string $album The album. + */ + public function setAlbum($album) + { + $this->_album = $album; + } + + /** + * Returns the year field. + * + * @return string + */ + public function getYear() + { + return $this->_year; + } + + /** + * Sets a new value for the year field. The field cannot exceed 4 + * characters in length. + * + * @param string $year The year. + */ + public function setYear($year) + { + $this->_year = $year; + } + + /** + * Returns the comment field. + * + * @return string + */ + public function getComment() + { + return $this->_comment; + } + + /** + * Sets a new value for the comment field. The field cannot exceed 30 + * characters in length. + * + * @param string $comment The comment. + */ + public function setComment($comment) + { + $this->_comment = $comment; + } + + /** + * Returns the track field. + * + * @since ID3v1.1 + * @return integer + */ + public function getTrack() + { + return $this->_track; + } + + /** + * Sets a new value for the track field. By setting this field you enforce + * the 1.1 version to be used. + * + * @since ID3v1.1 + * @param integer $track The track number. + */ + public function setTrack($track) + { + $this->_track = $track; + } + + /** + * Returns the genre. + * + * @return string + */ + public function getGenre() + { + if (isset(self::$genres[$this->_genre])) + return self::$genres[$this->_genre]; + else + return self::$genres[255]; // unknown + } + + /** + * Sets a new value for the genre field. The value may either be a numerical + * code representing one of the genres, or its string variant. + * + * The genre is set to unknown (code 255) in case the string is not found + * from the static {@link $genres} array of this class. + * + * @param integer $genre The genre. + */ + public function setGenre($genre) + { + if ((is_numeric($genre) && $genre >= 0 && $genre <= 255) || + ($genre = array_search($genre, self::$genres)) !== false) + $this->_genre = $genre; + else + $this->_genre = 255; // unknown + } + + /** + * Writes the possibly altered ID3v1 tag back to the file where it was read. + * If the class was constructed without a file name, one can be provided + * here as an argument. Regardless, the write operation will override + * previous tag information, if found. + * + * @param string $filename The optional path to the file. + * @throws Zend_Media_Id3_Exception if there is no file to write the tag to + */ + public function write($filename = null) + { + if ($filename === null && ($filename = $this->_filename) === null) { + require_once 'Zend/Media/Id3/Exception.php'; + throw new Zend_Media_Id3_Exception + ('No file given to write the tag to'); + } + + require_once('Zend/Io/FileWriter.php'); + try { + $writer = new Zend_Io_FileWriter($filename); + $offset = $writer->getSize(); + if ($this->_reader !== null) { + $offset = -128; + } else { + $reader = new Zend_Io_Reader($writer->getFileDescriptor()); + $reader->setOffset(-128); + if ($reader->read(3) == 'TAG') + $offset = -128; + } + $writer->setOffset($offset); + $writer->writeString8('TAG') + ->writeString8(substr($this->_title, 0, 30), 30) + ->writeString8(substr($this->_artist, 0, 30), 30) + ->writeString8(substr($this->_album, 0, 30), 30) + ->writeString8(substr($this->_year, 0, 4), 4); + if ($this->_track) { + $writer->writeString8(substr($this->_comment, 0, 28), 28) + ->writeInt8(0) + ->writeInt8($this->_track); + } else { + $writer->writeString8(substr($this->_comment, 0, 30), 30); + } + $writer->writeInt8($this->_genre); + $writer->flush(); + } catch (Zend_Io_Exception $e) { + require_once 'Zend/Media/Id3/Exception.php'; + throw new Zend_Media_Id3_Exception($e->getMessage()); + } + + $this->_filename = $filename; + } + + /** + * Magic function so that $obj->value will work. + * + * @param string $name The field name. + * @return mixed + */ + public function __get($name) + { + if (method_exists($this, 'get' . ucfirst(strtolower($name)))) { + return call_user_func + (array($this, 'get' . ucfirst(strtolower($name)))); + } else { + require_once('Zend/Media/Id3/Exception.php'); + throw new Zend_Media_Id3_Exception('Unknown field: ' . $name); + } + } + + /** + * Magic function so that assignments with $obj->value will work. + * + * @param string $name The field name. + * @param string $value The field value. + * @return mixed + */ + public function __set($name, $value) + { + if (method_exists($this, 'set' . ucfirst(strtolower($name)))) { + call_user_func + (array($this, 'set' . ucfirst(strtolower($name))), $value); + } else { + require_once('Zend/Media/Id3/Exception.php'); + throw new Zend_Media_Id3_Exception('Unknown field: ' . $name); + } + } +}