#!/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