source: extensions/charlies_content/getid3/getid3/module.tag.id3v1.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: 11.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// module.tag.id3v1.php                                        //
11// module for analyzing ID3v1 tags                             //
12// dependencies: NONE                                          //
13//                                                            ///
14/////////////////////////////////////////////////////////////////
15
16
17class getid3_id3v1
18{
19
20        function getid3_id3v1(&$fd, &$ThisFileInfo) {
21
22                if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
23                        $ThisFileInfo['warning'][] = 'Unable to check for ID3v1 because file is larger than 2GB';
24                        return false;
25                }
26
27                fseek($fd, -256, SEEK_END);
28                $preid3v1 = fread($fd, 128);
29                $id3v1tag = fread($fd, 128);
30
31                if (substr($id3v1tag, 0, 3) == 'TAG') {
32
33                        $ThisFileInfo['avdataend'] = $ThisFileInfo['filesize'] - 128;
34
35                        $ParsedID3v1['title']   = $this->cutfield(substr($id3v1tag,   3, 30));
36                        $ParsedID3v1['artist']  = $this->cutfield(substr($id3v1tag,  33, 30));
37                        $ParsedID3v1['album']   = $this->cutfield(substr($id3v1tag,  63, 30));
38                        $ParsedID3v1['year']    = $this->cutfield(substr($id3v1tag,  93,  4));
39                        $ParsedID3v1['comment'] =                 substr($id3v1tag,  97, 30);  // can't remove nulls yet, track detection depends on them
40                        $ParsedID3v1['genreid'] =             ord(substr($id3v1tag, 127,  1));
41
42                        // If second-last byte of comment field is null and last byte of comment field is non-null
43                        // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
44                        if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
45                                $ParsedID3v1['track']   = ord(substr($ParsedID3v1['comment'], 29,  1));
46                                $ParsedID3v1['comment'] =     substr($ParsedID3v1['comment'],  0, 28);
47                        }
48                        $ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
49
50                        $ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
51                        if (!empty($ParsedID3v1['genre'])) {
52                                unset($ParsedID3v1['genreid']);
53                        }
54                        if (empty($ParsedID3v1['genre']) || (@$ParsedID3v1['genre'] == 'Unknown')) {
55                                unset($ParsedID3v1['genre']);
56                        }
57
58                        foreach ($ParsedID3v1 as $key => $value) {
59                                $ParsedID3v1['comments'][$key][0] = $value;
60                        }
61
62                        // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
63                        $GoodFormatID3v1tag = $this->GenerateID3v1Tag(
64                                                                                        $ParsedID3v1['title'],
65                                                                                        $ParsedID3v1['artist'],
66                                                                                        $ParsedID3v1['album'],
67                                                                                        $ParsedID3v1['year'],
68                                                                                        (isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),
69                                                                                        $ParsedID3v1['comment'],
70                                                                                        @$ParsedID3v1['track']);
71                        $ParsedID3v1['padding_valid'] = true;
72                        if ($id3v1tag !== $GoodFormatID3v1tag) {
73                                $ParsedID3v1['padding_valid'] = false;
74                                $ThisFileInfo['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding';
75                        }
76
77                        $ParsedID3v1['tag_offset_end']   = $ThisFileInfo['filesize'];
78                        $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
79
80                        $ThisFileInfo['id3v1'] = $ParsedID3v1;
81                }
82
83                if (substr($preid3v1, 0, 3) == 'TAG') {
84                        // The way iTunes handles tags is, well, brain-damaged.
85                        // It completely ignores v1 if ID3v2 is present.
86                        // This goes as far as adding a new v1 tag *even if there already is one*
87
88                        // A suspected double-ID3v1 tag has been detected, but it could be that
89                        // the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag
90                        if (substr($preid3v1, 96, 8) == 'APETAGEX') {
91                                // an APE tag footer was found before the last ID3v1, assume false "TAG" synch
92                        } elseif (substr($preid3v1, 119, 6) == 'LYRICS') {
93                                // a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch
94                        } else {
95                                // APE and Lyrics3 footers not found - assume double ID3v1
96                                $ThisFileInfo['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes';
97                                $ThisFileInfo['avdataend'] -= 128;
98                        }
99                }
100
101                return true;
102        }
103
104        function cutfield($str) {
105                return trim(substr($str, 0, strcspn($str, "\x00")));
106        }
107
108        function ArrayOfGenres($allowSCMPXextended=false) {
109                static $GenreLookup = array(
110                        0    => 'Blues',
111                        1    => 'Classic Rock',
112                        2    => 'Country',
113                        3    => 'Dance',
114                        4    => 'Disco',
115                        5    => 'Funk',
116                        6    => 'Grunge',
117                        7    => 'Hip-Hop',
118                        8    => 'Jazz',
119                        9    => 'Metal',
120                        10   => 'New Age',
121                        11   => 'Oldies',
122                        12   => 'Other',
123                        13   => 'Pop',
124                        14   => 'R&B',
125                        15   => 'Rap',
126                        16   => 'Reggae',
127                        17   => 'Rock',
128                        18   => 'Techno',
129                        19   => 'Industrial',
130                        20   => 'Alternative',
131                        21   => 'Ska',
132                        22   => 'Death Metal',
133                        23   => 'Pranks',
134                        24   => 'Soundtrack',
135                        25   => 'Euro-Techno',
136                        26   => 'Ambient',
137                        27   => 'Trip-Hop',
138                        28   => 'Vocal',
139                        29   => 'Jazz+Funk',
140                        30   => 'Fusion',
141                        31   => 'Trance',
142                        32   => 'Classical',
143                        33   => 'Instrumental',
144                        34   => 'Acid',
145                        35   => 'House',
146                        36   => 'Game',
147                        37   => 'Sound Clip',
148                        38   => 'Gospel',
149                        39   => 'Noise',
150                        40   => 'Alt. Rock',
151                        41   => 'Bass',
152                        42   => 'Soul',
153                        43   => 'Punk',
154                        44   => 'Space',
155                        45   => 'Meditative',
156                        46   => 'Instrumental Pop',
157                        47   => 'Instrumental Rock',
158                        48   => 'Ethnic',
159                        49   => 'Gothic',
160                        50   => 'Darkwave',
161                        51   => 'Techno-Industrial',
162                        52   => 'Electronic',
163                        53   => 'Pop-Folk',
164                        54   => 'Eurodance',
165                        55   => 'Dream',
166                        56   => 'Southern Rock',
167                        57   => 'Comedy',
168                        58   => 'Cult',
169                        59   => 'Gangsta Rap',
170                        60   => 'Top 40',
171                        61   => 'Christian Rap',
172                        62   => 'Pop/Funk',
173                        63   => 'Jungle',
174                        64   => 'Native American',
175                        65   => 'Cabaret',
176                        66   => 'New Wave',
177                        67   => 'Psychedelic',
178                        68   => 'Rave',
179                        69   => 'Showtunes',
180                        70   => 'Trailer',
181                        71   => 'Lo-Fi',
182                        72   => 'Tribal',
183                        73   => 'Acid Punk',
184                        74   => 'Acid Jazz',
185                        75   => 'Polka',
186                        76   => 'Retro',
187                        77   => 'Musical',
188                        78   => 'Rock & Roll',
189                        79   => 'Hard Rock',
190                        80   => 'Folk',
191                        81   => 'Folk/Rock',
192                        82   => 'National Folk',
193                        83   => 'Swing',
194                        84   => 'Fast-Fusion',
195                        85   => 'Bebob',
196                        86   => 'Latin',
197                        87   => 'Revival',
198                        88   => 'Celtic',
199                        89   => 'Bluegrass',
200                        90   => 'Avantgarde',
201                        91   => 'Gothic Rock',
202                        92   => 'Progressive Rock',
203                        93   => 'Psychedelic Rock',
204                        94   => 'Symphonic Rock',
205                        95   => 'Slow Rock',
206                        96   => 'Big Band',
207                        97   => 'Chorus',
208                        98   => 'Easy Listening',
209                        99   => 'Acoustic',
210                        100  => 'Humour',
211                        101  => 'Speech',
212                        102  => 'Chanson',
213                        103  => 'Opera',
214                        104  => 'Chamber Music',
215                        105  => 'Sonata',
216                        106  => 'Symphony',
217                        107  => 'Booty Bass',
218                        108  => 'Primus',
219                        109  => 'Porn Groove',
220                        110  => 'Satire',
221                        111  => 'Slow Jam',
222                        112  => 'Club',
223                        113  => 'Tango',
224                        114  => 'Samba',
225                        115  => 'Folklore',
226                        116  => 'Ballad',
227                        117  => 'Power Ballad',
228                        118  => 'Rhythmic Soul',
229                        119  => 'Freestyle',
230                        120  => 'Duet',
231                        121  => 'Punk Rock',
232                        122  => 'Drum Solo',
233                        123  => 'A Cappella',
234                        124  => 'Euro-House',
235                        125  => 'Dance Hall',
236                        126  => 'Goa',
237                        127  => 'Drum & Bass',
238                        128  => 'Club-House',
239                        129  => 'Hardcore',
240                        130  => 'Terror',
241                        131  => 'Indie',
242                        132  => 'BritPop',
243                        133  => 'Negerpunk',
244                        134  => 'Polsk Punk',
245                        135  => 'Beat',
246                        136  => 'Christian Gangsta Rap',
247                        137  => 'Heavy Metal',
248                        138  => 'Black Metal',
249                        139  => 'Crossover',
250                        140  => 'Contemporary Christian',
251                        141  => 'Christian Rock',
252                        142  => 'Merengue',
253                        143  => 'Salsa',
254                        144  => 'Trash Metal',
255                        145  => 'Anime',
256                        146  => 'JPop',
257                        147  => 'Synthpop',
258
259                        255  => 'Unknown',
260
261                        'CR' => 'Cover',
262                        'RX' => 'Remix'
263                );
264
265                static $GenreLookupSCMPX = array();
266                if ($allowSCMPXextended && empty($GenreLookupSCMPX)) {
267                        $GenreLookupSCMPX = $GenreLookup;
268                        // http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended
269                        // Extended ID3v1 genres invented by SCMPX
270                        // Note that 255 "Japanese Anime" conflicts with standard "Unknown"
271                        $GenreLookupSCMPX[240] = 'Sacred';
272                        $GenreLookupSCMPX[241] = 'Northern Europe';
273                        $GenreLookupSCMPX[242] = 'Irish & Scottish';
274                        $GenreLookupSCMPX[243] = 'Scotland';
275                        $GenreLookupSCMPX[244] = 'Ethnic Europe';
276                        $GenreLookupSCMPX[245] = 'Enka';
277                        $GenreLookupSCMPX[246] = 'Children\'s Song';
278                        $GenreLookupSCMPX[247] = 'Japanese Sky';
279                        $GenreLookupSCMPX[248] = 'Japanese Heavy Rock';
280                        $GenreLookupSCMPX[249] = 'Japanese Doom Rock';
281                        $GenreLookupSCMPX[250] = 'Japanese J-POP';
282                        $GenreLookupSCMPX[251] = 'Japanese Seiyu';
283                        $GenreLookupSCMPX[252] = 'Japanese Ambient Techno';
284                        $GenreLookupSCMPX[253] = 'Japanese Moemoe';
285                        $GenreLookupSCMPX[254] = 'Japanese Tokusatsu';
286                        //$GenreLookupSCMPX[255] = 'Japanese Anime';
287                }
288
289                return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup);
290        }
291
292        function LookupGenreName($genreid, $allowSCMPXextended=true) {
293                switch ($genreid) {
294                        case 'RX':
295                        case 'CR':
296                                break;
297                        default:
298                                $genreid = intval($genreid); // to handle 3 or '3' or '03'
299                                break;
300                }
301                $GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
302                return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
303        }
304
305        function LookupGenreID($genre, $allowSCMPXextended=false) {
306                $GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
307                $LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre));
308                foreach ($GenreLookup as $key => $value) {
309                        foreach ($GenreLookup as $key => $value) {
310                                if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) {
311                                        return $key;
312                                }
313                        }
314                        return false;
315                }
316                return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
317        }
318
319        function StandardiseID3v1GenreName($OriginalGenre) {
320                if (($GenreID = getid3_id3v1::LookupGenreID($OriginalGenre)) !== false) {
321                        return getid3_id3v1::LookupGenreName($GenreID);
322                }
323                return $OriginalGenre;
324        }
325
326        function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') {
327                $ID3v1Tag  = 'TAG';
328                $ID3v1Tag .= str_pad(trim(substr($title,  0, 30)), 30, "\x00", STR_PAD_RIGHT);
329                $ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
330                $ID3v1Tag .= str_pad(trim(substr($album,  0, 30)), 30, "\x00", STR_PAD_RIGHT);
331                $ID3v1Tag .= str_pad(trim(substr($year,   0,  4)),  4, "\x00", STR_PAD_LEFT);
332                if (!empty($track) && ($track > 0) && ($track <= 255)) {
333                        $ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT);
334                        $ID3v1Tag .= "\x00";
335                        if (gettype($track) == 'string') {
336                                $track = (int) $track;
337                        }
338                        $ID3v1Tag .= chr($track);
339                } else {
340                        $ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
341                }
342                if (($genreid < 0) || ($genreid > 147)) {
343                        $genreid = 255; // 'unknown' genre
344                }
345                switch (gettype($genreid)) {
346                        case 'string':
347                        case 'integer':
348                                $ID3v1Tag .= chr(intval($genreid));
349                                break;
350                        default:
351                                $ID3v1Tag .= chr(255); // 'unknown' genre
352                                break;
353                }
354
355                return $ID3v1Tag;
356        }
357
358}
359
360
361?>
Note: See TracBrowser for help on using the repository browser.