Fun tasks

So the conundrum of the moment is: how to iteratively export pictures added to a FLAC[O en.WP] file?

The answer is to use metaflac[O], a command-line tool which is part of the FLAC project. The problem is how best to snabble the block numbers of the images, and then export the images. I am going to use php to script this, so here is how I can do it.

? metaflac --list --block-type=PICTURE /path/to/file.flac | exportPipe.php

Metaflac opens the file and spits out a list of all metadata blocks of type #6 (PICTURE). The exportPipe.php combs through the metaflac output, extracting the block number(s) and the type of image(s) into an array, if any. If there are images, it creates a temporary folder and exports each image titled by its type using metaflac. It then passes the name of the temporary folder as it ends execution (allowing the output to be further piped.)

 

FLAC files may have an effectively infinite number of images embedded in them. This is the only reason I wrote this little pipe script – to iterate over the possible files if there is more than one. And even this is not the real tool I am working on – I am really working on writing a script to test the integrity of the files in my collection, to ensure they are maximally compressed, and to normalize the naming conventions and directory structures.

But all that is relatively beside the point; here’s the basic script without the nonsense:

#!/usr/bin/php
<?php
 
/*
 * exportPipe.php
 * 
 * Copyright 2017 W Saewyc <wsaewyc © gmail.com>
 * 
 * This program is free software. It comes without any warranty, to
 * the extent permitted by applicable law. You can redistribute it
 * and/or modify it under the terms of the Do What The Fuck You Want
 * To Public License, Version 2, as published by Sam Hocevar. See
 * http://www.wtfpl.net/ for more details.
 * 
 */
 
$input_stream = fopen( "php://stdin", "r" );
$picBlocks = array();
 
$block = false;
$line = fgets( $input_stream, 4096);
do {
    if( $block &amp;&amp; ( substr( $line, 0, 4 ) != "    " ) ) {
        switch ($line) {
            case ( preg_match( '/type:/', $line ) ):
                if( !preg_match( '/PICTURE/', $line ) ){
                    $start = strpos( $line, ":" ) + 1;
                    $length = strlen( $line ) - strpos( $line, "(" );
                    $picBlocks[$block]['image_type'] = (int) substr( $line, $start, $length );
                }
                break;
            case ( preg_match( '/MIME type/', $line ) ):
                $pos = strpos( $line, ':' );
                if( $pos !== false ){
                    $picBlocks[$block]['mime'] = substr( $line, $pos + 2 );
                }
                break;
            case ( preg_match( '/data:/', $line ) ):
                // This assumes the picture data is always exported last.
                $block = false;
                break;
            default:
                // There are several other values which may be necessary later.
                break;
        }
    }elseif( 'METADATA block #' == substr( $line, 0, 16) ) {
        $block = (int) substr( $line, 16 );
        $picBlocks[$block] = array();
    }
 
} while( $line = fgets( $input_stream, 4096) );
 
fclose( $input_stream );
 
// If no images, our work is done.
if( count( $picBlocks ) < 1 ) {
    exit();
}

NB: this has been c/p out of something which sort of works, but not tested so it may not work as writ.

Obviously this does not yet do anything, but it has identified the block numbers, and captured image type and the mime type. The image type describes what the subject of the image is:

$imageType = array(
        0 => 'other',
        1 => 'icon-png',
        2 => 'icon-other',
        3 => 'cover',
        4 => 'cover-back',
        5 => 'leaflet',
        6 => 'media',
        7 => 'lead',
        8 => 'artist',
        9 => 'conductor',
        10 => 'group',
        11 => 'composer',
        12 => 'lyricist',
        13 => 'location',
        14 => 'recording',
        15 => 'performing',
        16 => 'screencap',
        17 => 'fish',
        18 => 'illustration',
        19 => 'performer-logo',
        20 => 'publisher-logo',
    );

There are rather a large range of mime types for images, but a much smaller set which are likely to be found in a music file. The original file names of images are not actually stored in the FLAC standard, so combining image type, an iterative counter, and the appropriate extension will create unique file names based on the ascendant block id number (e.g. cover.0.jpeg for METADATA block #0, image type 3, mime type image/jpeg.) This list is not exhaustive, but will likely serve for 99.9% of usual cases:

$extensions = array(
    'image/bmp' => '.bmp',
    'image/gif' => '.gif',
    'image/jpeg' => '.jpeg',
    'image/x-portable-bitmap' => '.pbm',
    'image/x-pict' => '.pic',
    'image/png' => '.png',
    'image/svg+xml' => '.svg',
    'image/tiff' => '.tif',
    'image/x-xbitmap' => '.xbm',
);