#!/bin/bash
#################################################
# Script zum Schneiden von OTR-Aufnahmen
# (c) 2006, Hagen Meyer -- hagenmeyer@web.de
#
# benutzung auf eigene gefahr...
#################################################
#######################
# CONFIG
#######################
# VORSICHT: tempdir wird komplett geleert!! Vorgabewert is empfehlenswert
tempdir="/tmp/multicut/"
# lokale cutlists (aktueller ordner) verwenden? name ist egal, wird nach inhalt ausgewaehlt
# KEIN vorrang vor heruntergeladenen cutlists bei aktiviertem download und automatischem modus
# 0: nein, 1: ja
local=1
# cutlists downloaden?
# versucht alle verfuegbaren cutlists auf cutlist.de zu finden
# 0: nein, 1: ja
download=1
# automatischer modus
# versucht, selbst die beste cutlist zu finden (lokal und internet, je nach einstellung), und verwendet diese.
# im interaktiven modus (automode=0) werden cutlists auch nach qualitaet sortiert, aber die auswahl erfolgt manuell
automode=0
# Mindest-bewertung (vom Autor) fuer cutlists (schlechter wird ignoriert).
#
# Werte:
# 5: framegenau und keine doppelten Szenen
# 4: framegenau
# 3: +/- 1 sek
# < 3: schlechter (wen interessierts? ;) )
#
# Empfehlung: 4. Wer will schon ungenaue schnitte??
# (wer mit avisplit schneidet kann auch schlechtere cutlists verwenden, das avisplit eh nur an keyframes schneidet)
min_rating=4
# mit welchem programm soll geschnitten werden?
# avidemux
# + kann framegenau schneiden
# - laeuft mit grafischer oberflaeche, kann daher nicht vollstaendig automatisiert werden
# (die fragen am anfang koennen negativ beantwortet werden - die schritte werden automatisch durchgefuehrt)
# - laeuft nicht aus cronjobs (kann evtl mit "xhost +local:" erlaubt werden - untested)
#
# avisplit (Standard, falls fehlerhafte angabe)
# - kann nur an keyframes schneiden -> schnitte sind nicht so genau, einige frames/sekunden werbung ist noch drin
# + laeuft vollstaendig auf console, daher auch gut in cronjobs einsetzbar
cutwith="avidemux"
# sollen die schnitte anschliessend mit mplayer ueberprueft werden?
# check=1 oder 0
# mplayer wird nacheinander ca. 10 sekunden vor dem schnitt gestartet. fuer den naechsten schnitt mplayer einfach beenden (mit q)
# anfang und ende werden auch gezeigt
check=1
# Farben (nur editieren, wenn man weiss was man tut ;) )
# auskommentieren wenn man keine farben moechte
c_filename="\033[00;34m"
c_author="\033[01;37;40m"
c_rating="\033[01;33;41m"
c_cuts="\033[01;33;41m"
c_selection="\033[01;37;40m"
c_error="\033[01;31;40m"
c_end="\033[00m"
#######################
# FUNCTIONS
#######################
# Laed ALLE verfuegbaren cutlists fuer diese datei herunter, die beste wird spaeter gesucht...
##############################################################################################
function dlCutlists () { # filename
cd $tempdir
filename=${1##*/}
### alter download, ohne suche
# for filename in $filename ${filename%.avi} ${filename%.mpg.avi} ; do
# i=0
# wget -q "http://cutlist.de/upload/$filename.cutlist"
# while [ $? -eq 0 ]; do
# let i++
# wget -q "http://cutlist.de/upload/$filename.$i.cutlist"
# done
# done
# echo "fertig."
echo -n "Lade cutlists fuer $filename herunter..."
# suche bei cutlist.de
search=${filename%.avi}
search=${search%.mpg}
content=$( wget -q -O - --post-data="seek_in=$search&submit=OK" "http://cutlist.de/index.php?cat=seek" )
urls=$( echo $content | egrep -o "getfile\.php\?id=[[:digit:]]+" )
# user-wertung
oldifs=$IFS
IFS="
"
userrating=( $( echo $content | egrep -o "[^\"]*User haben bewertet" ) )
IFS=$oldifs
i=0
for url in $urls ; do
# finde den cutlist-dateinamen heraus (wget kann bisher nur in der Beta den filename aus dem server-header verwenden, workaround fuer aeltere versionen)
cutlist=$( wget -S --spider "http://cutlist.de/$url" 2>&1 | egrep -o "filename=[^;]+" )
cutlist=${cutlist##filename=}
# lade die cutlist herunter
wget -q -O "$cutlist" "http://cutlist.de/$url"
rating=${userrating[$i]%%/*}
users=${userrating[$i]% User haben bewertet}
users=${users##**[ ]}
if [ $users == "0" ] ; then
echo -n "0|0" > ${cutlist}.rating
else
echo -n "$rating|$users" > ${cutlist}.rating
fi
let i++
done
echo "fertig. $i gefunden."
}
# sucht die beste cutlist fuer eine datei aus dem ordner $tempdir
#############################################################
function findBestCutlist () { # filename
echo "Filtere Cutlists nach gegebenen Kriterien..."
cd $tempdir
unset cutlist
filename=${1##*/}
i=1
# Lade die wichtigen daten der passenden cutlists in arrays (cutlist, rating, cuts)
for cl in `find . -exec grep -q "ApplyToFile=$filename" {} \; -print`; do
# FIELD: RatingByAuthor
tmp=`grep RatingByAuthor $cl`
tmp=${tmp%[[:cntrl:]]} # letztes zeichen loeschen (\r)
rating[$i]=${tmp#RatingByAuthor=}
[ ${rating[$i]} -lt $min_rating ] && continue # naechste cutlist, wenn rating zu gering
# "FIELD": Cutlist-Format
if grep -q "StartFrame=" $cl ; then
vcf2format[$i]=1
elif grep -q "Start=" $cl ; then
vcf2format[$i]=0
else
echo "ERROR: unbekanntes Cutlist-Format: ${cl##*/}" >&2
continue # cutlist ignorieren, unbekanntes format...
fi
# FIELD: Author
tmp=`grep "^Author" $cl`
tmp=${tmp%[[:cntrl:]]}
author[$i]=${tmp#Author=}
author[$i]=${author[$i]:-Unbekannt}
# FIELD: NoOfCuts
tmp=`grep NoOfCuts $cl`
tmp=${tmp%[[:cntrl:]]} # letztes zeichen loeschen (\r)
cuts[$i]=${tmp#NoOfCuts=}
# FIELD: Comment
tmp=`grep UserComment $cl`
tmp=${tmp%[[:cntrl:]]} # letztes zeichen loeschen (\r)
comment[$i]=${tmp#UserComment=}
# verschiedene fehler...
unset error content errordesc
if grep -q "EPGError=1" $cl ; then
tmp=`grep ActualContent $cl`
tmp=${tmp%[[:cntrl:]]} # letztes zeichen loeschen (\r)
content[$i]="Tatsaechlicher Inhalt: ${tmp#ActualContent=}"
error[$i]=" EPG-Fehler! "
fi
if grep -q "MissingBeginning=1" $cl ; then
error[$i]="${error[$i]} Anfang fehlt! "
fi
if grep -q "MissingEnding=1" $cl ; then
error[$i]="${error[$i]} Ende fehlt! "
fi
if grep -q "MissingAudio=1" $cl ; then
error[$i]="${error[$i]} Ton fehlt! "
fi
if grep -q "MissingVideo=1" $cl ; then
error[$i]="${error[$i]} Bild fehlt! "
fi
if grep -q "OtherError=1" $cl ; then
tmp=`grep OtherErrorDescription $cl`
tmp=${tmp%[[:cntrl:]]} # letztes zeichen loeschen (\r)
errordesc[$i]="Fehlerbeschreibung: ${tmp#OtherErrorDescription=}"
error[$i]="${error[$i]} Sonstiger Fehler! "
fi
# Nicht vergessen: dateiname ;)
cutlist[$i]=${cl##*/}
# userrating
if [ -f "${cutlist[$i]}.rating" ] ; then
userrating[$i]=$( cat "${cutlist[$i]}.rating" )
else
unset userrating[$i]
fi
# erst hier increment, vorher kann das speichern dieser cutlist abgebrochen werden
let i++
done
if [ ${#cutlist[@]} -eq 0 ] ; then
echo -e "Keine passende Cutlist für $c_filename$filename$c_end gefunden. Abbruch."
return 0
fi
sortCutlists # gesammelte cutlists nach "qualitaet" sortieren
[ $automode -ne 0 ] && return 1 # interaktive auswahl nicht noetig, ersten eintrag der arrays verwenden
# interaktive auswahl...
clear
echo -e "\nFuer die Datei $c_filename$filename$c_end wurden folgende cutlists gefunden:\n\n"
i=1
while [ $i -le ${#cutlist[@]} ] ; do
format_string=$( [ ${vcf2format[$i]} -eq 1 ] && echo "VCF2Cutlist-Standard" || echo "Assistant-Standard" )
printf "$c_selection[%d]$c_end\t%s von $c_author %s $c_end\n\tAutor-Wertung: $c_rating %d $c_end\t\tUser-Wertung: $c_rating %s $c_end (%d Stimmen)\n\tFormat: %s\tAnzahl Schnitte: $c_cuts %d $c_end\n\tKommentar: %s\n" $i "${cutlist[$i]}" "${author[$i]}" "${rating[$i]}" "${userrating[$i]%|*}" "${userrating[$i]#*|}" "$format_string" "${cuts[$i]}" "${comment[$i]}"
if [ -n "${error[$i]}" ] ; then
echo -e "\t$c_error${error[$i]}$c_end"
fi
if [ -n "${content[$i]}" ] ; then
echo -e "\t${content[$i]}"
fi
if [ -n "${errordesc[$i]}" ] ; then
echo -e "\t${errordesc[$i]}"
fi
echo
let i++
done
printf "$c_selection[0]$c_end\tKeine Auswahl. Diesen Film nicht schneiden.\n\n\n"
read -p "Auswahl: " ret
# hier koennte noch ein plausi-check erfolgen...
return $ret
}
# sortiere cutlists nach "qualitaet" (absteigend): RatingByAuthor->User-Rating->VCF2Format?->NoOfCuts->ActualContent
##########################################################################################
# Erklaerung:
# RatingByAuthor gibt die beste Auskunft der Schnittqualitaet
# VCF2Format schneidet nach Frames, nicht nach zeit ==> duerfte meistens besser sein
# Mehr Cuts duerfte meistens besser sein, (ueberschuessiges Ende weg, keine Werbung uebersehen, etc.)
# ActualContent: wenn leer, dann besser (meistens gesetzt, wenn es nicht um den eigentlich aufgenommenen film geht,
# sondern um das ende des vorhergehenden o.ae.)
# !! vollautomatisch kann NIE die absolut beste cutlist gefunden werden, es ist nur der versuch eine moeglichst gute zu finden...
##################################################################################################################################
function sortCutlists () { # greift auf die arrays zu, die in "findBestCutlist" erstellt werden
local changed=1
while [ $changed -eq 1 ] ; do
changed=0
i=2
while [ $i -le ${#cutlist[@]} ] ; do
if [ ${rating[$i-1]} -lt ${rating[$i]} ] ; then
swap $i
changed=1
elif [ ${rating[$i-1]} -eq ${rating[$i]} ] && [ "${userrating[$i-1]%|*}" \< "${userrating[$i]%|*}" ] ; then
swap $i
changed=1
elif [ ${rating[$i-1]} -eq ${rating[$i]} ] && [ "${userrating[$i-1]%|*}" = "${userrating[$i]%|*}" ] && [ ${vcf2format[$i-1]} -lt ${vcf2format[$i]} ] ; then
swap $i
changed=1
elif [ ${rating[$i-1]} -eq ${rating[$i]} ] && [ "${userrating[$i-1]%|*}" = "${userrating[$i]%|*}" ] && [ ${vcf2format[$i-1]} -eq ${vcf2format[$i]} ] && [ ${cuts[$i-1]} -lt ${cuts[$i]} ] ; then
swap $i
changed=1
elif [ ${rating[$i-1]} -eq ${rating[$i]} ] && [ "${userrating[$i-1]%|*}" = "${userrating[$i]%|*}" ] && [ ${vcf2format[$i-1]} -eq ${vcf2format[$i]} ] && [ ${cuts[$i-1]} -eq ${cuts[$i]} ] && [ -z "${content[$i]}" ] && [ -n "${content[$i-1]}" ]; then
swap $i
changed=1
fi
let i++
done
done
}
function swap () { # parameter: i: elemente i-1 und i werden (von allen arrays) vertauscht
local tmp=${cutlist[$1-1]}
cutlist[$1-1]=${cutlist[$1]}
cutlist[$1]=$tmp
tmp=${rating[$1-1]}
rating[$1-1]=${rating[$1]}
rating[$1]=$tmp
tmp=${userrating[$1-1]}
userrating[$1-1]=${userrating[$1]}
userrating[$1]=$tmp
tmp=${vcf2format[$i-1]}
vcf2format[$i-1]=${vcf2format[$i]}
vcf2format[$i]=$tmp
tmp=${cuts[$1-1]}
cuts[$1-1]=${cuts[$1]}
cuts[$1]=$tmp
tmp=${comment[$1-1]}
comment[$1-1]=${comment[$1]}
comment[$1]=$tmp
tmp=${content[$1-1]}
content[$1-1]=${content[$1]}
content[$1]=$tmp
tmp=${author[$1-1]}
author[$1-1]=${author[$1]}
author[$1]=$tmp
tmp=${error[$1-1]}
error[$1-1]=${error[$1]}
error[$1]=$tmp
tmp=${errordesc[$1-1]}
errordesc[$1-1]=${errordesc[$1]}
errordesc[$1]=$tmp
}
# schneide datei mit gegebener cutlist
########################################################################
function cutfilm () { # film, cutlist, vcf2format?
film=$1
cutlist=$2
vcf2format=$3
cuts=""
unset checktimes
checktimes[0]=0
checkcnt=0
cd $tempdir
[ $cutwith == "avidemux" ] && project_start $film
if [ $vcf2format -eq 1 ] ; then
lines=$( egrep "(StartFrame|DurationFrames)" $cutlist | tr -d "\r" )
else
lines=$( egrep "(Start|Duration)" $cutlist | tr -d "\r" )
fi
for line in $lines ; do
if echo $line | grep -q "Start" ; then ######### startcut ##########
startcut=${line##*=}
if [ $vcf2format -eq 1 ] ; then
if [ $cutwith == "avidemux" ] ; then
echo -n "app.addSegment(0,$startcut," >> project.js
else
cuts=$cuts$startcut-
fi
else
secs=${startcut%%.*}
decimalsecs=${startcut##*.}
decimalsecs=${decimalsecs::2}
decimalsecs=${decimalsecs#0}
if [ $cutwith == "avidemux" ] ; then
frames=$(( $secs*25 + ${decimalsecs:-0} / 4 ))
echo -n "app.addSegment(0,$frames," >> project.js
else
time=$( date -u -d @$secs +%T ) # wandelt sekunden.dezimalen in hh:mm:ss um...
frames=$(( ${decimalsecs:-0} / 4 )) # frames ausrechnen runden ist egal
cuts=$cuts$time.$frames- # hh:mm:ss.ms (ms ist frames, nicht millisekunden!!)
fi
fi
else ######### endcut ############
if [ $vcf2format -eq 1 ] ; then
if [ $cutwith == "avidemux" ] ; then
echo "${line##*=})" >> project.js
else
let endcut=$startcut+${line##*=}
cuts=$cuts$endcut
fi
let checkcnt++
let checktimes[$checkcnt]=${checktimes[$checkcnt-1]}+${line##*=}/25
else
length=${line##*=}
if [ $cutwith == "avidemux" ] ; then
decimalsecs=${length##*.}
decimalsecs=${decimalsecs::2}
decimalsecs=${decimalsecs#0}
frames=$(( ${length%%.*}*25 + ${decimalsecs:-0}/4 ))
echo "$frames)" >> project.js
else
add_decimal $startcut ${line##*=}
time=$( date -u -d @$solution +%T )
decimalsecs=${solution##*.}
decimalsecs=${decimalsecs::2}
decimalsecs=${decimalsecs#2}
frames=$(( ${decimalsecs:-0} / 4 ))
cuts=$cuts$time.$frames,
fi
let checkcnt++
let checktimes[$checkcnt]=${checktimes[$checkcnt-1]}+${length%%.*}
fi
fi
done
name=${film##*/}
outname="$cutdir/${name%.mpg.avi}-cut.avi"
if [ $cutwith == "avidemux" ] ; then
project_end "$outname"
echo "Starte avidemux2 mit den errechneten Schnitten..."
avidemux2 --run project.js
if [ $? -eq 0 ] ; then
echo "$name erfolgreich geschnitten!"
mv $film $uncutdir/$name
else
echo "Avidemux2 hat einen Fehler gemeldet! $name moeglicherweise nicht geschnitten!"
return
fi
else
cuts=${cuts%,} # letztes komma abtrennen...
echo "Uebergebe die errechneten Cuts an avisplit..."
echo avisplit -i $film -o "$outname" -t $cuts -c
# Auftrag an avisplit uebergeben... (mergen wird hier durch -c auch erreicht)
avisplit -i $film -o "$outname" -t $cuts -c
if [ $? -eq 0 ] ; then
echo "$name erfolgreich geschnitten!"
mv $film $uncutdir/$name
else
echo "Avisplit hat einen Fehler gemeldet! $name moeglicherweise nicht geschnitten!"
return
fi
fi
## schnittstellen ueberpruefen
echo "Schnitte bei folgenden Zeiten:"
i=0
while [ $i -lt ${#checktimes[@]} ] ; do
date -u -d @${checktimes[$i]} +%T
[ $check -eq 1 ] && type mplayer && mplayer -ss $(( ${checktimes[$i]} - 10 )) "$outname" 1>/dev/null 2>/dev/null
let i++
done
}
function project_start () { # film
cat << HEADER > project.js
//AD
var app = new Avidemux();
app.load("$1");
app.rebuildIndex();
app.clearSegments();
HEADER
}
function project_end () { # save-as
cat << FOOTER >> project.js
app.video.setPostProc(3,3,0);
app.video.setFps1000(25000);
app.video.codec("Copy","CQ=4","0 ");
app.audio.reset();
app.audio.codec("copy",128,0,"");
app.audio.normalizeMode=0;
app.audio.normalizeValue=0;
app.audio.delay=0;
app.audio.mixer("NONE");
app.audio.scanVBR();
app.setContainer("AVI");
setSuccess(app.save("$1"));
FOOTER
}
# dezimales addieren
########################
function add_decimal () { # zahl1, zahl2, ==> ergebnis in $solution
int1=${1%.*}
int2=${2%.*}
let ret_int=$int1+$int2
dec1=${1#*.}
dec2=${2#*.}
while [ ${#dec1} -lt ${#dec2} ] ; do # solange die erste zahl "kuerzer" ist...
dec1=${dec1}0
done
while [ ${#dec2} -lt ${#dec1} ] ; do # umgekehrt...
dec2=${dec2}0
done
length=${#dec1} # maximale laenge der nachkommastellen
shopt -s extglob
dec1=${dec1#*[0]} # fuehrende nuller entfernen
dec1=${dec1:-0} # und sicherstellen, dass nicht leer
dec2=${dec2#*[0]}
dec2=${dec2:-0}
let ret_dec=$dec1+$dec2
if [ ${#ret_dec} -gt $length ] ; then # laenger, also uebertragen
let ret_int++
ret_dec=${ret_dec#1}
fi
solution=$ret_int.$ret_dec
}
#######################
# START
#######################
if [ $cutwith == "avidemux" ] && ! type avidemux2 > /dev/null ; then
echo "Bitte installieren Sie avidemux! Abbruch."
exit 1
elif ! type avisplit > /dev/null ; then
echo "Bitte installieren Sie das Paket 'transcode'! Abbruch."
exit 1
fi
# benoetigte ordner (falls nicht vorhanden) erstellen
workdir=$PWD
cd $PWD
if [ ! -d cut ] ; then
mkdir cut
fi
cutdir=${PWD%/}/cut
if [ ! -d cut/uncut ] ; then
mkdir cut/uncut
fi
uncutdir=${PWD%/}/cut/uncut
if [ ! -d $tempdir ] ; then
mkdir $tempdir || (echo "Kann temporaeren Ordner nicht erstellen! Exit." && exit 1)
fi
tempdir=${tempdir%/}
# temporaeren ordner loeschen
rm -r $tempdir/* 2>/dev/null
# lokale cutlists kopieren (passende wird nach inhalt ausgewaehlt)
[ $local -ne 0 ] && cp *.cutlist $tempdir 2>/dev/null
# zu schneidende dateien durchgehen...
for file in ${@:-*.mpg.avi}; do
cd $workdir
file=`cd ${file%/*} 2>/dev/null ; pwd`/${file##*/} # absoluten dateinamen ermitteln
[ $download -ne 0 ] && dlCutlists $file
findBestCutlist $file
usecl=$? # zurueckgegebene cutlist verwenden. (array-index)
[ $usecl -eq 0 ] && continue # abbruch, film nicht schneiden
clear 2>/dev/null # fehler ignorieren (tritt bei cron-aufrufen auf, da $TERM nicht gesetzt ist)
format_string=$( [ ${vcf2format[$usecl]} -eq 1 ] && echo "VCF2Cutlist-Standard" || echo "Assistant-Standard" )
printf "\nSchneide Film $c_filename%s$c_end mit folgender Cutlist:\n\n%s von $c_author %s $c_end\nAutor-Wertung: $c_rating %d $c_end\t\tUser-Wertung: $c_rating %s $c_end (%d Stimmen)\nFormat: %s\tAnzahl Schnitte: $c_cuts %d $c_end\nKommentar: %s\n" ${file##*/} "${cutlist[$usecl]}" "${author[$usecl]}" "${rating[$usecl]}" "${userrating[$usecl]%|*}" "${userrating[$usecl]#*|}" "$format_string" "${cuts[$usecl]}" "${comment[$usecl]}"
if [ -n "${error[$usecl]}" ] ; then
echo -e "$c_error${error[$usecl]}$c_end"
fi
if [ -n "${content[$usecl]}" ] ; then
echo -e "${content[$usecl]}"
fi
if [ -n "${errordesc[$usecl]}" ] ; then
echo -e "${errordesc[$usecl]}"
fi
echo
cutfilm $file ${cutlist[$usecl]} ${vcf2format[$usecl]}
done
# temporaeren ordner loeschen
rm -r $tempdir/* 2>/dev/null