Tuesday, February 02, 2010

Bash script for batch renaming of video files, based on the media information

We have a (quiet good) Panasonic miniDV camcorder to save our Little Boy's moments. Software comes with the device can auto index the tape by recoreded date attribute and then batch capture (copy) all the movie to hard disk. It's wonderful feature, except the filenames — we got something like this for every tape:

phan@phan-laptop:/mnt/TQA/Videos/Sushi-1Y/08.03.09-31.05.09$ ls
hsicon.stg     MOVIE0006.avi  MOVIE0012.avi  MOVIE0018.avi  MOVIE0024.avi  MOVIE0030.avi
MOVIE0001.avi  MOVIE0007.avi  MOVIE0013.avi  MOVIE0019.avi  MOVIE0025.avi  MOVIE0031.avi
MOVIE0002.avi  MOVIE0008.avi  MOVIE0014.avi  MOVIE0020.avi  MOVIE0026.avi  MOVIE0032.avi
MOVIE0003.avi  MOVIE0009.avi  MOVIE0015.avi  MOVIE0021.avi  MOVIE0027.avi  TAPE08032009_1902.tap
MOVIE0004.avi  MOVIE0010.avi  MOVIE0016.avi  MOVIE0022.avi  MOVIE0028.avi
MOVIE0005.avi  MOVIE0011.avi  MOVIE0017.avi  MOVIE0023.avi  MOVIE0029.avi


Naturally, it is wanted to rename the movies to show the recorded date and, maybe, some other userful information. Although I like Métamorphose and other batch renaming tools, they can't extract video data. So I decided to create my own bash script. Here is it:

#!/bin/bash
# Bash script for batch renaming of video files, recored by a miniDV-tape
# camcorder, based on the media information (Recorded Date in my case).
# Created by Phan Vinh Thinh, teppi {-} vnoss.org,
# released 01 Feb 2010 under GPL, so feel free to make changes.
# In the script used some echo commands for diagnostic purpose
###############################################################################

# If there is not any given argument (filename), print the help message
if [ $# -eq 0 ]; then
echo "vrename.sh --- Bash script for batch video files renaming";
echo "Usage: ./vrename.sh [files]";
echo "Examples:";
echo -e "\t./vrename.sh *.avi";
echo -e "\t./vrename.sh MOVIE0001.avi MOVIE0002.avi MOVIE0005.avi";
fi;

#for FILE in MOVIE*.avi; do
# $@ expands to all command-line parameters separated by spaces
for FILE in $@; do

# Get the Recorded Date in YYYYMMDDhhmm format and then
# assign to TIME variable
TIME=`mediainfo $FILE | grep -i "recorded date" | cut -d ':' --field=2-3 | \
tr -cd [:alnum:]`;
#echo $TIME;

# Change date format to which we want
    YEAR=${TIME%????????};
    MIN=${TIME#??????????};
    HOUR=${TIME#????????};
    HOUR=${HOUR%$MIN};
    DAY=${TIME#??????};
    DAY=${DAY%$HOUR$MIN};
    MON=${TIME%??????};
    MON=${MON#$YEAR};
#    echo $DAY-$MON-$YEAR-$HOUR-$MIN;

# Change month to short name format (Jan, Feb, etc)
    if [ "$MON" = "01" ] ; then
        Month=Jan;
    elif
    [ "$MON" = "02" ]; then
        Month=Feb;
    elif
    [ "$MON" == "03" ]; then
        Month=Mar;
    elif
    [ "$MON" = "04" ] ; then
        Month=Apr;
    elif
    [ "$MON" = "05" ] ; then
        Month=May;
    elif
    [ "$MON" = "06" ] ; then
        Month=Jun;
    elif
    [ "$MON" = "07" ] ; then
        Month=Jul;
    elif
    [ "$MON" = "08" ] ; then
        Month=Aug;
    elif
    [ "$MON" = "09" ] ; then
        Month=Sep;
    elif
    [ "$MON" = "10" ] ; then
        Month=Oct;
    elif
    [ "$MON" = "11" ] ; then
        Month=Nov;
    elif
    [ "$MON" = "12" ] ; then
        Month=Dec;
    else
        echo "Try without any argument to see help!"
        exit 1;
    fi;
#    echo $Month;

# Assign NAME variable --- basename for future filename
    NAME=Movie-$DAY$Month$YEAR-$HOUR$MIN;
#    echo $NAME;

# Rename FILE to NAME.avi
# Use -i option to make sure there are not 2 files with same NAME
    mv -i -v $FILE $NAME.avi;
done


(To obtain media information, we need an external program — mediainfo).
You can download the script in gzip format from my Google Docs share.

Running script, we got:

phan@phan-laptop:/mnt/TQA/Videos/Sushi-1Y/08.03.09-31.05.09$ ~/vrename.sh *.avi
`MOVIE0001.avi' -> `Movie-08Mar2009-1902.avi'

`MOVIE0002.avi' -> `Movie-08Mar2009-1904.avi'
`MOVIE0003.avi' -> `Movie-08Mar2009-1905.avi'
`MOVIE0004.avi' -> `Movie-08Mar2009-1906.avi'
`MOVIE0005.avi' -> `Movie-08Mar2009-1907.avi'
`MOVIE0006.avi' -> `Movie-16Mar2009-1244.avi'
`MOVIE0007.avi' -> `Movie-16Mar2009-1252.avi'
`MOVIE0008.avi' -> `Movie-16Mar2009-1339.avi'
...

P.S. It's time to start Perl learning ;).

4 comments:

pclouds said...

Ờ.. tới giới hạn của bash rồi. Cái if/else cho tháng có thể dùng case/esac cho gọn. Muốn gọn hơn nữa thì đút thông tin cho "date --date=..." rồi nhờ "date" định dạng output dùm.

Với mấy cái script phức tạp như vầy thì nên thêm tuỳ chọn --dry-run để chỉ in những lệnh sẽ làm chớ không làm, để kiểm tra trước.

Phan Vinh Thinh said...

Bạn đồng ý dùng case/esac thay vì if/else (thanks), nhưng không thích dùng date vì không linh hoạt bằng dùng các biến.

Bạn đang nghiên cứu để thêm cái --dry-run vì thấy nó thật có ích. Tuy nhiên, thêm tùy chọn sẽ làm cái script phức tạp. Tạm thời nó phục vụ ngon lành cho nhu cầu của bạn.

Anonymous said...

* mấy ngày tháng đó thì dùng mảng luôn, khỏi if-then-case cho mệt
* dry-run thì chỉ việc thêm cái option trước khi mv là xong

if [ "x$1" == "x-dry-run"]; then
dry_run="echo"
else
dry_run=“”
fi

rồi dùng với mv:

$dry_run mv xxx yyy

Phan Vinh Thinh said...

-dry-run làm như bạn nói cũng OK. Tuy nhiên, vì tôi dùng $@ nên option này sẽ rơi vào vòng lặp như các tập tin *.avi. Mặc dù không có gì đáng sợ, nhưng muốn theo đúng chuẩn bash scripting thì có lẽ phải động đến getopt hoặc getopts.