summaryrefslogtreecommitdiff
path: root/getid3/write.apetag.php
diff options
context:
space:
mode:
Diffstat (limited to 'getid3/write.apetag.php')
-rw-r--r--getid3/write.apetag.php228
1 files changed, 228 insertions, 0 deletions
diff --git a/getid3/write.apetag.php b/getid3/write.apetag.php
new file mode 100644
index 0000000..189160a
--- /dev/null
+++ b/getid3/write.apetag.php
@@ -0,0 +1,228 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org> //
+// available at http://getid3.sourceforge.net //
+// or http://www.getid3.org //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details //
+/////////////////////////////////////////////////////////////////
+// //
+// write.apetag.php //
+// module for writing APE tags //
+// dependencies: module.tag.apetag.php //
+// ///
+/////////////////////////////////////////////////////////////////
+
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
+
+class getid3_write_apetag
+{
+
+ var $filename;
+ var $tag_data;
+ var $always_preserve_replaygain = true; // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data
+ var $warnings = array(); // any non-critical errors will be stored here
+ var $errors = array(); // any critical errors will be stored here
+
+ function getid3_write_apetag() {
+ return true;
+ }
+
+ function WriteAPEtag() {
+ // NOTE: All data passed to this function must be UTF-8 format
+
+ $getID3 = new getID3;
+ $ThisFileInfo = $getID3->analyze($this->filename);
+
+ if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
+ if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) {
+ // Current APE tag between Lyrics3 and ID3v1/EOF
+ // This break Lyrics3 functionality
+ if (!$this->DeleteAPEtag()) {
+ return false;
+ }
+ $ThisFileInfo = $getID3->analyze($this->filename);
+ }
+ }
+
+ if ($this->always_preserve_replaygain) {
+ $ReplayGainTagsToPreserve = array('mp3gain_minmax', 'mp3gain_album_minmax', 'mp3gain_undo', 'replaygain_track_peak', 'replaygain_track_gain', 'replaygain_album_peak', 'replaygain_album_gain');
+ foreach ($ReplayGainTagsToPreserve as $rg_key) {
+ if (isset($ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]) && !isset($this->tag_data[strtoupper($rg_key)][0])) {
+ $this->tag_data[strtoupper($rg_key)][0] = $ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0];
+ }
+ }
+ }
+
+ if ($APEtag = $this->GenerateAPEtag()) {
+ if ($fp = @fopen($this->filename, 'a+b')) {
+ $oldignoreuserabort = ignore_user_abort(true);
+ flock($fp, LOCK_EX);
+
+ $PostAPEdataOffset = $ThisFileInfo['avdataend'];
+ if (isset($ThisFileInfo['ape']['tag_offset_end'])) {
+ $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']);
+ }
+ if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) {
+ $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']);
+ }
+ fseek($fp, $PostAPEdataOffset, SEEK_SET);
+ $PostAPEdata = '';
+ if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) {
+ $PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset);
+ }
+
+ fseek($fp, $PostAPEdataOffset, SEEK_SET);
+ if (isset($ThisFileInfo['ape']['tag_offset_start'])) {
+ fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
+ }
+ ftruncate($fp, ftell($fp));
+ fwrite($fp, $APEtag, strlen($APEtag));
+ if (!empty($PostAPEdata)) {
+ fwrite($fp, $PostAPEdata, strlen($PostAPEdata));
+ }
+ flock($fp, LOCK_UN);
+ fclose($fp);
+ ignore_user_abort($oldignoreuserabort);
+ return true;
+
+ }
+ return false;
+ }
+ return false;
+ }
+
+ function DeleteAPEtag() {
+ $getID3 = new getID3;
+ $ThisFileInfo = $getID3->analyze($this->filename);
+ if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) {
+ if ($fp = @fopen($this->filename, 'a+b')) {
+
+ flock($fp, LOCK_EX);
+ $oldignoreuserabort = ignore_user_abort(true);
+
+ fseek($fp, $ThisFileInfo['ape']['tag_offset_end'], SEEK_SET);
+ $DataAfterAPE = '';
+ if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) {
+ $DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']);
+ }
+
+ ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']);
+ fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
+
+ if (!empty($DataAfterAPE)) {
+ fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE));
+ }
+
+ flock($fp, LOCK_UN);
+ fclose($fp);
+ ignore_user_abort($oldignoreuserabort);
+
+ return true;
+
+ }
+ return false;
+ }
+ return true;
+ }
+
+
+ function GenerateAPEtag() {
+ // NOTE: All data passed to this function must be UTF-8 format
+
+ $items = array();
+ if (!is_array($this->tag_data)) {
+ return false;
+ }
+ foreach ($this->tag_data as $key => $arrayofvalues) {
+ if (!is_array($arrayofvalues)) {
+ return false;
+ }
+
+ $valuestring = '';
+ foreach ($arrayofvalues as $value) {
+ $valuestring .= str_replace("\x00", '', $value)."\x00";
+ }
+ $valuestring = rtrim($valuestring, "\x00");
+
+ // Length of the assigned value in bytes
+ $tagitem = getid3_lib::LittleEndian2String(strlen($valuestring), 4);
+
+ //$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false);
+ $tagitem .= "\x00\x00\x00\x00";
+
+ $tagitem .= $this->CleanAPEtagItemKey($key)."\x00";
+ $tagitem .= $valuestring;
+
+ $items[] = $tagitem;
+
+ }
+
+ return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false);
+ }
+
+ function GenerateAPEtagHeaderFooter(&$items, $isheader=false) {
+ $tagdatalength = 0;
+ foreach ($items as $itemdata) {
+ $tagdatalength += strlen($itemdata);
+ }
+
+ $APEheader = 'APETAGEX';
+ $APEheader .= getid3_lib::LittleEndian2String(2000, 4);
+ $APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4);
+ $APEheader .= getid3_lib::LittleEndian2String(count($items), 4);
+ $APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false);
+ $APEheader .= str_repeat("\x00", 8);
+
+ return $APEheader;
+ }
+
+ function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) {
+ $APEtagFlags = array_fill(0, 4, 0);
+ if ($header) {
+ $APEtagFlags[0] |= 0x80; // Tag contains a header
+ }
+ if (!$footer) {
+ $APEtagFlags[0] |= 0x40; // Tag contains no footer
+ }
+ if ($isheader) {
+ $APEtagFlags[0] |= 0x20; // This is the header, not the footer
+ }
+
+ // 0: Item contains text information coded in UTF-8
+ // 1: Item contains binary information °)
+ // 2: Item is a locator of external stored information °°)
+ // 3: reserved
+ $APEtagFlags[3] |= ($encodingid << 1);
+
+ if ($readonly) {
+ $APEtagFlags[3] |= 0x01; // Tag or Item is Read Only
+ }
+
+ return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]);
+ }
+
+ function CleanAPEtagItemKey($itemkey) {
+ $itemkey = eregi_replace("[^\x20-\x7E]", '', $itemkey);
+
+ // http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
+ switch (strtoupper($itemkey)) {
+ case 'EAN/UPC':
+ case 'ISBN':
+ case 'LC':
+ case 'ISRC':
+ $itemkey = strtoupper($itemkey);
+ break;
+
+ default:
+ $itemkey = ucwords($itemkey);
+ break;
+ }
+ return $itemkey;
+
+ }
+
+}
+
+?> \ No newline at end of file