HandBrakeCLI bash script convert all videos in a folder - bash

Firstly, I searched around for my problem. But none can solve it.
I want to convert all videos file in a directory and the output will be saved in another directory. I got a bash script from somewhere I dont remember.
#!/bin/bash
SRC="/home/abc/public_html/filex/store/vids/toriko/VIDEOS HERE"
DEST="/home/abc/public_html/filex/store/vids/toriko/51-100"
DEST_EXT=mp4
HANDBRAKE_CLI=HandBrakeCLI
PRESET="iPhone & iPod Touch"
for FILE in "`ls $SRC`"
do
filename=$(basename $FILE)
extension=${filename##*.}
filename=${filename%.*}
$HANDBRAKE_CLI -i "$SRC"/$FILE -o "$DEST"/"$filename".$DEST_EXT "$PRESET"
done
the problem is, the output of the file will be without filename.. only ".mp4".
and, there is only 1 file generated.. means, from 50 videos in the folder, only 1 files generated with name ".mp4" and after that, HandBrakeCLI exit.
can anyone fix my code?
I got no experince in bash coding.. so, the right script giiven will be appreciate :)

Your line
for FILE in "`ls $SRC`"
effectively creates only one iteration where FILE contains the list of the files (and it is not able to handle the space in $SRC). Better replace it with
for FILE in "$SRC"/*
Example:
$ ls test
1.txt 2.txt
$ SRC=test; for f in "`ls $SRC`" ; do echo $f; done
1.txt 2.txt
$ SRC=test; for f in "$SRC"/* ; do echo $f; done
test/1.txt
test/2.txt
Side note: you can have a space in there with no problem
$ ls "the test"
1.txt 2.txt
$ SRC="the test"; for f in "$SRC"/* ; do echo $f; done
the test/1.txt
the test/2.txt

I tried this script, and others like it, but I wanted to convert recursive directory tree's and have files placed in the same directory with .mp4 extension and delete .avi files, after much trial and error I gave up on this code and searched for a new code, id like to credit
http://www.surlyjake.com/blog/2010/08/10/script-to-run-handbrake-recursively-through-a-folder-tree/
For the original code!
Here is my modified script, barely modified BTW this script is short, sweet and easy to understand.
#!/bin/bash
# This Script Goes in Root Folder of TV show -- Example Folder Structure
# /Stargate/Season\ 1/Epiosde.avi
# /Stargate/Season\ 2/Epiosde.avi
# /Stargate/handbrake_folder.script
# Outputs all Files back inside same dir's and does all folders inside Startgate DIR
# /Stargate/Season\ 1/Epiosde.mp4
# /Stargate/Season\ 2/Epiosde.mp4
# PRESET = -o flags for CLI can be got from GUI under Activity Log or from https://trac.handbrake.fr/wiki/CLIGuide OR you can use actual Presets!
# PRESET="iPhone & iPod Touch"
PRESET="--modulus 2 -e x264 -q 20 --vfr -a 1 -E ac3 -6 5point1 -R Auto -B 384 -D 0 --gain 0 --audio-fallback ac3 --encoder-preset=veryfast --encoder-level="5.2" --encoder-profile=high --verbose=1"
if [ -z "$1" ] ; then
TRANSCODEDIR="."
else
TRANSCODEDIR="$1"
fi
find "$TRANSCODEDIR"/* -type f -name "*.avi" -exec bash -c 'HandBrakeCLI -i "$1" -o "${1%\.*}".mp4 --preset="$PRESET"' __ {} \; && find . -name '*.avi' -exec rm -r {} \;
BE WARNED: THIS WILL CONVERT THEN DELETE ALL .AVI FILES ABOVE THE SCRIPT IN FILE TREE!
Feel free to remove the
[-name "*.avi"] & [&& find . -name '*.avi' -exec rm -r {} \;]
to disable only converting .avi and removal of .avi or modify to suite another extension.

I have found the solution:
#!/bin/bash
SRC="/home/abc/public_html/filex/store/vids/toriko/VIDEOS HERE"
DEST="/home/abc/public_html/filex/store/vids/toriko/51-100"
DEST_EXT=mp4
HANDBRAKE_CLI=HandBrakeCLI
for FILE in "$SRC"/*
do
filename=$(basename "$FILE")
extension=${filename##*.}
filename=${filename%.*}
$HANDBRAKE_CLI -i "$FILE" -o "$DEST"/"$filename".$DEST_EXT
done

I just tried using this script with the modification suggested above. I found I need to to put double quotes around the two uses of $FILE in order to handle file names with spaces.
So...
filename=$(basename "$FILE")
and
$HANDBRAKE_CLI -i "$SRC"/"$FILE" -o "$DEST"/"$filename".$DEST_EXT "$PRESET"

I'd rather prefer this solution:
#!/bin/bash
SRC="$1"
DEST="$2"
EXT='mp4'
PRESET='iPhone & iPod Touch'
#for FILE in "`ls $SRC`"; do
for FILE in `find . -type f`; do
FILE=$(basename "$FILE")
filename=$(basename "$FILE")
extension=${filename##*.}
filename=${filename%.*}
HandBrakeCLI -i "$SRC"/$FILE -o "$DEST"/"$filename"."$EXT" "$PRESET"
done

Related

Downloading only new subtitles in RaspberyPi

I have problems with writing downloading subtitles on Raspberry Pi with bash script.
The issue is I would like to have 1 script running with cron every hour or so that would download subtitles for new video files in some directory.
There is a program called qnapi that automatically downloads subtitles in the correct format in the directory but it works for 1 folder and always re-downloads subtitle files.
usage is:
qnapi /path/to/folder
So what I've found is
find /media/pi/RaspStorage -iname "*.mkv"
basically gives me a list of all movie files. Now I would like to somehow compare if a *.srt subtitle file with a same base name exists and if no then somehow loop through all found files with qnapi command. Write it in a bash script and cron it.
Perhaps something along the line of this would work (untested, may have typos).
I'm guessing at the syntax of qnapi from a google search.
Basically, symlink videos that don't have subtitles into a temporary folder, download the subtitles there, then move them back to the main folder.
#!/bin/bash
MEDIA=/media/pi/RaspStorage
WORK=/tmp/workingfolder
mkdir -p "$WORK"
pushd "$WORK" || { echo "ERROR: no working directory"; exit 1; }
# assuming subtitle name is video name except with ".srt" extension
find "$MEDIA" -iname "*.mkv" | while read video; do
vidnoext="${video%????}"
viddir="${vidnoext%/*}"
subtitle="${vidnoext}.srt"
if [ ! -f "$subtitle" ]; then
# this assumes bare filenames (without directory) are distinct
ln -s "$video" "${vidnoext}.mkv"
fi
done
qnapi -c .
find . -iname '*.srt' | while IFS= read -r subtitle; do
video="$(realpath "$(subtitle%????}.mkv")"
mv -i "$subtitle" "$(dirname "$video")/"
done
rm *
popd
rmdir "$WORK"
thanks for help :)
I got it working
#!/bin/bash
echo "Updated DLNA folder"
sudo minidlnad -R
for file in $(find /media/pi/RaspStorage -name '*.mkv' -or -name '*.avi'); do
name=${file:: -4}
ext=".srt"
name=$name$ext
if [ -f $name ]; then
echo "Napisy już ściągnięte dla:" "$(basename "$file")"
else
qnapi "$(dirname "$file")"/"$(basename "$file")"
fi
done
Works fine. All subtitles are downloading and if there are some already it's skipping

Shell Script: How to copy files with specific string from big corpus

I have a small bug and don't know how to solve it. I want to copy files from a big folder with many files, where the files contain a specific string. For this I use grep, ack or (in this example) ag. When I'm inside the folder it matches without problem, but when I want to do it with a loop over the files in the following script it doesn't loop over the matches. Here my script:
ag -l "${SEARCH_QUERY}" "${INPUT_DIR}" | while read -d $'\0' file; do
echo "$file"
cp "${file}" "${OUTPUT_DIR}/${file}"
done
SEARCH_QUERY holds the String I want to find inside the files, INPUT_DIR is the folder where the files are located, OUTPUT_DIR is the folder where the found files should be copied to. Is there something wrong with the while do?
EDIT:
Thanks for the suggestions! I took this one now, because it also looks for files in subfolders and saves a list with all the files.
ag -l "${SEARCH_QUERY}" "${INPUT_DIR}" > "output_list.txt"
while read file
do
echo "${file##*/}"
cp "${file}" "${OUTPUT_DIR}/${file##*/}"
done < "output_list.txt"
Better implement it like below with a find command:
find "${INPUT_DIR}" -name "*.*" | xargs grep -l "${SEARCH_QUERY}" > /tmp/file_list.txt
while read file
do
echo "$file"
cp "${file}" "${OUTPUT_DIR}/${file}"
done < /tmp/file_list.txt
rm /tmp/file_list.txt
or another option:
grep -l "${SEARCH_QUERY}" "${INPUT_DIR}/*.*" > /tmp/file_list.txt
while read file
do
echo "$file"
cp "${file}" "${OUTPUT_DIR}/${file}"
done < /tmp/file_list.txt
rm /tmp/file_list.txt
if you do not mind doing it in just one line, then
grep -lr 'ONE\|TWO\|THREE' | xargs -I xxx -P 0 cp xxx dist/
guide:
-l just print file name and nothing else
-r search recursively the CWD and all sub-directories
match these works alternatively: 'ONE' or 'TWO' or 'THREE'
| pipe the output of grep to xargs
-I xxx name of the files is saved in xxx it is just an alias
-P 0 run all the command (= cp) in parallel (= as fast as possible)
cp each file xxx to the dist directory
If i understand the behavior of ag correctly, then you have to
adjust the read delimiter to '\n' or
use ag -0 -l to force delimiting by '\0'
to solve the problem in your loop.
Alternatively, you can use the following script, that is based on find instead of ag.
while read file; do
echo "$file"
cp "$file" "$OUTPUT_DIR/$file"
done < <(find "$INPUT_DIR" -name "*$SEARCH_QUERY*" -print)

Doing something to all files in an entire tree

The scenario is that I want to convert all of my music files from .mp3 to .ogg. They are in a folder called "Music". In this folder there are folders and files. The files are .mp3s. The directories may contain .mp3s or directories which further contain .mp3s or directories, and so on. This is because some artists have albums which have parts and some do not, etc.
I want to write a script that converts each file using avconv.
Basically, what I am going to do is manually cd into every directory and run the following:
for file in $(ls); do avconv -i $file `echo \`basename $file .mp3\`.ogg`; done
This successfully gets me what I want. However, this is not great as I have a lot of folders, and manually going into each of them and executing this is slow.
My question, then, is how do I write a script that runs this in any directory that has .mp3s, and then goes into any subdirectory it finds and recursively calls itself? My intuition tells me to use Perl or Python because of the complex nature of this.
Thanks for any suggestions!
I'm not familiar with avconv but assuming your command is:
avconv -i inputname outputname
And you want to convert all inputname.mp3 to inputname.ogg in their original directories below Music, then the following should work in bash:
#!/bin/bash
while read -r fname; do
avconv -i "$fname" "${fname%.mp3}.ogg"
done < <(find /path/to/Music -type f -name "*.mp3")
Note: this does not remove the original .mp3, and the space between < < is required. Also note, for file in $(ls) is filled with potential for errors.
You can do it with bash in one liner:
First you find all files (of type file (-type f) ) that match next pattern "*.mp3". To read each one you use 'while' and invoke avconf.
For exchange extension I prefer 'sed' command, that keep folder so you don't need the 'cd' command.
Notice that you must put quotes on $FN variable because it can contain spaces.
find -type f -iname "*.mp3" | while read "FN" ; do avconf -i "$FN" $(echo "$FN" | sed 's/\.mp3/\.ogg/g') ; done
find <music-folder> -type f -name '*.mp3' | \
xargs -I{} bash -c 'mp3="$0"; ogg="${mp3%.mp3}.ogg"; avconv -i "$mp3" "$ogg";' {}
This should survive in cases of "weird" filenames with spaces, quotes and other strange symbols within.
You can list directories with absolute paths and recursively cd into every directory using find $PWD -type d syntax:
Just inside from Music directory run:
for d in $(find $PWD -type d)
do
cd $d
for file in $(find . -maxdepth 1 -type f)
do
echo $file
avconv -i $file `echo \`basename $file .mp3\`.ogg`
done
done

Batch converting videos in folders and sub folders

I have a huge collection of videos that all need to be converted into mp4. The folder structure is like this
Events
Chicago
Boston
San Fran
London Expo
Inside each event holds all of videos in either avi format or .mkv. I want them to be converted to the same file name. but with the mp4 extension.
My question is how do I loop through a folders sub folders, and also keep the file names because at the moment they have spaces in them.. Here is what I have at the moment.
sourcedir="$1"
destdir="$2"
cd "$sourcedir"
for i in `ls`; do
HandBrakeCLI -i "$i" -o "$destdir/${i%.*}.mp4" --preset="AppleTV"
echo $i
done
Phillips Code:
cd "$sourcedir"
echo "Directory: $sourcedir"
destdir = sourcedir
for subdir in *
do
if [[ -d "$subdir" ]]
then
for file in "$subdir"/*
do
HandBrakeCLI -i "$file" -o "${file%.*}.mp4" --preset=AppleTV
echo "$file"
done
fi
done
Use a nested loop, and don't use ls:
for subdir in "$sourcedir"/*
do
if [[ -d "$subdir" ]]
then
for file in "$subdir"/*
do
HandBrakeCLI -i "$file" -o "$destdir/${file%.*}.mp4" --preset=AppleTV
echo "$file"
done
fi
done
Another option is to use find:
find "$sourcedir" -maxdepth 2 -mindepth 2 -type f -exec bash -c 'HandBrakeCLI -i "$0" -o "'"$destdir"'/${0%.*}.mp4" --preset=AppleTV' '{}' ';' -print
Both solutions will work with filenames containing spaces or newlines.
Either use for i in "$sourcedir/*" (or since you've already done a cd there you could do for i in *).
or do find "$sourcedir" -type f | while read -r i (with this, the variable i will include the source directory name, so you'll have to strip that off using a brace expansion or basename).
change program to iVI link to iVi.
this program does the trcik, even better than handbrake...
bash 4
shopt -s globstar
destdir="/somewhere"
for file in **/*.mkv **/*.avi
do
HandBrakeCLI -i "$file" -o "$destdir/${file%.*}.mp4" --preset=AppleTV
done

Bash rename extension recursive

I know there are a lot of things like this around, but either they don't work recursively or they are huge.
This is what I got:
find . -name "*.so" -exec mv {} `echo {} | sed s/.so/.dylib/` \;
When I just run the find part it gives me a list of files. When I run the sed part it replaces any .so with .dylib. When I run them together they don't work.
I replaced mv with echo to see what happened:
./AI/Interfaces/C/0.1/libAIInterface.so ./AI/Interfaces/C/0.1/libAIInterface.so
Nothing is replaced at all!
What is wrong?
This will do everything correctly:
find -L . -type f -name "*.so" -print0 | while IFS= read -r -d '' FNAME; do
mv -- "$FNAME" "${FNAME%.so}.dylib"
done
By correctly, we mean:
1) It will rename just the file extension (due to use of ${FNAME%.so}.dylib). All the other solutions using ${X/.so/.dylib} are incorrect as they wrongly rename the first occurrence of .so in the filename (e.g. x.so.so is renamed to x.dylib.so, or worse, ./libraries/libTemp.so-1.9.3/libTemp.so is renamed to ./libraries/libTemp.dylib-1.9.3/libTemp.so - an error).
2) It will handle spaces and any other special characters in filenames (except double quotes).
3) It will not change directories or other special files.
4) It will follow symbolic links into subdirectories and links to target files and rename the target file, not the link itself (the default behaviour of find is to process the symbolic link itself, not the file pointed to by the link).
for X in `find . -name "*.so"`
do
mv $X ${X/.so/.dylib}
done
A bash script to rename file extensions generally
#/bin/bash
find -L . -type f -name '*.'$1 -print0 | while IFS= read -r -d '' file; do
echo "renaming $file to $(basename ${file%.$1}.$2)";
mv -- "$file" "${file%.$1}.$2";
done
Credits to aps2012.
Usage
Create a file e.g. called ext-rename (no extension, so you can run it like a command) in e.g. /usr/bin (make sure /usr/bin is added to your $PATH)
run ext-rename [ext1] [ext2] anywhere in terminal, where [ext1] is renaming from and [ext2] is renaming to. An example use would be: ext-rename so dylib, which will rename any file with extension .so to same name but with extension .dylib.
What is wrong is that
echo {} | sed s/.so/.dylib/
is only executed once, before the find is launched, sed is given {} on its input, which doesn't match /.so/ and is left unchanged, so your resulting command line is
find . -name "*.so" -exec mv {} {}
if you have Bash 4
#!/bin/bash
shopt -s globstar
shopt -s nullglob
for file in /path/**/*.so
do
echo mv "$file" "${file/%.so}.dylib"
done
He needs recursion:
#!/bin/bash
function walk_tree {
local directory="$1"
local i
for i in "$directory"/*;
do
if [ "$i" = . -o "$i" = .. ]; then
continue
elif [ -d "$i" ]; then
walk_tree "$i"
elif [ "${i##*.}" = "so" ]; then
echo mv $i ${i%.*}.dylib
else
continue
fi
done
}
walk_tree "."

Resources