0

Can anyone tell me where I'm going wrong doing this.

The idea is to find and convert all files in a directory to m4v's. Regardless of the extension. Then once it has been converted, to generate a thumbnail from a supposedly 'relevant' part of the converted file. Then once it has done that to remove the original file (ideally if it is smaller than the original - however I haven't figured out this bit yet.) I am very new to shell scripts so any help would be most appreciated.

I'd then like for it in the future to add the file name and details to a MySQL database. If anyone has any tips on that I'd be very appreciative. Also how to add in a check to skip the conversion on files that are already m4v's.

#!/bin/bash
#Convert files using ffmpeg
OrDir="/Volumes/Misc/Test/"
NewDir="/Volumes/Misc/Conv2/"

find "$OrDir" -type f -exec /bin/bash -c \
    'f2=$(basename "$1"); \
     ffmpeg -i "$1" -c:v libx264 -crf 23 -preset ultrafast -c:a aac -strict experimental -b:a 192k -ac 2 "${NewDir}${f2%.*}.m4v" ' _ {} \;
     ffmpeg -ss 3 -i "${NewDir}${f2}.m4v" -vf "select=gt(scene\,0.4)" -frames:v 5 -vsync vfr fps=fps=1/600 "${NewDir}pictures/${f2%.*}.jpg"
     rm $1

UPDATE: I've been playing around with this a lot in the last few days and this is what I've got at the moment:

#!/bin/bash
#Convert files using ffmpeg

OrDir="/Volumes/Misc/Test"

find "$OrDir" -type f -exec /bin/bash -c \
    'ND="/Volumes/Misc/Convert/"
     f2=$(basename "$1")
     eval $(ffprobe -v error -of flat=s=_ -select_streams v:0 -show_entries stream=height,width "$1")
     size=${streams_stream_0_height}
     if [ "$size" -gt 720 ]
     then
         size=720
     fi
     ffmpeg -y -hide_banner -i "$1" -codec:v libx264 -profile:v high -preset ultrafast -b:v 500k -qmin 10 -qmax 42 -maxrate 1000k -bufsize 1000k -threads 0 -vf "scale=-1:$size" -pass 1 -c:a aac -strict -2 -b:a 128k -f m4v /dev$
     ffmpeg -y -hide_banner -i "$1" -codec:v libx264 -profile:v high -preset ultrafast -b:v 500k -qmin 10 -qmax 42 -maxrate 1000k -bufsize 1000k -threads 0 -vf "scale=-1:$size" -pass 2 -c:a aac -strict -2 -b:a 128k "$ND${f2%.*$
     ffmpeg -hide_banner -i "$ND${f2%.*}.m4v" -vf "select=gt(scene\,0.4), scale=-1:135, fps=1/60" -frames:v 5 -vsync vfr "${ND}pictures/${f2%.*}_%03d.png"
     rm $1
     mysql --host=****** --user=***** --password=****** db << EOF
     INSERT INTO tbl (Name, FileType, Time, AddedDate) VALUES ("${f2%.*}", '.m4v', 0, NOW());
     EOF
     ' _ {} \;

This has two issues; firstly It doesn't always generate the thumbnails, sometimes returning Output file is empty, nothing was encoded (check -ss / -t / -frames parameters if used) however the file that is created is converted perfectly and secondly the mysql line isn't executing correctly returning the error: _: line 12: mysql: command not found I also haven't figured out how to get the duration of the video yet to add that to the db as well.

Tom
  • 113

1 Answers1

0

If you are planning on expanding this script - which, from your question about a database, you are - I would recommend paramaterising as much as possible.

I would also recommend using ffmpegthumbnailer to generate the thumbnails, which should be a simple yum install ffmpegthumbnailer / apt-get install ffmpegthumbnailer away. Check the options- in my script I generate a thumbnail at the same size (-s0) as the original, but you may not want that.

This example script should get you started:

#!/bin/bash
# Convert files using ffmpeg
# USE AT YOUR OWN RISK!

# Parameters
OrDir="/tmp/video/"             # Original directory
NewDir="/tmp/video/out/"        # New directory
PicsDir=${NewDir}pictures/
ignore="m4v"                    # ignore single filetype being converted
convto="$ignore"                # in case you want to convert to something else
thumbnailseek=5                 # Seek % at which to generate thumbnail

# Change to original directory
# For each file:
#       check appropriate (exists, not directory, not already same type)
#       convert to $convto type media file (ffmpeg)
#       generate thumbnail (ffmpegthumbnailer)
#       (optional) remove original if new version smaller

cd "$OrDir"
for oldfile in *.mkv; do        # indeed every file; globbing will eat up  spaces properly
    ext=${i##*.}
    # ignore 1) non-existant files 2) directories 3) $ignore-extentioned files
    if [[ ! -e "$oldfile" ]] || [[ -d "$oldfile"  ]] || [[ "$ext" = "$ignore" ]]; then
            continue
    fi

    filename=${oldfile%.*}
    newfile="$NewDir$filename.$convto"
    oFS=$(stat --printf="%s" $oldfile) # old file size

    ffmpeg -y -loglevel quiet -i "$oldfile" -c:v libx264 -crf 23 -preset ultrafast -c:a aac -strict experimental -$
    nFS=$(stat --printf="%s" $newfile) # new file size

    ffmpegthumbnailer -s0 -i "$newfile" -t $thumbnailseek -o "$PicsDir$filename.jpg" &>/dev/null
    if [ "$oFS" -gt "$nFS" ]; then
            #rm -- "$OrDir$oldfile" # AT OWN RISK: uncomment to remove older file if smaller
    fi
done

I'm no expert, as any careful perusal of the above code will show. I've mixed in my own variable naming conventions with those in the original code (my intention was to maintain some familiarity); I could have parameterised further; I've encoded a fair number of assumptions into the script (like OrDir only containing video files, for example); some conditional testing could be simplified; filename and directory handling could be made uniform.

But this isn't Code Review. There shouldn't be any glaring errors, and it works locally on some mkvs I had kicking around just waiting for something like this.

If you intend to do much more with bash scripting, it is worth reading up on some common bash pitfalls, and if you have the time the Advanced Bash scripting guide.


For the 'SQL database', more info is needed to provide any kind of advice. What kind of database? SQLite? Why SQL at all, versus a simple flat file (CSV, etc)? What details?

bertieb
  • 7,543