Plugin Directory

Changeset 3479623


Ignore:
Timestamp:
03/10/2026 11:54:41 PM (2 weeks ago)
Author:
dglingren
Message:

Fix XMP metadata extraction for MP3 files with embedded images.

Location:
media-library-assistant/trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • media-library-assistant/trunk/includes/class-mla-core.php

    r3477233 r3479623  
    3131     * @var string
    3232     */
    33     const MLA_DEVELOPMENT_VERSION = '20260307';
     33    const MLA_DEVELOPMENT_VERSION = '20260310';
    3434
    3535    /**
  • media-library-assistant/trunk/includes/class-mla-data.php

    r3476788 r3479623  
    28292829//error_log( __LINE__ . " MLAData::mla_parse_xmp_metadata( {$new_offset} ) ", 0 );
    28302830                $xmp_chunk = file_get_contents( $file_name, true, NULL, $new_offset, $chunksize );
    2831 //error_log( __LINE__ . " MLAData::mla_parse_xmp_metadata( {$new_offset} ) chunk = \r\n" . MLAData::mla_hex_dump( $xmp_chunk ), 0 );
     2831//error_log( __LINE__ . " MLAData::mla_parse_xmp_metadata( {$new_offset} ) chunk = \r\n" . MLAData::mla_hex_dump( $xmp_chunk ), 0, 32, 0 );
    28322832            } // while not found
    28332833        } else { // if not found
     
    28352835        }
    28362836
    2837 //error_log( __LINE__ . " MLAData::mla_parse_xmp_metadata( {$start_tag} ) ", 0 );
     2837//error_log( __LINE__ . " MLAData::mla_parse_xmp_metadata( offset {$new_offset}, start {$start_tag} ) ", 0 );
    28382838        if ( false === $start_tag ) {
    28392839            return NULL;
    28402840        }
    28412841
    2842         // If necessary and possible, expand the $xmp_chunk until it contains the start tag
    2843         if ( false === ( $end_tag = strpos( $xmp_chunk, '</x:xmpmeta>', $start_tag ) ) && ( $chunksize == strlen( $xmp_chunk ) ) ) {
     2842        // If necessary and possible, expand the $xmp_chunk until it contains the end tag
     2843        if ( false === ( $end_tag = strpos( $xmp_chunk, '</x:xmpmeta>', $start_tag ) ) && ( $chunksize === strlen( $xmp_chunk ) ) ) {
    28442844            $new_offset = $new_offset + $start_tag;
    28452845            $start_tag = 0;
     
    28562856        }
    28572857
    2858         $xmp_string = "<?xml version='1.0'?>\n" . substr( $xmp_chunk, $start_tag, ( $end_tag + 12 ) - $start_tag );
     2858        $length =  ( $end_tag + 12 ) - $start_tag;
     2859//error_log( __LINE__ . " MLAData::mla_parse_xmp_metadata( offset {$new_offset}, start {$start_tag}, end {$end_tag}, length {$length} ) ", 0 );
     2860        $xmp_string = "<?xml version='1.0'?>\n" . substr( $xmp_chunk, $start_tag, $length );
    28592861//error_log( __LINE__ . " MLAData::mla_parse_xmp_metadata xmp_string = " . var_export( $xmp_string, true ), 0 );
    28602862//error_log( __LINE__ . "  MLAData::mla_parse_xmp_metadata xmp_string = \r\n" . MLAData::mla_hex_dump( $xmp_string ), 0 );
    28612863
    28622864        $results = MLAData::mla_parse_xml_string( $xmp_string );
     2865        if ( is_array( $results ) ) {
     2866            $results['xmp_offset'] = $new_offset + $start_tag;
     2867            $results['xmp_length'] = $length;
    28632868//error_log( __LINE__ . " MLAData::mla_parse_xmp_metadata results = " . var_export( $results, true ), 0 );
    2864         return $results;
     2869
     2870            //$more_results = self::mla_parse_xmp_metadata( $file_name, $new_offset + $start_tag + $length );
     2871//error_log( __LINE__ . " MLAData::mla_parse_xmp_metadata more_results = " . var_export( $results, true ), 0 );
     2872        }
     2873
     2874return $results;
    28652875    }
    28662876
     
    42794289        $id3_metadata = self::mla_fetch_attachment_id3_metadata( $post_id );
    42804290        if ( ! empty( $id3_metadata ) && !isset( $id3_metadata['error'] ) ) {
     4291            // Replace image blobs with a text message
     4292            if ( isset( $id3_metadata['comments']['picture'] ) && is_array( $id3_metadata['comments']['picture'] ) ) {
     4293                foreach ( $id3_metadata['comments']['picture'] as $key => $picture ) {
     4294                    if ( isset( $picture['data'] ) ) {
     4295                        $image_size = strlen( $picture['data'] );
     4296                        $id3_metadata['comments']['picture'][ $key ]['data'] = '( ' . $image_size . ' ' . __( 'bytes of BINARY DATA', 'media-library-assistant' ) . ' )';
     4297                    }
     4298                }
     4299
     4300            }
     4301
     4302            if ( isset( $id3_metadata['image']['data'] ) ) {
     4303                $image_size = strlen( $id3_metadata['image']['data'] );
     4304                $id3_metadata['image']['data'] = '( ' . $image_size . ' ' . __( 'bytes of BINARY DATA', 'media-library-assistant' ) . ' )';
     4305            }
     4306
    42814307            $text .= self::_compose_metadata_array( $id3_metadata, 'id3', ':' );
    42824308        }
    42834309
    42844310        return $text; // var_export( $metadata, true );
     4311    }
     4312
     4313    /**
     4314     * Fetch XMP metadata for an MP3 audio file.
     4315     *
     4316     * @since 3.35
     4317     *
     4318     * @param   string  Path to the MP3 file.
     4319     *
     4320     * @return  array   XMP Meta data variables, if present, or an empty string
     4321     */
     4322    public static function mla_fetch_mp3_xmp_metadata( $path ) {
     4323        $results = array(
     4324            'mla_id3_xmp_metadata' => array(),
     4325            'mla_id3_xmp_errors' => array(),
     4326        );
     4327
     4328        $data = file_get_contents( $path );
     4329        if ( false === $data ) {
     4330            $results['mla_id3_xmp_errors'][] = "Unable to read MP3 file";
     4331            return $results;
     4332        }
     4333
     4334        if ( 'ID3' !== substr( $data, 0, 3 ) ) {
     4335            $results['mla_id3_xmp_errors'][] = "Invalid MP3 file; no ID3 tag found";
     4336            return $results;
     4337        }
     4338
     4339        // Parse synchsafe tag size
     4340        $tagSize = (
     4341            (ord($data[6]) & 0x7F) << 21 |
     4342            (ord($data[7]) & 0x7F) << 14 |
     4343            (ord($data[8]) & 0x7F) << 7  |
     4344            (ord($data[9]) & 0x7F)
     4345        );
     4346
     4347        $tagData = substr( $data, 10, $tagSize );
     4348        $offset = 0;
     4349        $len = strlen( $tagData );
     4350        $xmp_frame = '';
     4351
     4352        while ($offset + 10 <= $len) {
     4353            $frameId   = substr( $tagData, $offset, 4) ;
     4354            $frameSize = unpack( "N", substr( $tagData, $offset + 4, 4 ) )[1];
     4355
     4356            if ( $frameSize <= 0 || $offset + 10 + $frameSize > $len ) {
     4357                $results['mla_id3_xmp_errors'][] = "Invalid MP3 file; bad frame size at offset {$offset}";
     4358                break;
     4359            }
     4360
     4361//error_log( __LINE__ . " MLAData::mla_fetch_mp3_xmp_metadata( $offset, $frameId, $frameSize )", 0 );
     4362            $frameData = substr( $tagData, $offset + 10, $frameSize );
     4363
     4364            // XMP FRAME (XMP , XMP\0, XMP_)
     4365            if ( str_starts_with( $frameId, "XMP" ) ) {
     4366                $xmp_frame = $frameData;
     4367                break;
     4368            }
     4369
     4370            // PRIV FRAME
     4371            if ( $frameId === "PRIV" ) {
     4372
     4373                // Owner identifier (null-terminated)
     4374                $nullPos = strpos( $frameData, "\x00" );
     4375
     4376                if ( false !== $nullPos ) {
     4377                    if ( 'XMP' === substr($frameData, 0, $nullPos) ) {
     4378                        $xmp_frame = substr($frameData, $nullPos + 1);
     4379                        break;
     4380                    }
     4381                }
     4382            }
     4383
     4384            $offset += 10 + $frameSize;
     4385        }
     4386
     4387        if ( ! empty( $xmp_frame ) ) {
     4388//error_log( __LINE__ . " MLAData::mla_fetch_mp3_xmp_metadata( {$path} ) XMP FRAME = " . var_export( $xmp_frame, true ), 0 );
     4389            $start = strpos( $xmp_frame, '<x:xmpmeta' );
     4390            if ( false !== $start ) {
     4391                $end = strpos( $xmp_frame, '</x:xmpmeta>' );
     4392                if ( false !== $end ) {
     4393                    $xmp_frame = substr( $xmp_frame, $start, $end - $start + 12 );
     4394                    $results['mla_id3_xmp_metadata'] = MLAData::mla_parse_xml_string( $xmp_frame );
     4395                    if ( empty( $results['mla_id3_xmp_metadata'] ) ) {
     4396                        $results['mla_id3_xmp_errors'][] = "XMP metadata found but could not be parsed";
     4397                    }
     4398                } else {
     4399                    $results['mla_id3_xmp_errors'][] = "XMP metadata found but end tag not found";
     4400                }
     4401            } else {
     4402                $results['mla_id3_xmp_errors'][] = "XMP metadata frame found but start tag not found";
     4403            }
     4404        }
     4405
     4406        return $results;
    42854407    }
    42864408
     
    45184640            } // image/webp
    45194641
    4520             $results['mla_xmp_metadata'] = self::mla_parse_xmp_metadata( $path, 0 );
    4521             if ( NULL === $results['mla_xmp_metadata'] ) {
    4522                 $results['mla_xmp_metadata'] = array();
     4642            if ( false !== strpos( $filetype['type'], 'audio/mpeg' ) ) {
     4643                $id3_metadata = self::mla_fetch_mp3_xmp_metadata( $path );
     4644
     4645                if ( ! empty( $id3_metadata['mla_id3_xmp_metadata'] ) ) {
     4646                    $results['mla_xmp_metadata'] = $id3_metadata['mla_id3_xmp_metadata'];
     4647                   
     4648                    if ( ! empty( $id3_metadata['mla_id3_xmp_errors'] ) ) {
     4649                    $results['mla_xmp_errors'] = $id3_metadata['mla_id3_xmp_errors'];
     4650                    }
     4651                }
     4652            } // audio/mpeg
     4653
     4654            if ( empty( $results['mla_xmp_metadata'] ) ) {
     4655                $results['mla_xmp_metadata'] = self::mla_parse_xmp_metadata( $path, 0 );
     4656
     4657                if ( NULL === $results['mla_xmp_metadata'] ) {
     4658                    $results['mla_xmp_metadata'] = array();
     4659                }
    45234660            }
    45244661
  • media-library-assistant/trunk/index.php

    r3477233 r3479623  
    1616Plugin Name: Media Library Assistant
    1717Plugin URI: http://davidlingren.com/#two
    18 Description: 20260307 Enhances the Media Library; powerful [mla_gallery] [mla_tag_cloud] [mla_term_list], [mla_custom_list], [mla_archive_list], taxonomy support, IPTC/EXIF/XMP/PDF processing, bulk/quick edit.
     18Description: 20260310 Enhances the Media Library; powerful [mla_gallery] [mla_tag_cloud] [mla_term_list], [mla_custom_list], [mla_archive_list], taxonomy support, IPTC/EXIF/XMP/PDF processing, bulk/quick edit.
    1919Version: 3.34
    2020Requires at least: 4.7
  • media-library-assistant/trunk/readme.txt

    r3477233 r3479623  
    199199
    200200= 3.35 =
     201* Fix: For MP3 files, XMP metadata is now extracted from XML and PRIV frames, bypassing embedded image files.
    201202* Fix: For shortcodes that generate pagination links, a defect in handling URL fragments has been corrected.
    202203
Note: See TracChangeset for help on using the changeset viewer.