29
29
/* Default Encoding */
30
public $encoding = '';
31
public $encoding_id3v1 = 'ISO-8859-1';
32
public $encoding_id3v2 = 'ISO-8859-1';
32
34
/* Loaded Variables */
35
public $filename = '';
37
public $tags = array();
37
39
/* Internal Information */
41
var $_file_encoding = '';
42
var $_file_pattern = '';
43
var $_dir_pattern = '';
40
public $_raw = array();
41
public $_raw2 = array();
43
public $_iconv = false;
44
public $_file_encoding = '';
45
public $_file_pattern = '';
46
public $_dir_pattern = '';
48
/* Internal Private */
49
private $_binary_parse = array();
47
54
* This function just sets up the class, it doesn't
48
55
* actually pull the information
50
function vainfo($file,$encoding='',$dir_pattern,$file_pattern) {
57
public function __construct($file,$encoding='',$encoding_id3v1='',$encoding_id3v2='',$dir_pattern,$file_pattern) {
60
if (function_exists('iconv')) {
52
64
$this->filename = $file;
61
73
$this->_file_pattern = $file_pattern;
62
74
$this->_dir_pattern = $dir_pattern;
64
// Initialize getID3 engine
65
$this->_getID3 = new getID3();
66
$this->_getID3->option_md5_data = false;
67
$this->_getID3->option_md5_data_source = false;
68
$this->_getID3->option_tags_html = false;
69
$this->_getID3->option_extra_info = false;
70
$this->_getID3->option_tag_lyrics3 = false;
71
// $this->_getID3->encoding = $this->encoding;
72
$this->_getID3->option_tags_process = true;
75
if (function_exists('iconv')) {
76
if(strtoupper(substr(PHP_OS,0,3)) == 'WIN') {
77
$this->_pathinfo = str_replace('%3A', ':', urlencode($this->filename));
78
$this->_pathinfo = pathinfo(str_replace('%5C', '\\', $this->_pathinfo));
81
$this->_pathinfo = pathinfo(str_replace('%2F', '/', urlencode($this->filename)));
83
$this->_pathinfo['extension'] = strtolower($this->_pathinfo['extension']);
85
// Before we roll the _getID3 route let's see about using exec + a binary
87
if (!isset($this->_binary_parse[$this->_pathinfo['extension']])) {
88
// Figure out if we've got binary parse ninja-skills here
89
$this->_binary_parse[$this->_pathinfo['extension']] = $this->can_binary_parse();
90
debug_event('BinaryParse','Binary Parse for ' . $this->_pathinfo['extension'] . ' set to ' . make_bool($this->_binary_parse[$this->_pathinfo['extension']]),'5');
93
// Initialize getID3 engine
94
$this->_getID3 = new getID3();
96
// if ($this->_binary_parse[$this->_pathinfo['extension']]) { return true; }
98
// get id3tag encodings
99
// we have to run this right here because we don't know what we have in the files
100
// and so we pull broken, then pull good later... this needs to be fixed
102
$this->_raw2 = $this->_getID3->analyze($file);
104
catch (Exception $error) {
105
debug_event('getid3',$error->message,'1');
108
if(function_exists('mb_detect_encoding')) {
109
$this->encoding_id3v1 = array();
110
$this->encoding_id3v1[] = mb_detect_encoding($this->_raw2['tags']['id3v1']['artist']['0']);
111
$this->encoding_id3v1[] = mb_detect_encoding($this->_raw2['tags']['id3v1']['album']['0']);
112
$this->encoding_id3v1[] = mb_detect_encoding($this->_raw2['tags']['id3v1']['genre']['0']);
113
$this->encoding_id3v1[] = mb_detect_encoding($this->_raw2['tags']['id3v1']['title']['0']);
114
array_multisort($this->encoding_id3v1);
115
array_splice($this->encoding_id3v1, -4, 3);
116
if($this->encoding_id3v1[0] != "ASCII") {
117
$this->encoding_id3v1 = $this->encoding_id3v1[0];
119
$this->encoding_id3v1 = "ISO-8859-1";
123
$this->encoding_id3v2 = array();
124
$this->encoding_id3v2[] = mb_detect_encoding($this->_raw2['tags']['id3v2']['artist']['0']);
125
$this->encoding_id3v2[] = mb_detect_encoding($this->_raw2['tags']['id3v2']['album']['0']);
126
$this->encoding_id3v2[] = mb_detect_encoding($this->_raw2['tags']['id3v2']['genre']['0']);
127
$this->encoding_id3v2[] = mb_detect_encoding($this->_raw2['tags']['id3v2']['title']['0']);
128
array_multisort($this->encoding_id3v2);
129
array_splice($this->encoding_id3v2, -4, 3);
130
if($this->encoding_id3v2[0] != "ASCII"){
131
$this->encoding_id3v2 = $this->encoding_id3v2[0];
133
$this->encoding_id3v2 = "ISO-8859-1";
137
$this->encoding_id3v1 = "ISO-8859-1";
138
$this->encoding_id3v2 = "ISO-8859-1";
141
$this->_getID3->option_md5_data = false;
142
$this->_getID3->option_md5_data_source = false;
143
$this->_getID3->option_tags_html = false;
144
$this->_getID3->option_extra_info = true;
145
$this->_getID3->option_tag_lyrics3 = true;
146
$this->_getID3->encoding = $this->encoding;
147
$this->_getID3->encoding_id3v1 = $this->encoding_id3v1;
148
$this->_getID3->encoding_id3v2 = $this->encoding_id3v2;
149
$this->_getID3->option_tags_process = true;
85
158
* all filled up with tagie goodness or if specified filename
86
159
* pattern goodness
90
/* Get the Raw file information */
92
$this->_raw = $this->_getID3->analyze($this->filename);
94
catch (Exception $error) {
95
debug_event('getid3',$error->message,'1');
98
/* Figure out what type of file we are dealing with */
99
$this->type = $this->_get_type();
101
/* Get the general information about this file */
102
$info = $this->_get_info();
161
public function get_info() {
163
// If we've got a green light try out the binary
164
// if ($this->_binary_parse[$this->_pathinfo['extension']]) {
165
// $this->run_binary_parse();
170
/* Get the Raw file information */
172
$this->_raw = $this->_getID3->analyze($this->filename);
174
catch (Exception $error) {
175
debug_event('getid3',$error->message,'1');
178
/* Figure out what type of file we are dealing with */
179
$this->type = $this->_get_type();
181
/* Get the general information about this file */
182
$info = $this->_get_info();
105
185
/* Gets the Tags */
106
186
$this->tags = $this->_get_tags();
195
* This takes the result set, and the the tag_order
196
* As defined by your config file and trys to figure out
197
* which tag type it should use, if your tag_order
198
* doesn't match anything then it just takes the first one
199
* it finds in the results.
201
public static function get_tag_type($results) {
203
/* Pull In the config option */
204
$order = Config::get('tag_order');
206
if (!is_array($order)) {
207
$order = array($order);
210
/* Foreach through the defined key order
211
* the first one we find is the first one we use
213
foreach($order as $key) {
214
if ($results[$key]) {
215
$returned_key = $key;
220
/* If we didn't find anything then default it to the
221
* first in the results set
223
if (!isset($returned_key)) {
224
$keys = array_keys($results);
225
$returned_key = $keys['0'];
228
return $returned_key;
234
* This function takes the array from vainfo along with the
235
* key we've decided on and the filename and returns it in a
236
* sanatized format that ampache can actually use
238
public static function clean_tag_info($results,$key,$filename) {
242
$clean_array = array("\n","\t","\r","\0");
243
$wipe_array = array("","","","");
245
$info['file'] = $filename;
246
$info['title'] = stripslashes(trim($results[$key]['title']));
247
$info['comment'] = Dba::escape(str_replace($clean_array,$wipe_array,$results[$key]['comment']));
249
/* This are pulled from the info array */
250
$info['bitrate'] = intval($results['info']['bitrate']);
251
$info['rate'] = intval($results['info']['sample_rate']);
252
$info['mode'] = $results['info']['bitrate_mode'];
254
// Convert special version of constant bitrate mode to cbr
255
if($info['mode'] == 'con') {
256
$info['mode'] = 'cbr';
259
$info['size'] = $results['info']['filesize'];
260
$info['mime'] = $results['info']['mime'];
261
$into['encoding'] = $results['info']['encoding'];
262
$info['time'] = intval($results['info']['playing_time']);
263
$info['channels'] = intval($results['info']['channels']);
265
// Specific Audio Flags
266
if (!$results[$key]['video_codec']) {
267
$slash_point = strpos($results[$key]['disk'],'/');
268
if ($slash_point !== FALSE) {
269
$results[$key]['disk'] = substr($results[$key]['disk'],0,$slash_point);
271
/* These are used to generate the correct ID's later */
272
$info['year'] = intval($results[$key]['year']);
273
$info['disk'] = intval($results[$key]['disk']);
274
$info['artist'] = trim($results[$key]['artist']);
275
$info['album'] = trim($results[$key]['album']);
276
$info['genre'] = trim($results[$key]['genre']);
277
/* @TODO language doesn't import from id3tag. @momo-i */
278
$info['language'] = Dba::escape($results[$key]['language']);
279
if (!empty($results[$key]['unsynchronised lyric'])) { // ID3v2 USLT
280
$info['lyrics'] = str_replace(array("\r\n","\r","\n"), '<br />',strip_tags($results[$key]['unsynchronised lyric']));
282
else { // Lyrics3 v2.0
283
$info['lyrics'] = str_replace(array("\r\n","\r","\n"), '<br />',strip_tags($results['info']['lyrics']['unsynchedlyrics']));
285
$info['track'] = intval($results[$key]['track']);
288
$info['resolution_x'] = intval($results[$key]['resolution_x']);
289
$info['resolution_y'] = intval($results[$key]['resolution_y']);
290
$info['audio_codec'] = Dba::escape($results[$key]['audio_codec']);
291
$info['video_codec'] = Dba::escape($results[$key]['video_codec']);
115
300
* This function takes the raw information and figures out
116
301
* what type of file we are dealing with for use by the tag
119
function _get_type() {
304
public function _get_type() {
121
306
/* There are a few places that the file type can
122
307
* come from, in the end we trust the encoding
310
if ($type = $this->_raw['video']['dataformat']) {
311
// Manually set the tag information
312
if ($type == 'flv') {
313
$this->_raw['tags']['flv'] = array();
315
if ($type == 'quicktime') {
316
$this->_raw['tags']['quicktime'] = array();
318
if($type == 'mpeg' OR $type == 'mpg') {
319
$this->_raw['tags']['mpeg'] = array();
322
$this->_raw['tags']['asf'] = array();
325
$this->_raw['tags']['wmv'] = array();
328
$this->_raw['tags']['avi'] = array();
330
$type = $this->_clean_type($type);
125
333
if ($type = $this->_raw['audio']['streams']['0']['dataformat']) {
126
$this->_clean_type($type);
334
$type = $this->_clean_type($type);
129
337
if ($type = $this->_raw['audio']['dataformat']) {
130
$this->_clean_type($type);
338
$type = $this->_clean_type($type);
133
341
if ($type = $this->_raw['fileformat']) {
134
$this->_clean_type($type);
342
$type = $this->_clean_type($type);
167
372
foreach ($this->_raw['tags'] as $key=>$tag_array) {
169
374
case 'vorbiscomment':
375
debug_event('_get_tags', 'Parsing vorbis', '5');
170
376
$results[$key] = $this->_parse_vorbiscomment($tag_array);
379
debug_event('_get_tags', 'Parsing id3v1', '5');
173
380
$results[$key] = $this->_parse_id3v1($tag_array);
383
debug_event('_get_tags', 'Parsing id3v2', '5');
176
384
$results[$key] = $this->_parse_id3v2($tag_array);
387
debug_event('_get_tags', 'Parsing ape', '5');
179
388
$results[$key] = $this->_parse_ape($tag_array);
181
390
case 'quicktime':
391
debug_event('_get_tags', 'Parsing quicktime', '5');
182
392
$results[$key] = $this->_parse_quicktime($tag_array);
395
debug_event('_get_tags', 'Parsing riff', '5');
185
396
$results[$key] = $this->_parse_riff($tag_array);
399
debug_event('_get_tags', 'Parsing flv', '5');
400
$results[$key] = $this->_parse_flv($this->_raw2);
404
debug_event('_get_tags', 'Parsing MPEG', '5');
405
$results[$key] = $this->_parse_mpg($this->_raw2);
409
debug_event('_get_tags', 'Parsing WMV/WMA/ASF', '5');
410
$results[$key] = $this->_parse_wmv($this->_raw2);
413
debug_event('_get_tags', 'Parsing avi', '5');
414
$results[$key] = $this->_parse_avi($this->_raw2);
417
debug_event('_get_tags', 'Parsing lyrics3', '5');
418
$results[$key] = $this->_parse_lyrics($tag_array);
188
421
debug_event('vainfo','Error: Unable to determine tag type of ' . $key . ' for file ' . $this->filename . ' Assuming id3v2','5');
189
$results[$key] = $this->_parse_id3v2($tag_array);
422
$results[$key] = $this->_parse_id3v2($this->_raw['id3v2']['comments']);
428
/* Gather Tag information from the filenames */
429
$results['file'] = $this->_parse_filename($this->filename);
417
686
private function _parse_quicktime($tags) {
422
/* go through them all! */
423
foreach ($tags as $tag=>$data) {
425
/* We need to translate a few of these tags */
427
case 'creation_date':
691
/* go through them all! */
692
foreach ($tags as $tag=>$data) {
694
/* We need to translate a few of these tags */
696
case 'creation_date':
428
697
if (strlen($data['0']) > 4) {
429
698
/* Weird Date format, attempt to normalize */
430
699
$data['0'] = date("Y",strtotime($data['0']));
432
$array['year'] = $this->_clean_tag($data['0']);
436
$array[$tag] = $this->_clean_tag($data['0']);
701
$array['year'] = $this->_clean_tag($data['0']);
705
$array[$tag] = $this->_clean_tag($data['0']);
709
// Also add in any video related stuff we might find
710
if (strpos($this->_raw2['mime_type'],'video') !== false) {
711
$info = $this->_parse_avi($this->_raw2);
712
$info['video_codec'] = $this->_raw2['quicktime']['ftyp']['fourcc'];
713
$array = array_merge($info,$array);
442
718
} // _parse_quicktime
722
* This attempts to parse our the information on an avi file and present it in some
723
* kind of sane format, this is a little hard as these files don't have tags
725
private function _parse_avi($tags) {
729
$array['title'] = urldecode($this->_pathinfo['filename']);
730
$array['video_codec'] = $tags['video']['fourcc'];
731
$array['audio_codec'] = $tags['audio']['dataformat'];
732
$array['resolution_x'] = $tags['video']['resolution_x'];
733
$array['resolution_y'] = $tags['video']['resolution_y'];
734
$array['mime'] = $tags['mime_type'];
735
$array['comment'] = $tags['video']['codec'];
743
* This attempts to parse our the information on a mpg file and present it in some
744
* kind of sane format, this is a little hard as these files don't have tags
746
private function _parse_mpg($tags) {
750
$array['title'] = urldecode($this->_pathinfo['filename']);
751
$array['video_codec'] = $tags['video']['codec'];
752
$array['audio_codec'] = $tags['audio']['dataformat'];
753
$array['resolution_x'] = $tags['video']['resolution_x'];
754
$array['resolution_y'] = $tags['video']['resolution_y'];
755
$array['mime'] = $tags['mime_type'];
756
$array['comment'] = $tags['video']['codec'];
764
* This attempts to parse our the information on a asf/wmv file and present it in some
765
* kind of sane format, this is a little hard as these files don't have tags
767
private function _parse_wmv($tags) {
771
$array['mime'] = $tags['mime_type'];
773
switch($array['mime']) {
775
case 'video/x-ms-wmv':
776
if(isset($tags['tags']['asf']['title']['0'])) {
777
$array['title'] = $tags['tags']['asf']['title']['0'];
780
$array['title'] = urldecode($this->_pathinfo['filename']);
782
$array['video_codec'] = $tags['video']['streams']['2']['codec'];
783
$array['audio_codec'] = $tags['audio']['streams']['1']['codec'];
784
$array['resolution_x'] = $tags['video']['streams']['2']['resolution_x'];
785
$array['resolution_y'] = $tags['video']['streams']['2']['resolution_y'];
786
$array['comment'] = $tags['tags']['asf']['title']['1'];
796
* This attempts to parse our the information on an flv file and present it in some
797
* kind of sane format, this is a little hard as these files don't have tags
799
private function _parse_flv($tags) {
803
$array['title'] = urldecode($this->_pathinfo['filename']);
804
$array['video_codec'] = $tags['video']['codec'];
805
$array['audio_codec'] = $tags['audio']['dataformat'];
806
$array['resolution_x'] = $tags['video']['resolution_x'];
807
$array['resolution_y'] = $tags['video']['resolution_y'];
808
$array['mime'] = $tags['mime_type'];
809
$array['comment'] = $tags['video']['codec'];
446
816
* _parse_filename
497
893
// If we've got iconv then go ahead and clear her up
498
894
if ($this->_iconv) {
499
895
/* Guess that it's UTF-8 */
896
/* Try GNU iconv //TRANSLIT extension first */
500
897
if (!$encoding) { $encoding = $this->_getID3->encoding; }
501
898
$charset = $this->encoding . '//TRANSLIT';
502
$tag = iconv($encoding,$charset,$tag);
899
$enc_tag = iconv($encoding,$charset,$tag);
900
if(strcmp($enc_tag, "") == 0) {
901
$enc_tag = iconv($encoding,$this->encoding,$tag);
911
* This returns true/false if we can do a binary parse of the file in question
912
* only the extension is passed so this can be inaccurate
914
public function can_binary_parse() {
916
// We're going to need exec for this
917
if (!is_callable('exec')) {
922
// For now I'm going to use an approved list of apps, later we should allow user config
923
switch ($this->_pathinfo['extension']) {
925
// Verify the application is there and callable
926
exec('id3v2 -v',$results,$retval);
927
if ($retval == 0) { return true; }
936
} // can_binary_parse
940
* This runs the binary parse operations here down in Ampache land
941
* it is passed the filename, and only called if can_binary_parse passes
943
public function run_binary_parse() {
945
// Switch on the extension
946
switch ($this->_pathinfo['extension']) {
948
$this->_raw['tags'] = $this->mp3_binary_parse();
951
$this->_raw['tags'] = array();
953
} // switch on extension
955
} // run_binary_parse
959
* This tries to read the tag information from mp3s using a binary and the exec() command
960
* This will not work on a lot of systems... but it should be faster
962
public function mp3_binary_parse() {
964
require_once(Config::get('prefix') . '/modules/getid3/module.tag.id3v2.php');
966
$filename = escapeshellarg($this->filename);
968
exec('id3v2 -l ' . $filename,$info,$retval);
970
if ($retval != 0) { return array(); }
975
// If we've got Id3v1 tag information
976
if (substr($info[$position],0,5) == 'id3v1') {
978
$v1['title'][] = trim(substr($info[$position],8,30));
979
$v1['artist'][] = trim(substr($info[$position],49,79));
981
$v1['album'][] = trim(substr($info[$position],8,30));
982
$v1['year'][] = trim(substr($info[$position],47,53));
983
$v1['genre'][] = trim(preg_replace("/\(\d+\)/","",substr($info[$position],60,strlen($info[$position]))));
985
$v1['comment'][]= trim(substr($info[$position],8,30));
986
$v1['track'][] = trim(substr($info[$position],48,3));
987
$results['id3v1'] = $v1;
990
if (substr($info[$position],0,5) == 'id3v2') {
992
$element_count = count($info);
993
while ($position < $element_count) {
995
$element = getid3_id3v2::FrameNameShortLookup(substr($info[$position],0,4));
996
if (!$element) { continue; }
997
$data = explode(":",$info[$position],2);
998
$value = array_pop($data);
999
$results['id3v2'][$element][] = $value;
1005
} // mp3_binary_parse
511
1007
} // end class vainfo