#!/bin/bash # # Author: Twily 2015 # Description: 4chan image downloader # Requires: imagemagick, ffmpeg, wget, sed, perl # Optional: libnotify, thunar, sxiv # Usage: $ sh ./4cdl # (URL Format: http(s)://../thread//) # # Features: * Image download w/ Auto-update until 404 # * Stores the thread alongside; archiving # * Generates thumbnails locally # * Creates a catalog file for each of the boards # * Creates an index homepage file; hosting # # Optional rename tool for reuploading images to 4can: http://pastebin.com/bLqHyBPg # P=/home/guest/4cdl-images # Download Path PW=5532 # Index access code C=true # Clear output L=true # Loop until 404 E=true # Exit on finish V=18 # Threads per page in catalog W=(10 15 20 30 60 90 120 180 240 300) # Update Timer # function prepare Prepare directories, files and variables # function thread Archives the thread as an html page # function download Downloads all images from the thread # function progress Progressbar for image downloads # function thumbnail Generates thumbnails for images # function clean Print links and cleanup files # function cleanse Cleanse the url from bad characters # function catalog Generates a catalog for the current board # function index Generates an index file for board listing # function loop Loops the downloader until thread 404 # function input Choose action to do once download has completed # function main Script begin # (See bottom for manual catalog/index rebuilding) # # Color Scheme # # 17181A - Background 8C88FC - Link Normal A2E059 - Quote # 27282B - Content BG EA75BC - Link Active D54A56 - Trip # CECFD1 - Foreground 606163 - Link Dead, Active Border F4AA5C - Subject # function prepare { # Generate folder name from url S=${H%/*} && T=${S##*/} # T = Thread ID F=${H##*/}; if [ "$T" = "thread" ]; then T=$F && F="thread"; fi S=${H%%/thread*} && B=${S##*/} # B = Board Tag N=$B"/"$T"-"$F # Temporary files F1="4c.index.$T.tmp" F2="4c.list.$T.tmp" F3="4c.name.$T.tmp" # Thread html files F4=$T"-"$F".html" F5=$B"-catalog.html" F6="index.html" # Make directory d=false if [ ! -d "$P/$N" ]; then mkdir -p $P/$N && d=true; fi # Download page/thread wget "$H" -O $P/$F1 if [[ "$?" -ne "0" ]]; then X=true; if $d; then rm -rf $P/$N; fi fi # Add lines to index file sed -i 's/>\n \n $P/$F2 grep "fileText" < $P/$F1 > $P/$F3 # Remove all html code from the links/names sed -i 's/.*/DELETED/g' $P/$F2 sed -i 's///g' $P/$F3 sed -i 's/<\/a>.*//g' $P/$F3 sed -i 's/">.*//g' $P/$F3 if ! $X; then thread; fi } function thread { # Create thread html page echo -ne "\033[1;33mBuilding thread...." cp $P/$F1 $P/$N/$F4 a=0 && b=0 && e=0 c=true && d=true # Find lines to remove while read p; do if $c; then let a=$a+1 case "$p" in *form\ name=\"delform\"*) c=false ;; esac fi if $d; then let b=$b+1 case "$p" in *navLinks\ navLinksBot*) d=false && let b=$b-1 ;; esac fi let e=$e+1 done < $P/$N/$F4 # Trim html code sed -i $b','$e'd' $P/$N/$F4 sed -i '3,'$a'd' $P/$N/$F4 sed -i '///' $P/$N/$F4 sed -i 's///' $P/$N/$F4 # Apply CSS style to html file echo -e "\n\n" \ "\n" \ "$F4\n" \ "" | cat - $P/$N/$F4 > $P/$N/$F4"_tmp" && mv $P/$N/$F4"_tmp" $P/$N/$F4 sed -i "s/class=\"quotelink\">>>$T<\/a>/class=\"quotelink\">\>\>$T (OP)<\/a>/g" $P/$N/$F4 # Find post IDs and quotelinks e=1 && t=false declare -A arr while read p; do if $t; then o=$(echo $p | sed -e 's/.*) t=true ;; *\"\ class=\"quotelink\"\>*) q=$(echo $p | sed -e 's/.*=1; j-- )); do perl -i -ne 'print; print " >>'${arr2[$j]}'" if $. == '${arr2[0]} $P/$N/$F4 done done echo -e "[\033[1;37mDONE\033[1;33m]\033[0m" } I=0 && M=0 function download { # Loop trough list and download all images i=1 && op=true t=$(wc -l < $P/$F2) if $C; then echo -en "\ec"; fi while read p <&3; do a=${p##*/} a=${a%%.*} b=$(sed -n $i"p" $P/$F3) b=${b//'/} b=${b//\'/} progress $t if [[ ! -f $P/$N/"$a-$b" ]]; then # Image Download if [[ "$p" != "DELETED" ]]; then wget $p -O $P/$N/"$a-$b" if $op; then thumbnail "$P/$N" "$a-$b" 250x250 \#17181A else thumbnail "$P/$N" "$a-$b" 125x125 \#27282B fi else let i=$i-1 let t=$t-1 fi else # Skip if image exists echo -e "\033[0;37m'$P/$N/$a-$b'\033[0m\n\033[1;37m...\033[1;33m[\033[1;37mSKIPPED\033[1;33m]\033[0m" tA="${b%.*}" # filename if [[ ! -f $P/$N/"thumbs/$a-$tA"_s.jpg ]]; then if $op; then thumbnail "$P/$N" "$a-$b" 250x250 \#17181A else thumbnail "$P/$N" "$a-$b" 125x125 \#27282B fi fi fi op=false cleanse $b && b=$s tE="${b##*.}" # extention tA="${b%.*}" # filename # Update images in html file sed -i "s/href=\".*"$a".*\"/href=\"\.\/$a-$b\"/g" $P/$N/$F4 sed -i "s/src=\".*"$a"s.*\"/src=\"\.\/thumbs\/$a-$tA\_s.jpg\" width=\"125px\" height=\"125px\"/g" $P/$N/$F4 let i=$i+1 if $C; then echo -en "\ec"; fi done 3< $P/$F2 if [ "$I" -lt "$i" ]; then let I=$i-1; fi if [ "$(md5sum $P/$N/$F4)" != "$M" ]; then R=0; fi M=$(md5sum $P/$N/$F4) # Remove failed images to attempt redownload next time find $P/$N -size 0 -exec rm -f {} + } function progress { # Progressbar ($1 = max value) echo -ne "\033[1;30mProgress: \033[1;33m[$i / $t]" pC=$(( $(tput cols) - 30 )) pL=$(( $i * $pC / $1 )) pP=$(( $i * 100 / $1 )) printf ' %.0s' $(seq 1 $(( 4 - ${#pP} )) ) echo -ne "$pP%[" for j in $(seq 1 $pL); do echo -n "="; done echo -n ">" for k in $(seq $pL $(( $pC - 1 )) ); do echo -n " "; done echo -e "]\033[0m\n" } function thumbnail { # Make thumbnail for image downloaded tD="$1" tF="$2" if [ ! -d "$tD/thumbs/" ]; then mkdir -p "$tD/thumbs/"; fi tE="${tF##*.}" # extention tA="${tF%.*}" # filename if [ ! -f "$tD/thumbs/$tA"_s.jpg ]; then echo -e "$tD/$tF >> $tD/thumbs/$tA"_s.jpg case "$tE" in "gif") convert "$tD/$tF[0]" -trim +repage -resize $3\> -gravity center -background $4 -extent $3 "$tD/thumbs/$tA"_s.jpg ;; "webm") ffmpeg -i "$tD/$tF" -f image2 -vframes 1 -s $3 "$tD/thumbs/$tA"_s.jpg ;; *) convert "$tD/$tF" -trim +repage -resize $3\> -gravity center -background $4 -extent $3 "$tD/thumbs/$tA"_s.jpg ;; esac fi } function clean { # Notification #notify-send "4cdl: All ("$I") images have finished downloading.\nDirectory: \"$P/$N/\"" echo -e "\033[1;32mAll ("$I") images have finished downloading.\033[0m" echo -e "\033[1;30mThread: \033[0m"$H echo -e "\033[1;30mDirectory: \033[0m"$P/$N/ echo -e "\033[1;30mLocal: \033[0mfile://"$P/$N/$F4 echo -e "\033[1;30mCatalog: \033[0mfile://"$P/$F5 echo -e "\033[1;30mIndex: \033[0mfile://"$P/$F6" \033[1;30m(Code: "$PW")\n" #catalog #index # Remove temporary files rm -f $P/$F1 rm -f $P/$F2 rm -f $P/$F3 } function cleanse { # Clean urls/paths s="$@" s=${s//%/%25} s=${s//#/%23} s=${s//&/%26} s=${s//\?/%3F} } function catalog { # Create catalog of all downloaded threads from current board echo -ne "\033[1;33mBuilding catalog..." d=($P/$B/*) rm -f $P/$F5 # Apply header content echo -e "\n\n" \ "\n" \ "/"$B"/ - Catalog\n" \ "

/"$B"/ - Catalog

\n" \ "\n" \ "\n\n" \ "
\n" \ "
\n" \ "
\n" \ "
\n" \ "
\n" \ "
\n" \ "
\n" \ "\n\n\n" >> $P/$F5 echo -e "[\033[1;37mDONE\033[1;33m]\033[0m" } function index { # index html page if $X; then rm -f $P/$F1 F1="4c.index.tmp" wget "https://boards.4chan.org/g/" -O $P/$F1 # Add lines to index file sed -i 's/>\n \n\n\n" \ "\n" \ "4cdl\n\n" \ "\n" \ "\n\n" \ "
\n Select Board:\n

\n" >> $P/$F6 # get directories array=($P*/*/) for dir in "${array[@]}"; do aD1=${dir#$P} aD1=$(echo "$aD1" | sed -e 's/\//\\\//g') aD2=$(grep "$aD1" < $P/$F1) aD3=${aD1//\\} aD3=${aD3//\/} aD2=$(echo "$aD2" | grep -Po '^.*?\K(?<='$aD1'" title=").*?(?=">'$aD3')') echo -e "
" >> $P/$F6 done echo -e "
\n\n" \ "
\n Enter Code:\n

\n\n" \ "
\n" \ " \n" \ " \n" \ "
\n
\n\n" >> $P/$F6 echo -e "[\033[1;37mDONE\033[1;33m]\033[0m" rm -f $P/$F1 } function loop { if ! $X; then echo -ne "\n\033[1;33mUpdating in " # Wait timer i=${W[$R]} while [ "$i" -gt "0" ]; do echo -ne $i".." sleep 1 let i=$i-1 done echo -e "\033[0m" if [ "$R" -lt "$(( ${#W[@]} - 1 ))" ]; then let R=$R+1; fi main else echo -e "\n\033[1;31mThread no longer exist [404].\033[0m\n" input fi } function input { if $E; then exit 0; fi R=0 echo -e "\nOptions: " echo -e " 1. Continue Download" echo -e " 2. Open Directory" echo -e " 3. View Pictures" echo -e " 4. Quit\n" read -p "Enter: " -n 1 -r echo -e "\n" case "$REPLY" in 1) main ;; 2) thunar $P/$N; input ;; 3) sxiv $P/$N/*; input ;; 4) exit 0 ;; *) echo "Invalid input..."; input ;; esac } function main { prepare download clean if $L; then loop; else input; fi } H="$1" X=false R=0 if [ "$H" == "catalog" ]; then # Rebuild catalog w/ $ sh ./4cdl catalog mlp B="$2" F5=$B"-catalog.html" catalog echo -e "\033[1;30mCatalog: \033[0mfile://"$P/$F5"\n" exit 0 fi if [ "$H" == "index" ]; then # Rebuild index w/ $ sh ./4cdl index X=true F1="4c.index.tmp" F6="index.html" index echo -e "\033[1;30mIndex: \033[0mfile://"$P/$F6" \033[1;30m(Code: "$PW")\n" exit 0 fi main exit 0