How to massconvert filenames from PREFIX_00000000.png to PREFIX_HH:mm:ss.ffff.png

102 Views Asked by At

I've a huge number of png files, which are named the following way: PREFIX_00000000.png.

These files come from extracting frames from video files using ffmpeg -skip_frame nokey -i ORIGINALVIDEO.MP4 -vsync vfr -frame_pts true -r 1000 FRAMES_%d.png which outputs files like:

FRAMES_15416721.png
FRAMES_26708343.png
FRAMES_38000000.png  
FRAMES_49291677.png
FRAMES_60583335.png
FRAMES_71875085.png

(I came up with this by this answer. )

PREFIX_ is a fixed string and is followed by eight digits. Till now the video files are 24h max – which brings me to, 86400000ms (8 digits). Maybe the video files will longer – thus I'm looking for a solution which could be changed to any number of digits. These numbers represent milliseconds (Since the beginning of the video file / representing the time into the video).

Now i'm searching for a way to convert the filename from the given format to the format PREFIX_HH:mm:ss.fff.png. (String, followed by Hour:Minute:Second.Millisecond). So a file named FRAMES_49291677.png would become FRAMES_13:41:31.677.png.

Sample values would be the following. (I added examples for >24h as well.)
Input:

PREFIX_1.png
PREFIX_000000001.png
PREFIX_1012.png
PREFIX_00001012.png
PREFIX_12106.png
PREFIX_000012106.png
PREFIX_725463.png
PREFIX_00725463.png
PREFIX_1204242.png
PREFIX_001204242.png
PREFIX_9864336.png
PREFIX_09864336.png
PREFIX_36012486.png
PREFIX_036012486.png
PREFIX_49291677.png
PREFIX_049291677.png
PREFIX_86400000.png
PREFIX_086400000.png
PREFIX_113744568.png
PREFIX_261736874.png

Output:

PREFIX_00:00:00.001.png
PREFIX_00:00:00.001.png
PREFIX_00:00:01.012.png
PREFIX_00:00:01.012.png
PREFIX_00:00:12.106.png
PREFIX_00:00:12.106.png
PREFIX_00:02:05.463.png
PREFIX_00:02:05.463.png
PREFIX_00:20:04.242.png
PREFIX_00:20:04.242.png
PREFIX_02:44:30.336.png
PREFIX_02:44:30.336.png
PREFIX_10:00:12.486.png
PREFIX_10:00:12.486.png
PREFIX_13:41:31.677.png
PREFIX_13:41:31.677.png
PREFIX_24:00:00.000.png
PREFIX_24:00:00.000.png
PREFIX_31:25:44.568.png
PREFIX_72:22:16.874.png

The turnside? This should be done via bash (Ubuntu).

Did anyone of you do sth like this? Or does have a solution?

2

There are 2 best solutions below

2
Ed Morton On BEST ANSWER

Using any awk:

$ cat tst.sh
#!/usr/bin/env bash

shopt -s extglob

while read -r old new; do
    echo mv -- "$old" "$new"
done < <(
    printf '%s\n' PREFIX_+([[:digit:]]).png |
    awk -F '[_.]' '{
        hrs  = int( ($2 / (1000 * 60 * 60)) )
        mins = int( ($2 / (1000 * 60)) % 60 )
        secs = int( ($2 / 1000) % 60 )
        ms   = $2 % 1000
        printf "%s %s_%02d:%02d:%02d.%03d.%s\n", $0, $1, hrs, mins, secs, ms, $3
    }'
)

$ ./tst.sh
mv -- PREFIX_10799999.png PREFIX_02:59:59.999.png
mv -- PREFIX_12345678.png PREFIX_03:25:45.678.png
mv -- PREFIX_87654321.png PREFIX_24:20:54.321.png

Remove the echo once you're happy with the results.

Check the math and tweak if necessary as I mostly just copied it from From milliseconds to hour, minutes, seconds and milliseconds.


Modified to read input from a file and produce the expected output in the question for testing purposes:

$ cat tst.sh
#!/usr/bin/env bash

shopt -s extglob

while read -r old new; do
    #echo mv -- "$old" "$new"
    echo "$new"
done < <(
    # printf '%s\n' PREFIX_+([[:digit:]]).png |
    cat file |
    awk -F '[_.]' '{
        hrs  = int( ($2 / (1000 * 60 * 60)) )
        mins = int( ($2 / (1000 * 60)) % 60 )
        secs = int( ($2 / 1000) % 60 )
        ms   = $2 % 1000
        printf "%s %s_%02d:%02d:%02d.%03d.%s\n", $0, $1, hrs, mins, secs, ms, $3
    }'
)

$ ./tst.sh
PREFIX_00:00:00.001.png
PREFIX_00:00:00.001.png
PREFIX_00:00:01.012.png
PREFIX_00:00:01.012.png
PREFIX_00:00:12.106.png
PREFIX_00:00:12.106.png
PREFIX_00:12:05.463.png
PREFIX_00:12:05.463.png
PREFIX_00:20:04.242.png
PREFIX_00:20:04.242.png
PREFIX_02:44:24.336.png
PREFIX_02:44:24.336.png
PREFIX_10:00:12.486.png
PREFIX_10:00:12.486.png
PREFIX_13:41:31.677.png
PREFIX_13:41:31.677.png
PREFIX_24:00:00.000.png
PREFIX_24:00:00.000.png
PREFIX_31:35:44.568.png
PREFIX_72:42:16.874.png
2
jhnc On

A pure bash implementation:

ms2hms()(
    for i; do

        if ! [[ $i =~ ^(.+_)([0-9]+)(\.png)$ ]]; then
            echo 1>&2 bad filename: "$i"
            continue
        fi

        pre=${BASH_REMATCH[1]}
        sfx=${BASH_REMATCH[3]}

        t=10#${BASH_REMATCH[2]}
        ((
            f = t%1000,

            t = t/1000,
            s = t%60,

            t = t/60,
            m = t%60,

            h = t/60
        ))

        printf -v o '%s%02d:%02d:%02d.%03d%s' "$pre" $h $m $s $f "$sfx"

        echo mv -i "$i" "$o"

    done
)

You used "milliseconds" but gave 4 digits after the decimal. If you really intended 4 digits, replace {3} with {4} and %03d with %04d (or append a fixed 0 to the printf format string if input actually is milliseconds but you still want 4 digits).