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
  • 1
  • 8
  • How can `ffmpeg` know, what is _relevant_ for you? – Endoro Jul 08 '15 at 14:36
  • @Endoro - I'm not entirely sure what you mean. The main idea is to just convert it to m4v and try to make the file smaller. I've been playing around with it in the last few days and will update my OP with what I've come up with. This sets what I think is 'relevant' to me. But if I've mis understood please let me know – Tom Jul 08 '15 at 14:38
  • How can `ffmpeg` make a thumbnail from a _relevant part_ of the movie? What do you mean with _relevant_? – Endoro Jul 08 '15 at 14:42
  • @Endoro I see what you mean now. What I meant was something similar to this [Link](http://superuser.com/questions/538112/meaningful-thumbnails-for-a-video-using-ffmpeg) So that it captured thumbnails that were actually something instead of just black, or credits. – Tom Jul 08 '15 at 14:44
  • OK, nice, I will look into this. If you need _small_ files with _good quality_ you shouldn't use the `ultrafast` preset of x264. – Endoro Jul 08 '15 at 14:51

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,344
  • 36
  • 42
  • 54
  • @betrieb Thank you very much for this. It's been very helpful - I've updated my OP with what I've got now. I should've said that I've got a mySQL db running which I want to put the details into (The videos are then served to a web server with the pages being created off of the back of the details in the mySQL db) – Tom Jul 08 '15 at 14:41
  • @Tom no problem, I enjoy automating `ffmpeg`. I read your question again, as it now stands it's not clear which areas you are still having difficulty with- perhaps you could append this info? To get the data into MySQL you'll have a number of options - pipes, infiles etc - depending on how you want to go about it. – bertieb Jul 08 '15 at 14:49
  • I'm fairly new to shell scripting, and what I'd like to do is eventually having it run on my synology NAS, or Mac server (hence the automation) I'm needing help with the automation of it. At the moment I am getting the error on the third ffmpeg line, where it outputs 'Output file is empty' instead of generating the pictures. I'd also like to have the mysql details all in file, BUT I don't want them to then be readable. Can I change the permissions so it is still executable but not readable? – Tom Jul 08 '15 at 14:55
  • @Tom I would reiterate to make as much of it parametrisable as possible; (including things like ffmpeg codec options). If you are having an error with the third command I would note that in the OP - comments aren't intended for extended discussion. Binary files can be readable not executable; but not shell scripts [as per a recent unix.se question](http://unix.stackexchange.com/questions/214274/purpose-of-permissions-such-as-0111-or-0333) – bertieb Jul 08 '15 at 15:01
  • Sorry I'll update my OP what do you mean exactly by parameterisable? – Tom Jul 08 '15 at 15:02
  • @Tom by that I mean put things like `ffmpeg` config options into variables. It's not critical but it increases readability and keeps configuration to its own section. – bertieb Jul 08 '15 at 16:38