/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; $info['fileformat'] = 'jpg'; $info['video']['dataformat'] = 'jpg'; $info['video']['lossless'] = false; $info['video']['bits_per_sample'] = 24; $info['video']['pixel_aspect_ratio'] = (double) 1; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); $imageinfo = array(); list($width, $height, $type) = GetId3_Lib_Helper::GetDataImageSize(fread($this->getid3->fp, $info['filesize']), $imageinfo); if (isset($imageinfo['APP13'])) { // http://php.net/iptcparse // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html $iptc_parsed = iptcparse($imageinfo['APP13']); if (is_array($iptc_parsed)) { foreach ($iptc_parsed as $iptc_key_raw => $iptc_values) { list($iptc_record, $iptc_tagkey) = explode('#', $iptc_key_raw); $iptc_tagkey = intval(ltrim($iptc_tagkey, '0')); foreach ($iptc_values as $key => $value) { $IPTCrecordName = $this->IPTCrecordName($iptc_record); $IPTCrecordTagName = $this->IPTCrecordTagName($iptc_record, $iptc_tagkey); if (isset($info['iptc'][$IPTCrecordName][$IPTCrecordTagName])) { $info['iptc'][$IPTCrecordName][$IPTCrecordTagName][] = $value; } else { $info['iptc'][$IPTCrecordName][$IPTCrecordTagName] = array($value); } } } } } $returnOK = false; switch ($type) { case IMG_JPG: $info['video']['resolution_x'] = $width; $info['video']['resolution_y'] = $height; if (isset($imageinfo['APP1'])) { if (function_exists('exif_read_data')) { if (substr($imageinfo['APP1'], 0, 4) == 'Exif') { $info['jpg']['exif'] = @exif_read_data($info['filenamepath'], '', true, false); } else { $info['warning'][] = 'exif_read_data() cannot parse non-EXIF data in APP1 (expected "Exif", found "' . substr($imageinfo['APP1'], 0, 4) . '")'; } } else { $info['warning'][] = 'EXIF parsing only available when ' . (GetId3_GetId3::environmentIsWindows() ? 'php_exif.dll enabled' : 'compiled with --enable-exif'); } } $returnOK = true; break; default: break; } $cast_as_appropriate_keys = array('EXIF', 'IFD0', 'THUMBNAIL'); foreach ($cast_as_appropriate_keys as $exif_key) { if (isset($info['jpg']['exif'][$exif_key])) { foreach ($info['jpg']['exif'][$exif_key] as $key => $value) { $info['jpg']['exif'][$exif_key][$key] = $this->CastAsAppropriate($value); } } } if (isset($info['jpg']['exif']['GPS'])) { if (isset($info['jpg']['exif']['GPS']['GPSVersion'])) { for ($i = 0; $i < 4; $i++) { $version_subparts[$i] = ord(substr($info['jpg']['exif']['GPS']['GPSVersion'], $i, 1)); } $info['jpg']['exif']['GPS']['computed']['version'] = 'v' . implode('.', $version_subparts); } if (isset($info['jpg']['exif']['GPS']['GPSDateStamp'])) { $explodedGPSDateStamp = explode(':', $info['jpg']['exif']['GPS']['GPSDateStamp']); $computed_time[5] = isset($explodedGPSDateStamp[0]) ? $explodedGPSDateStamp[0] : ''; $computed_time[3] = isset($explodedGPSDateStamp[1]) ? $explodedGPSDateStamp[1] : ''; $computed_time[4] = isset($explodedGPSDateStamp[2]) ? $explodedGPSDateStamp[2] : ''; if (function_exists('date_default_timezone_set')) { date_default_timezone_set('UTC'); } else { ini_set('date.timezone', 'UTC'); } $computed_time = array(0 => 0, 1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0); if (isset($info['jpg']['exif']['GPS']['GPSTimeStamp']) && is_array($info['jpg']['exif']['GPS']['GPSTimeStamp'])) { foreach ($info['jpg']['exif']['GPS']['GPSTimeStamp'] as $key => $value) { $computed_time[$key] = GetId3_Lib_Helper::DecimalizeFraction($value); } } $info['jpg']['exif']['GPS']['computed']['timestamp'] = mktime($computed_time[0], $computed_time[1], $computed_time[2], $computed_time[3], $computed_time[4], $computed_time[5]); } if (isset($info['jpg']['exif']['GPS']['GPSLatitude']) && is_array($info['jpg']['exif']['GPS']['GPSLatitude'])) { $direction_multiplier = isset($info['jpg']['exif']['GPS']['GPSLatitudeRef']) && $info['jpg']['exif']['GPS']['GPSLatitudeRef'] == 'S' ? -1 : 1; foreach ($info['jpg']['exif']['GPS']['GPSLatitude'] as $key => $value) { $computed_latitude[$key] = GetId3_Lib_Helper::DecimalizeFraction($value); } $info['jpg']['exif']['GPS']['computed']['latitude'] = $direction_multiplier * ($computed_latitude[0] + $computed_latitude[1] / 60 + $computed_latitude[2] / 3600); } if (isset($info['jpg']['exif']['GPS']['GPSLongitude']) && is_array($info['jpg']['exif']['GPS']['GPSLongitude'])) { $direction_multiplier = isset($info['jpg']['exif']['GPS']['GPSLongitudeRef']) && $info['jpg']['exif']['GPS']['GPSLongitudeRef'] == 'W' ? -1 : 1; foreach ($info['jpg']['exif']['GPS']['GPSLongitude'] as $key => $value) { $computed_longitude[$key] = GetId3_Lib_Helper::DecimalizeFraction($value); } $info['jpg']['exif']['GPS']['computed']['longitude'] = $direction_multiplier * ($computed_longitude[0] + $computed_longitude[1] / 60 + $computed_longitude[2] / 3600); } if (isset($info['jpg']['exif']['GPS']['GPSAltitude'])) { $direction_multiplier = isset($info['jpg']['exif']['GPS']['GPSAltitudeRef']) && $info['jpg']['exif']['GPS']['GPSAltitudeRef'] === chr(1) ? -1 : 1; $info['jpg']['exif']['GPS']['computed']['altitude'] = $direction_multiplier * GetId3_Lib_Helper::DecimalizeFraction($info['jpg']['exif']['GPS']['GPSAltitude']); } } if (class_exists('GetId3_Module_Tag_Xmp')) { if (isset($info['filenamepath'])) { $image_xmp = new GetId3_Module_Tag_Xmp($info['filenamepath']); $xmp_raw = $image_xmp->getAllTags(); foreach ($xmp_raw as $key => $value) { list($subsection, $tagname) = explode(':', $key); $info['xmp'][$subsection][$tagname] = $this->CastAsAppropriate($value); } } } if (!$returnOK) { unset($info['fileformat']); return false; } return true; }
/** * * @return boolean */ public function Analyze() { $info =& $this->getid3->info; if (!GetId3_Lib_Helper::intValueSupported($info['filesize'])) { $info['warning'][] = 'Unable to check for APEtags because file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB'; return false; } $id3v1tagsize = 128; $apetagheadersize = 32; $lyrics3tagsize = 10; if ($this->overrideendoffset == 0) { fseek($this->getid3->fp, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END); $APEfooterID3v1 = fread($this->getid3->fp, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize); //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) { if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') { // APE tag found before ID3v1 $info['ape']['tag_offset_end'] = $info['filesize'] - $id3v1tagsize; //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) { } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') { // APE tag found, no ID3v1 $info['ape']['tag_offset_end'] = $info['filesize']; } } else { fseek($this->getid3->fp, $this->overrideendoffset - $apetagheadersize, SEEK_SET); if (fread($this->getid3->fp, 8) == 'APETAGEX') { $info['ape']['tag_offset_end'] = $this->overrideendoffset; } } if (!isset($info['ape']['tag_offset_end'])) { // APE tag not found unset($info['ape']); return false; } // shortcut $thisfile_ape =& $info['ape']; fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET); $APEfooterData = fread($this->getid3->fp, 32); if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) { $info['error'][] = 'Error parsing APE footer at offset ' . $thisfile_ape['tag_offset_end']; return false; } if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET); $thisfile_ape['tag_offset_start'] = ftell($this->getid3->fp); $APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize); } else { $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize']; fseek($this->getid3->fp, $thisfile_ape['tag_offset_start'], SEEK_SET); $APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize']); } $info['avdataend'] = $thisfile_ape['tag_offset_start']; if (isset($info['id3v1']['tag_offset_start']) && $info['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end']) { $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data'; unset($info['id3v1']); foreach ($info['warning'] as $key => $value) { if ($value == 'Some ID3v1 fields do not use NULL characters for padding') { unset($info['warning'][$key]); sort($info['warning']); break; } } } $offset = 0; if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) { $offset += $apetagheadersize; } else { $info['error'][] = 'Error parsing APE header at offset ' . $thisfile_ape['tag_offset_start']; return false; } } // shortcut $info['replay_gain'] = array(); $thisfile_replaygain =& $info['replay_gain']; for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) { $value_size = GetId3_Lib_Helper::LittleEndian2Int(substr($APEtagData, $offset, 4)); $offset += 4; $item_flags = GetId3_Lib_Helper::LittleEndian2Int(substr($APEtagData, $offset, 4)); $offset += 4; if (strstr(substr($APEtagData, $offset), "") === false) { $info['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #' . $i . ' and value. ItemKey starts ' . $offset . ' bytes into the APE tag, at file offset ' . ($thisfile_ape['tag_offset_start'] + $offset); return false; } $ItemKeyLength = strpos($APEtagData, "", $offset) - $offset; $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength)); // shortcut $thisfile_ape['items'][$item_key] = array(); $thisfile_ape_items_current =& $thisfile_ape['items'][$item_key]; $thisfile_ape_items_current['offset'] = $thisfile_ape['tag_offset_start'] + $offset; $offset += $ItemKeyLength + 1; // skip 0x00 terminator $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size); $offset += $value_size; $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags); switch ($thisfile_ape_items_current['flags']['item_contents_raw']) { case 0: // UTF-8 // UTF-8 case 3: // Locator (URL, filename, etc), UTF-8 encoded $thisfile_ape_items_current['data'] = explode("", trim($thisfile_ape_items_current['data'])); break; default: // binary data break; } switch (strtolower($item_key)) { case 'replaygain_track_gain': $thisfile_replaygain['track']['adjustment'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! $thisfile_replaygain['track']['originator'] = 'unspecified'; break; case 'replaygain_track_peak': $thisfile_replaygain['track']['peak'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! $thisfile_replaygain['track']['originator'] = 'unspecified'; if ($thisfile_replaygain['track']['peak'] <= 0) { $info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: ' . $thisfile_replaygain['track']['peak'] . ' (original value = "' . $thisfile_ape_items_current['data'][0] . '")'; } break; case 'replaygain_album_gain': $thisfile_replaygain['album']['adjustment'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! $thisfile_replaygain['album']['originator'] = 'unspecified'; break; case 'replaygain_album_peak': $thisfile_replaygain['album']['peak'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! $thisfile_replaygain['album']['originator'] = 'unspecified'; if ($thisfile_replaygain['album']['peak'] <= 0) { $info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: ' . $thisfile_replaygain['album']['peak'] . ' (original value = "' . $thisfile_ape_items_current['data'][0] . '")'; } break; case 'mp3gain_undo': list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]); $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left); $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right); $thisfile_replaygain['mp3gain']['undo_wrap'] = $mp3gain_undo_wrap == 'Y' ? true : false; break; case 'mp3gain_minmax': list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]); $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min); $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max); break; case 'mp3gain_album_minmax': list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]); $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min); $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max); break; case 'tracknumber': if (is_array($thisfile_ape_items_current['data'])) { foreach ($thisfile_ape_items_current['data'] as $comment) { $thisfile_ape['comments']['track'][] = $comment; } } break; case 'cover art (artist)': case 'cover art (back)': case 'cover art (band logo)': case 'cover art (band)': case 'cover art (colored fish)': case 'cover art (composer)': case 'cover art (conductor)': case 'cover art (front)': case 'cover art (icon)': case 'cover art (illustration)': case 'cover art (lead)': case 'cover art (leaflet)': case 'cover art (lyricist)': case 'cover art (media)': case 'cover art (movie scene)': case 'cover art (other icon)': case 'cover art (other)': case 'cover art (performance)': case 'cover art (publisher logo)': case 'cover art (recording)': case 'cover art (studio)': // list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("", $thisfile_ape_items_current['data'], 2); $thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename'] . ""); $thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']); $thisfile_ape_items_current['image_mime'] = ''; $imageinfo = array(); $imagechunkcheck = GetId3_Lib_Helper::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo); $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); do { if ($this->inline_attachments === false) { // skip entirely unset($thisfile_ape_items_current['data']); break; } if ($this->inline_attachments === true) { // great } elseif (is_int($this->inline_attachments)) { if ($this->inline_attachments < $thisfile_ape_items_current['data_length']) { // too big, skip $info['warning'][] = 'attachment at ' . $thisfile_ape_items_current['offset'] . ' is too large to process inline (' . number_format($thisfile_ape_items_current['data_length']) . ' bytes)'; unset($thisfile_ape_items_current['data']); break; } } elseif (is_string($this->inline_attachments)) { $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR); if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) { // cannot write, skip $info['warning'][] = 'attachment at ' . $thisfile_ape_items_current['offset'] . ' cannot be saved to "' . $this->inline_attachments . '" (not writable)'; unset($thisfile_ape_items_current['data']); break; } } // if we get this far, must be OK if (is_string($this->inline_attachments)) { $destination_filename = $this->inline_attachments . DIRECTORY_SEPARATOR . md5($info['filenamepath']) . '_' . $thisfile_ape_items_current['data_offset']; if (!file_exists($destination_filename) || is_writable($destination_filename)) { file_put_contents($destination_filename, $thisfile_ape_items_current['data']); } else { $info['warning'][] = 'attachment at ' . $thisfile_ape_items_current['offset'] . ' cannot be saved to "' . $destination_filename . '" (not writable)'; } $thisfile_ape_items_current['data_filename'] = $destination_filename; unset($thisfile_ape_items_current['data']); } else { if (!isset($info['ape']['comments']['picture'])) { $info['ape']['comments']['picture'] = array(); } $info['ape']['comments']['picture'][] = array('data' => $thisfile_ape_items_current['data'], 'image_mime' => $thisfile_ape_items_current['image_mime']); } } while (false); break; default: if (is_array($thisfile_ape_items_current['data'])) { foreach ($thisfile_ape_items_current['data'] as $comment) { $thisfile_ape['comments'][strtolower($item_key)][] = $comment; } } break; } } if (empty($thisfile_replaygain)) { unset($info['replay_gain']); } return true; }
/** * * @return boolean */ public function ParseVorbisComments() { $info =& $this->getid3->info; $OriginalOffset = $this->ftell(); $commentdataoffset = 0; $VorbisCommentPage = 1; switch ($info['audio']['dataformat']) { case 'vorbis': case 'speex': $CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block $this->fseek($CommentStartOffset); $commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments']; $commentdata = $this->fread(self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset); if ($info['audio']['dataformat'] == 'vorbis') { $commentdataoffset += strlen('vorbis') + 1; } break; case 'flac': $CommentStartOffset = $info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4; $this->fseek($CommentStartOffset); $commentdata = $this->fread($info['flac']['VORBIS_COMMENT']['raw']['block_length']); break; default: return false; } $VendorSize = GetId3_Lib_Helper::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); $commentdataoffset += 4; $info['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize); $commentdataoffset += $VendorSize; $CommentsCount = GetId3_Lib_Helper::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); $commentdataoffset += 4; $info['avdataoffset'] = $CommentStartOffset + $commentdataoffset; $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT'); $ThisFileInfo_ogg_comments_raw =& $info['ogg']['comments_raw']; for ($i = 0; $i < $CommentsCount; $i++) { $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset; if ($this->ftell() < $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4) { if ($oggpageinfo = $this->ParseOggPageHeader()) { $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; $VorbisCommentPage++; // First, save what we haven't read yet $AsYetUnusedData = substr($commentdata, $commentdataoffset); // Then take that data off the end $commentdata = substr($commentdata, 0, $commentdataoffset); // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct $commentdata .= str_repeat("", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); $commentdataoffset += 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']; // Finally, stick the unused data back on the end $commentdata .= $AsYetUnusedData; //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); $commentdata .= $this->fread($this->OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1)); } } $ThisFileInfo_ogg_comments_raw[$i]['size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); // replace avdataoffset with position just after the last vorbiscomment $info['avdataoffset'] = $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + $ThisFileInfo_ogg_comments_raw[$i]['size'] + 4; $commentdataoffset += 4; while (strlen($commentdata) - $commentdataoffset < $ThisFileInfo_ogg_comments_raw[$i]['size']) { if ($ThisFileInfo_ogg_comments_raw[$i]['size'] > $info['avdataend'] || $ThisFileInfo_ogg_comments_raw[$i]['size'] < 0) { $info['warning'][] = 'Invalid Ogg comment size (comment #' . $i . ', claims to be ' . number_format($ThisFileInfo_ogg_comments_raw[$i]['size']) . ' bytes) - aborting reading comments'; break 2; } $VorbisCommentPage++; $oggpageinfo = $this->ParseOggPageHeader(); $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; // First, save what we haven't read yet $AsYetUnusedData = substr($commentdata, $commentdataoffset); // Then take that data off the end $commentdata = substr($commentdata, 0, $commentdataoffset); // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct $commentdata .= str_repeat("", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); $commentdataoffset += 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']; // Finally, stick the unused data back on the end $commentdata .= $AsYetUnusedData; //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); if (!isset($info['ogg']['pageheader'][$VorbisCommentPage])) { $info['warning'][] = 'undefined Vorbis Comment page "' . $VorbisCommentPage . '" at offset ' . $this->ftell(); break; } $readlength = self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1); if ($readlength <= 0) { $info['warning'][] = 'invalid length Vorbis Comment page "' . $VorbisCommentPage . '" at offset ' . $this->ftell(); break; } $commentdata .= $this->fread($readlength); //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset']; } $ThisFileInfo_ogg_comments_raw[$i]['offset'] = $commentdataoffset; $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo_ogg_comments_raw[$i]['size']); $commentdataoffset += $ThisFileInfo_ogg_comments_raw[$i]['size']; if (!$commentstring) { // no comment? $info['warning'][] = 'Blank Ogg comment [' . $i . ']'; } elseif (strstr($commentstring, '=')) { $commentexploded = explode('=', $commentstring, 2); $ThisFileInfo_ogg_comments_raw[$i]['key'] = strtoupper($commentexploded[0]); $ThisFileInfo_ogg_comments_raw[$i]['value'] = isset($commentexploded[1]) ? $commentexploded[1] : ''; $ThisFileInfo_ogg_comments_raw[$i]['data'] = base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']); $ThisFileInfo_ogg_comments_raw[$i]['data_length'] = strlen($ThisFileInfo_ogg_comments_raw[$i]['data']); if ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'METADATA_BLOCK_PICTURE') { // http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE // The unencoded format is that of the FLAC picture block. The fields are stored in big endian order as in FLAC, picture data is stored according to the relevant standard. // http://flac.sourceforge.net/format.html#metadata_block_picture $getid3_temp = new GetId3_GetId3(); $getid3_flac = new GetId3_Module_Audio_Flac($getid3_temp); $getid3_flac->data_string = $ThisFileInfo_ogg_comments_raw[$i]['data']; $getid3_flac->data_string_flag = true; if ($getid3_flac->parsePICTURE()) { if (!empty($getid3_temp->info['flac']['PICTURE'])) { foreach ($getid3_temp->info['flac']['PICTURE'] as $key => $value) { $ThisFileInfo_ogg_comments_raw[$i]['data'] = $value['data']; $ThisFileInfo_ogg_comments_raw[$i]['data_length'] = strlen($value['data']); $ThisFileInfo_ogg_comments_raw[$i]['image_mime'] = $value['image_mime']; $ThisFileInfo_ogg_comments_raw[$i]['width'] = $value['width']; $ThisFileInfo_ogg_comments_raw[$i]['height'] = $value['height']; $ThisFileInfo_ogg_comments_raw[$i]['type'] = $value['type']; $ThisFileInfo_ogg_comments_raw[$i]['typeid'] = $value['typeid']; $ThisFileInfo_ogg_comments_raw[$i]['color_depth'] = $value['color_depth']; $ThisFileInfo_ogg_comments_raw[$i]['colors_indexed'] = $value['colors_indexed']; } } } else { $info['warning'][] = 'Failed to GetId3_flac.parsePICTURE()'; } unset($getid3_flac, $getid3_temp); } if (preg_match('#^(BM|GIF|\\xFF\\xD8\\xFF|\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A|II\\x2A\\x00|MM\\x00\\x2A)#s', $ThisFileInfo_ogg_comments_raw[$i]['data'])) { $imageinfo = array(); $imagechunkcheck = GetId3_Lib_Helper::GetDataImageSize($ThisFileInfo_ogg_comments_raw[$i]['data'], $imageinfo); unset($imageinfo); if (!empty($imagechunkcheck)) { $ThisFileInfo_ogg_comments_raw[$i]['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); if ($ThisFileInfo_ogg_comments_raw[$i]['image_mime'] && $ThisFileInfo_ogg_comments_raw[$i]['image_mime'] != 'application/octet-stream') { unset($ThisFileInfo_ogg_comments_raw[$i]['value']); } } } if (isset($ThisFileInfo_ogg_comments_raw[$i]['value'])) { unset($ThisFileInfo_ogg_comments_raw[$i]['data']); $info['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value']; } else { do { if ($this->inline_attachments === false) { // skip entirely unset($ThisFileInfo_ogg_comments_raw[$i]['data']); break; } if ($this->inline_attachments === true) { // great } elseif (is_int($this->inline_attachments)) { if ($this->inline_attachments < $ThisFileInfo_ogg_comments_raw[$i]['data_length']) { // too big, skip $info['warning'][] = 'attachment at ' . $ThisFileInfo_ogg_comments_raw[$i]['offset'] . ' is too large to process inline (' . number_format($ThisFileInfo_ogg_comments_raw[$i]['data_length']) . ' bytes)'; unset($ThisFileInfo_ogg_comments_raw[$i]['data']); break; } } elseif (is_string($this->inline_attachments)) { $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR); if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) { // cannot write, skip $info['warning'][] = 'attachment at ' . $ThisFileInfo_ogg_comments_raw[$i]['offset'] . ' cannot be saved to "' . $this->inline_attachments . '" (not writable)'; unset($ThisFileInfo_ogg_comments_raw[$i]['data']); break; } } // if we get this far, must be OK if (is_string($this->inline_attachments)) { $destination_filename = $this->inline_attachments . DIRECTORY_SEPARATOR . md5($info['filenamepath']) . '_' . $ThisFileInfo_ogg_comments_raw[$i]['offset']; if (!file_exists($destination_filename) || is_writable($destination_filename)) { file_put_contents($destination_filename, $ThisFileInfo_ogg_comments_raw[$i]['data']); } else { $info['warning'][] = 'attachment at ' . $ThisFileInfo_ogg_comments_raw[$i]['offset'] . ' cannot be saved to "' . $destination_filename . '" (not writable)'; } $ThisFileInfo_ogg_comments_raw[$i]['data_filename'] = $destination_filename; unset($ThisFileInfo_ogg_comments_raw[$i]['data']); } else { $info['ogg']['comments']['picture'][] = array('data' => $ThisFileInfo_ogg_comments_raw[$i]['data'], 'image_mime' => $ThisFileInfo_ogg_comments_raw[$i]['image_mime']); } } while (false); } } else { $info['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair [' . $i . ']: ' . $commentstring; } } // Replay Gain Adjustment // http://privatewww.essex.ac.uk/~djmrob/replaygain/ if (isset($info['ogg']['comments']) && is_array($info['ogg']['comments'])) { foreach ($info['ogg']['comments'] as $index => $commentvalue) { switch ($index) { case 'rg_audiophile': case 'replaygain_album_gain': $info['replay_gain']['album']['adjustment'] = (double) $commentvalue[0]; unset($info['ogg']['comments'][$index]); break; case 'rg_radio': case 'replaygain_track_gain': $info['replay_gain']['track']['adjustment'] = (double) $commentvalue[0]; unset($info['ogg']['comments'][$index]); break; case 'replaygain_album_peak': $info['replay_gain']['album']['peak'] = (double) $commentvalue[0]; unset($info['ogg']['comments'][$index]); break; case 'rg_peak': case 'replaygain_track_peak': $info['replay_gain']['track']['peak'] = (double) $commentvalue[0]; unset($info['ogg']['comments'][$index]); break; case 'replaygain_reference_loudness': $info['replay_gain']['reference_volume'] = (double) $commentvalue[0]; unset($info['ogg']['comments'][$index]); break; default: // do nothing break; } } } $this->fseek($OriginalOffset); return true; }
public function ASF_WMpicture(&$data) { //typedef struct _WMPicture{ // LPWSTR pwszMIMEType; // BYTE bPictureType; // LPWSTR pwszDescription; // DWORD dwDataLen; // BYTE* pbData; //} WM_PICTURE; $WMpicture = array(); $offset = 0; $WMpicture['image_type_id'] = GetId3_Lib_Helper::LittleEndian2Int(substr($data, $offset, 1)); $offset += 1; $WMpicture['image_type'] = $this->WMpictureTypeLookup($WMpicture['image_type_id']); $WMpicture['image_size'] = GetId3_Lib_Helper::LittleEndian2Int(substr($data, $offset, 4)); $offset += 4; $WMpicture['image_mime'] = ''; do { $next_byte_pair = substr($data, $offset, 2); $offset += 2; $WMpicture['image_mime'] .= $next_byte_pair; } while ($next_byte_pair !== ""); $WMpicture['image_description'] = ''; do { $next_byte_pair = substr($data, $offset, 2); $offset += 2; $WMpicture['image_description'] .= $next_byte_pair; } while ($next_byte_pair !== ""); $WMpicture['dataoffset'] = $offset; $WMpicture['data'] = substr($data, $offset); $imageinfo = array(); $WMpicture['image_mime'] = ''; $imagechunkcheck = GetId3_Lib_Helper::GetDataImageSize($WMpicture['data'], $imageinfo); unset($imageinfo); if (!empty($imagechunkcheck)) { $WMpicture['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); } if (!isset($this->getid3->info['asf']['comments']['picture'])) { $this->getid3->info['asf']['comments']['picture'] = array(); } $this->getid3->info['asf']['comments']['picture'][] = array('data' => $WMpicture['data'], 'image_mime' => $WMpicture['image_mime']); return $WMpicture; }