source: spip-zone/_core_/branches/spip-3.1/plugins/medias/lib/getid3/write.apetag.php @ 113163

Last change on this file since 113163 was 113163, checked in by spip.franck@…, 7 months ago

Mise à jour de la lib getid en version 1.9.16, nous étions en 1.9.12
https://github.com/JamesHeinrich/getID3/blob/master/changelog.txt

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