source: extensions/charlies_content/getid3/getid3/write.apetag.php @ 3544

Last change on this file since 3544 was 3544, checked in by vdigital, 15 years ago

Change: getid3 upgraded to -> 1.7.9

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Author Date Id Revision
File size: 7.2 KB
Line 
1<?php
2/////////////////////////////////////////////////////////////////
3/// getID3() by James Heinrich <info@getid3.org>               //
4//  available at http://getid3.sourceforge.net                 //
5//            or http://www.getid3.org                         //
6/////////////////////////////////////////////////////////////////
7// See readme.txt for more details                             //
8/////////////////////////////////////////////////////////////////
9//                                                             //
10// write.apetag.php                                            //
11// module for writing APE tags                                 //
12// dependencies: module.tag.apetag.php                         //
13//                                                            ///
14/////////////////////////////////////////////////////////////////
15
16
17getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
18
19class getid3_write_apetag
20{
21
22        var $filename;
23        var $tag_data;
24        var $always_preserve_replaygain = true;  // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data
25        var $warnings = array();                 // any non-critical errors will be stored here
26        var $errors   = array();                 // any critical errors will be stored here
27
28        function getid3_write_apetag() {
29                return true;
30        }
31
32        function WriteAPEtag() {
33                // NOTE: All data passed to this function must be UTF-8 format
34
35                $getID3 = new getID3;
36                $ThisFileInfo = $getID3->analyze($this->filename);
37
38                if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
39                        if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) {
40                                // Current APE tag between Lyrics3 and ID3v1/EOF
41                                // This break Lyrics3 functionality
42                                if (!$this->DeleteAPEtag()) {
43                                        return false;
44                                }
45                                $ThisFileInfo = $getID3->analyze($this->filename);
46                        }
47                }
48
49                if ($this->always_preserve_replaygain) {
50                        $ReplayGainTagsToPreserve = array('mp3gain_minmax', 'mp3gain_album_minmax', 'mp3gain_undo', 'replaygain_track_peak', 'replaygain_track_gain', 'replaygain_album_peak', 'replaygain_album_gain');
51                        foreach ($ReplayGainTagsToPreserve as $rg_key) {
52                                if (isset($ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]) && !isset($this->tag_data[strtoupper($rg_key)][0])) {
53                                        $this->tag_data[strtoupper($rg_key)][0] = $ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0];
54                                }
55                        }
56                }
57
58                if ($APEtag = $this->GenerateAPEtag()) {
59                        if ($fp = @fopen($this->filename, 'a+b')) {
60                                $oldignoreuserabort = ignore_user_abort(true);
61                                flock($fp, LOCK_EX);
62
63                                $PostAPEdataOffset = $ThisFileInfo['avdataend'];
64                                if (isset($ThisFileInfo['ape']['tag_offset_end'])) {
65                                        $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']);
66                                }
67                                if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) {
68                                        $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']);
69                                }
70                                fseek($fp, $PostAPEdataOffset, SEEK_SET);
71                                $PostAPEdata = '';
72                                if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) {
73                                        $PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset);
74                                }
75
76                                fseek($fp, $PostAPEdataOffset, SEEK_SET);
77                                if (isset($ThisFileInfo['ape']['tag_offset_start'])) {
78                                        fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
79                                }
80                                ftruncate($fp, ftell($fp));
81                                fwrite($fp, $APEtag, strlen($APEtag));
82                                if (!empty($PostAPEdata)) {
83                                        fwrite($fp, $PostAPEdata, strlen($PostAPEdata));
84                                }
85                                flock($fp, LOCK_UN);
86                                fclose($fp);
87                                ignore_user_abort($oldignoreuserabort);
88                                return true;
89
90                        }
91                        return false;
92                }
93                return false;
94        }
95
96        function DeleteAPEtag() {
97                $getID3 = new getID3;
98                $ThisFileInfo = $getID3->analyze($this->filename);
99                if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) {
100                        if ($fp = @fopen($this->filename, 'a+b')) {
101
102                                flock($fp, LOCK_EX);
103                                $oldignoreuserabort = ignore_user_abort(true);
104
105                                fseek($fp, $ThisFileInfo['ape']['tag_offset_end'], SEEK_SET);
106                                $DataAfterAPE = '';
107                                if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) {
108                                        $DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']);
109                                }
110
111                                ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']);
112                                fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
113
114                                if (!empty($DataAfterAPE)) {
115                                        fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE));
116                                }
117
118                                flock($fp, LOCK_UN);
119                                fclose($fp);
120                                ignore_user_abort($oldignoreuserabort);
121
122                                return true;
123
124                        }
125                        return false;
126                }
127                return true;
128        }
129
130
131        function GenerateAPEtag() {
132                // NOTE: All data passed to this function must be UTF-8 format
133
134                $items = array();
135                if (!is_array($this->tag_data)) {
136                        return false;
137                }
138                foreach ($this->tag_data as $key => $arrayofvalues) {
139                        if (!is_array($arrayofvalues)) {
140                                return false;
141                        }
142
143                        $valuestring = '';
144                        foreach ($arrayofvalues as $value) {
145                                $valuestring .= str_replace("\x00", '', $value)."\x00";
146                        }
147                        $valuestring = rtrim($valuestring, "\x00");
148
149                        // Length of the assigned value in bytes
150                        $tagitem  = getid3_lib::LittleEndian2String(strlen($valuestring), 4);
151
152                        //$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false);
153                        $tagitem .= "\x00\x00\x00\x00";
154
155                        $tagitem .= $this->CleanAPEtagItemKey($key)."\x00";
156                        $tagitem .= $valuestring;
157
158                        $items[] = $tagitem;
159
160                }
161
162                return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false);
163        }
164
165        function GenerateAPEtagHeaderFooter(&$items, $isheader=false) {
166                $tagdatalength = 0;
167                foreach ($items as $itemdata) {
168                        $tagdatalength += strlen($itemdata);
169                }
170
171                $APEheader  = 'APETAGEX';
172                $APEheader .= getid3_lib::LittleEndian2String(2000, 4);
173                $APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4);
174                $APEheader .= getid3_lib::LittleEndian2String(count($items), 4);
175                $APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false);
176                $APEheader .= str_repeat("\x00", 8);
177
178                return $APEheader;
179        }
180
181        function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) {
182                $APEtagFlags = array_fill(0, 4, 0);
183                if ($header) {
184                        $APEtagFlags[0] |= 0x80; // Tag contains a header
185                }
186                if (!$footer) {
187                        $APEtagFlags[0] |= 0x40; // Tag contains no footer
188                }
189                if ($isheader) {
190                        $APEtagFlags[0] |= 0x20; // This is the header, not the footer
191                }
192
193                // 0: Item contains text information coded in UTF-8
194                // 1: Item contains binary information °)
195                // 2: Item is a locator of external stored information °°)
196                // 3: reserved
197                $APEtagFlags[3] |= ($encodingid << 1);
198
199                if ($readonly) {
200                        $APEtagFlags[3] |= 0x01; // Tag or Item is Read Only
201                }
202
203                return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]);
204        }
205
206        function CleanAPEtagItemKey($itemkey) {
207                $itemkey = eregi_replace("[^\x20-\x7E]", '', $itemkey);
208
209                // http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
210                switch (strtoupper($itemkey)) {
211                        case 'EAN/UPC':
212                        case 'ISBN':
213                        case 'LC':
214                        case 'ISRC':
215                                $itemkey = strtoupper($itemkey);
216                                break;
217
218                        default:
219                                $itemkey = ucwords($itemkey);
220                                break;
221                }
222                return $itemkey;
223
224        }
225
226}
227
228?>
Note: See TracBrowser for help on using the repository browser.