#!/bin/bash # DoNG - Do News Groups # # A script to par2 recover and auto-extract newsgroup downloads. # # usage: dong [-n] [stubs] # -n don't extract # -l num Don't process stubs with more than num files (default 100) # -c num Don't process more than num stubs (default unlimited) # -e glob Pass files that match glob to par2 to see if they have more data # stubs enough of a filename to uniquely identify an archive set # if not given, a set is generated from par2 files in the current directory DONEDIR="finished" TRASHDIR="trash" LOGFILE="$(basename $0).log" TMP="/tmp/$(basename $0).$$" # Generate list of stubs that have associated par2 files in the current directory. # eg: foo.par2, foo.vol01+02.PAR2, etc --> foo stubs () { ls *[Pp][Aa][Rr]2 | sed -r 's/(\.[Vv][Oo][Ll][0-9]+\+[0-9]+)?(_copy_[0-9]+)?\.[Pp][Aa][Rr]2$//' | uniq | while read stub; do files=("$stub"*) if (( ${#files[@]} > "$fileslimit" )) ;then echo "Stub \"$stub\" matches too many files -- try -l ${#files[@]} if it really is just one file set." >&2 else echo "$stub" fi done | if [[ -z "$stubslimit" ]];then cat else head -n "$stubslimit" fi } # Generate a single par2 file from a stub stub2parfile () { ls -- "$1"*[Pp][Aa][Rr]2 | head -n1 } # Use par2 to check a stub # Repair files if possible # Note: Must return par2-success-status as exit code parcheck () { stub="$1" echo -n "$stub - " par2 r "$(stub2parfile "$stub")" "$stub"* $extra > "$TMP" ret=$? sed -r 's/.*\r//' "$TMP" >> "$LOGFILE" awk ' BEGIN { RS="[\n\r]" } /Loaded [0-9]+ new packets including [0-9]+ recovery blocks/ { recovery += $6} /There are a total of [0-9]+ data blocks/ { total = $6 } /You have [0-9]+ out of [0-9]+ data blocks available/ { have = $3 } /All files are correct, repair is not required/ { have = total } END { print ( 100 * ( have + recovery )) / total "% complete"; }' "$TMP" return $ret } # Extract a rar file $1 to directory $2 handlerar () { origdir="`pwd`" cd -- "$2" unrar x -- "$origdir/$1" >> "$origdir/$LOGFILE" ret=$? cd -- "$origdir" return $ret } # Extract a zip file $1 to directory $2 handlezip () { unzip -d "$2" "$1" >> "$LOGFILE" } # The file was split directly, and par2 already reassembled it. handleparsplit () { mv "$1" "$2" } # Nothing is known about the packing method. Just move the files # described in par2's output. handlefiles () { < "$1" filesinpar | tr \\n \\0 | xargs -0 bash -c ' dest="$1" shift mv "$@" "$dest"' -s "$2" } # Handle a completed download handlefinisheddownload () { stub="$1" if $extract; then destdir="$DONEDIR/$stub" mkdir -p -- "$destdir" echo -n "Extracting files... " # Test for rar file files=("$stub"*.[Rr][Aa][Rr]) if [[ -e "${files[0]}" ]];then handler=handlerar else # Test for zip fie files=("$stub"*.[Zz][Ii][Pp]) if [[ -e "${files[0]}" ]];then handler=handlezip else # Assume the stub is the file if [[ -e "$stub" ]];then files=("$stub") handler=handleparsplit else # Add more filetype checks here... # Unknown archive type. Just move the par-protected files. files=("$TMP") handler=handlefiles fi fi fi $handler "${files[0]}" "$destdir" ret=$? if [[ $ret == 0 ]];then echo "done!" mkdir -p -- "$TRASHDIR" mv -- "$stub"* "$TRASHDIR" else echo "extract failed!" fi return $ret else # Don't extract # Don't do anything? : fi } usage () { echo "$(basename "$0") [-n] [-c stubcount] [-e extrafiles] [-l limitperstub] [stub [stub ...]]" >&2 exit 1 } # MAIN extra='' fileslimit=100 stubslimit='' extract=true while getopts c:e:l:nh opt;do case $opt in c) stubslimit="$OPTARG";; e) extra="$OPTARG";; l) fileslimit="$OPTARG";; n) extract=false;; h) usage;; esac done shift `expr $OPTIND - 1` { echo -n "Starting $(basename "$0") run at "; date; } >> "$LOGFILE" { if [[ $# -eq 0 ]];then stubs else while [[ $# -gt 0 ]];do echo "$1" shift done fi } | { while read stub;do parcheck "$stub" if [[ $? -eq 0 ]];then handlefinisheddownload "$stub" fi done } { echo -n "Finished $(basename "$0") run at "; date; } >> "$LOGFILE" rm "$TMP"