public function DeleteMetaFLAC() { $oldignoreuserabort = ignore_user_abort(true); if (Utils::isWindows()) { if (file_exists(Utils::getHelperAppDirectory() . 'metaflac.exe')) { // To at least see if there was a problem, compare file modification timestamps before and after writing clearstatcache(); $timestampbeforewriting = filemtime($this->filename); $commandline = Utils::getHelperAppDirectory() . 'metaflac.exe --remove-all-tags "' . $this->filename . '" 2>&1'; $metaflacError = `{$commandline}`; if (empty($metaflacError)) { clearstatcache(); if ($timestampbeforewriting == filemtime($this->filename)) { $metaflacError = 'File modification timestamp has not changed - it looks like the tags were not deleted'; } } } else { $metaflacError = 'metaflac.exe not found in ' . Utils::getHelperAppDirectory(); } } else { // It's simpler on *nix $commandline = 'metaflac --remove-all-tags "' . $this->filename . '" 2>&1'; $metaflacError = `{$commandline}`; } ignore_user_abort($oldignoreuserabort); if (!empty($metaflacError)) { $this->errors[] = 'System call to metaflac failed with this message returned: ' . "\n\n" . $metaflacError; return false; } return true; }
public function WriteVorbisComment() { // Create file with new comments $tempcommentsfilename = tempnam(Utils::getTempDirectory(), 'getID3'); if (is_writable($tempcommentsfilename) && is_file($tempcommentsfilename) && ($fpcomments = fopen($tempcommentsfilename, 'wb'))) { foreach ($this->tag_data as $key => $value) { foreach ($value as $commentdata) { fwrite($fpcomments, $this->CleanVorbisCommentName($key) . '=' . $commentdata . "\n"); } } fclose($fpcomments); } else { $this->errors[] = 'failed to open temporary tags file "' . $tempcommentsfilename . '", tags not written'; return false; } $oldignoreuserabort = ignore_user_abort(true); if (Utils::isWindows()) { if (file_exists(Utils::getHelperAppDirectory() . 'vorbiscomment.exe')) { //$commandline = '"'.Utils::getHelperAppDirectory().'vorbiscomment.exe" -w --raw -c "'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"'; // vorbiscomment works fine if you copy-paste the above commandline into a command prompt, // but refuses to work with `backtick` if there are "doublequotes" present around BOTH // the metaflac pathname and the target filename. For whatever reason...?? // The solution is simply ensure that the metaflac pathname has no spaces, // and therefore does not need to be quoted // On top of that, if error messages are not always captured properly under Windows // To at least see if there was a problem, compare file modification timestamps before and after writing clearstatcache(); $timestampbeforewriting = filemtime($this->filename); $commandline = Utils::getHelperAppDirectory() . 'vorbiscomment.exe -w --raw -c "' . $tempcommentsfilename . '" "' . $this->filename . '" 2>&1'; $VorbiscommentError = `{$commandline}`; if (empty($VorbiscommentError)) { clearstatcache(); if ($timestampbeforewriting == filemtime($this->filename)) { $VorbiscommentError = 'File modification timestamp has not changed - it looks like the tags were not written'; } } } else { $VorbiscommentError = 'vorbiscomment.exe not found in ' . Utils::getHelperAppDirectory(); } } else { $commandline = 'vorbiscomment -w --raw -c "' . $tempcommentsfilename . '" "' . $this->filename . '" 2>&1'; $VorbiscommentError = `{$commandline}`; } // Remove temporary comments file unlink($tempcommentsfilename); ignore_user_abort($oldignoreuserabort); if (!empty($VorbiscommentError)) { $this->errors[] = 'system call to vorbiscomment failed with message: ' . "\n\n" . $VorbiscommentError; return false; } return true; }
public function Analyze() { $info =& $this->getid3->info; $this->fseek($info['avdataoffset']); $ShortenHeader = $this->fread(8); $magic = 'ajkg'; if (substr($ShortenHeader, 0, 4) != $magic) { $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($ShortenHeader, 0, 4)) . '"'; return false; } $info['fileformat'] = 'shn'; $info['audio']['dataformat'] = 'shn'; $info['audio']['lossless'] = true; $info['audio']['bitrate_mode'] = 'vbr'; $info['shn']['version'] = Utils::LittleEndian2Int(substr($ShortenHeader, 4, 1)); $this->fseek($info['avdataend'] - 12); $SeekTableSignatureTest = $this->fread(12); $info['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK'); if ($info['shn']['seektable']['present']) { $info['shn']['seektable']['length'] = Utils::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4)); $info['shn']['seektable']['offset'] = $info['avdataend'] - $info['shn']['seektable']['length']; $this->fseek($info['shn']['seektable']['offset']); $SeekTableMagic = $this->fread(4); $magic = 'SEEK'; if ($SeekTableMagic != $magic) { $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['shn']['seektable']['offset'] . ', found "' . Utils::PrintHexBytes($SeekTableMagic) . '"'; return false; } else { // typedef struct tag_TSeekEntry // { // unsigned long SampleNumber; // unsigned long SHNFileByteOffset; // unsigned long SHNLastBufferReadPosition; // unsigned short SHNByteGet; // unsigned short SHNBufferOffset; // unsigned short SHNFileBitOffset; // unsigned long SHNGBuffer; // unsigned short SHNBitShift; // long CBuf0[3]; // long CBuf1[3]; // long Offset0[4]; // long Offset1[4]; // }TSeekEntry; $SeekTableData = $this->fread($info['shn']['seektable']['length'] - 16); $info['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80); //$info['shn']['seektable']['entries'] = array(); //$SeekTableOffset = 0; //for ($i = 0; $i < $info['shn']['seektable']['entry_count']; $i++) { // $SeekTableEntry['sample_number'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // $SeekTableEntry['shn_file_byte_offset'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // $SeekTableEntry['shn_last_buffer_read_position'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // $SeekTableEntry['shn_byte_get'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); // $SeekTableOffset += 2; // $SeekTableEntry['shn_buffer_offset'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); // $SeekTableOffset += 2; // $SeekTableEntry['shn_file_bit_offset'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); // $SeekTableOffset += 2; // $SeekTableEntry['shn_gbuffer'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // $SeekTableEntry['shn_bit_shift'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2)); // $SeekTableOffset += 2; // for ($j = 0; $j < 3; $j++) { // $SeekTableEntry['cbuf0'][$j] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // } // for ($j = 0; $j < 3; $j++) { // $SeekTableEntry['cbuf1'][$j] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // } // for ($j = 0; $j < 4; $j++) { // $SeekTableEntry['offset0'][$j] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // } // for ($j = 0; $j < 4; $j++) { // $SeekTableEntry['offset1'][$j] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4)); // $SeekTableOffset += 4; // } // // $info['shn']['seektable']['entries'][] = $SeekTableEntry; //} } } if (Utils::isWindows()) { $RequiredFiles = array('shorten.exe', 'cygwin1.dll', 'head.exe'); foreach ($RequiredFiles as $required_file) { if (!is_readable(Utils::getHelperAppDirectory() . $required_file)) { $info['error'][] = Utils::getHelperAppDirectory() . $required_file . ' does not exist'; return false; } } $commandline = Utils::getHelperAppDirectory() . 'shorten.exe -x "' . $info['filenamepath'] . '" - | ' . Utils::getHelperAppDirectory() . 'head.exe -c 64'; $commandline = str_replace('/', '\\', $commandline); } else { static $shorten_present; if (!isset($shorten_present)) { $shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`; } if (!$shorten_present) { $info['error'][] = 'shorten binary was not found in path or /usr/local/bin'; return false; } $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '') . 'shorten -x ' . escapeshellarg($info['filenamepath']) . ' - | head -c 64'; } $output = `{$commandline}`; if (!empty($output) && substr($output, 12, 4) == 'fmt ') { $fmt_size = Utils::LittleEndian2Int(substr($output, 16, 4)); $DecodedWAVFORMATEX = Riff::parseWAVEFORMATex(substr($output, 20, $fmt_size)); $info['audio']['channels'] = $DecodedWAVFORMATEX['channels']; $info['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample']; $info['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate']; if (substr($output, 20 + $fmt_size, 4) == 'data') { $info['playtime_seconds'] = Utils::LittleEndian2Int(substr($output, 20 + 4 + $fmt_size, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec']; } else { $info['error'][] = 'shorten failed to decode DATA chunk to expected location, cannot determine playtime'; return false; } $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds'] * 8; } else { $info['error'][] = 'shorten failed to decode file to WAV for parsing'; return false; } return true; }
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; $this->fseek($info['avdataoffset']); $imageinfo = array(); //list($width, $height, $type) = Utils::GetDataImageSize($this->fread($info['filesize']), $imageinfo); list($width, $height, $type) = getimagesize($info['filenamepath'], $imageinfo); // http://www.getid3.org/phpBB3/viewtopic.php?t=1474 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']['comments'][$IPTCrecordName][$IPTCrecordTagName])) { $info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName][] = $value; } else { $info['iptc']['comments'][$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['warning'][] = 'known issue: https://bugs.php.net/bug.php?id=62523'; //return false; $info['jpg']['exif'] = exif_read_data($info['filenamepath'], null, 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 ' . (Utils::isWindows() ? '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] : ''; $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] = Utils::DecimalizeFraction($value); } } $info['jpg']['exif']['GPS']['computed']['timestamp'] = gmmktime($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] = Utils::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] = Utils::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']['GPSAltitudeRef'])) { $info['jpg']['exif']['GPS']['GPSAltitudeRef'] = ord($info['jpg']['exif']['GPS']['GPSAltitudeRef']); // 0 = above sea level; 1 = below sea level } if (isset($info['jpg']['exif']['GPS']['GPSAltitude'])) { $direction_multiplier = !empty($info['jpg']['exif']['GPS']['GPSAltitudeRef']) ? -1 : 1; // 0 = above sea level; 1 = below sea level $info['jpg']['exif']['GPS']['computed']['altitude'] = $direction_multiplier * Utils::DecimalizeFraction($info['jpg']['exif']['GPS']['GPSAltitude']); } } if (isset($info['filenamepath'])) { $image_xmp = new Xmp($info['filenamepath']); $xmp_raw = $image_xmp->getAllTags(); foreach ($xmp_raw as $key => $value) { if (strpos($key, ':')) { list($subsection, $tagname) = explode(':', $key); $info['xmp'][$subsection][$tagname] = $this->CastAsAppropriate($value); } else { $info['warning'][] = 'XMP: expecting "<subsection>:<tagname>", found "' . $key . '"'; } } } if (!$returnOK) { unset($info['fileformat']); return false; } return true; }