From 795d7846bfcc5c584261878eecf7518ef7ca91fb Mon Sep 17 00:00:00 2001
From: Valdos Sine <iam@toofat.ru>
Date: Sat, 24 Aug 2013 22:04:28 +0400
Subject: [PATCH] Rewrite downloader script to Python 2

Old shell scripts moved to legacy/shell branch.
---
 massdl.py | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 massdl.sh | 197 -------------------------------------------
 massps.sh |  31 -------
 3 files changed, 244 insertions(+), 228 deletions(-)
 create mode 100644 massdl.py
 delete mode 100755 massdl.sh
 delete mode 100755 massps.sh

diff --git a/massdl.py b/massdl.py
new file mode 100644
index 0000000..4ba507a
--- /dev/null
+++ b/massdl.py
@@ -0,0 +1,244 @@
+# *-* encoding: utf-8 *-*
+# For NNM-Club Uploaders
+# Copyright (c) 2012-2013 Valdos Sine <iam@toofat.ru>
+#
+# Usage:
+#
+#    python2 ./massdl.py [user] [forum_number]
+# 
+# For getting help, execute "python2 ./massdl.py" without params
+
+import time, tempfile, os, sys, commands
+version = 0.61
+unixtime = int(time.time())
+cookie = tempfile.mkstemp()
+curdir = os.getcwd()
+log_file = "/%i-massdl.log" % unixtime
+# Anti-Zapret
+domain = "http://nnm-club.me"
+
+
+def write_to_log(imessage):
+    """
+    Note, all script messages must be formatted as:
+    
+        [status] #status_number: message
+    
+    where:
+    
+        status: "I" = information, "W" - warning, "E" - error.
+        status_number - error number:
+        
+             0xx -- script environment messages
+             1xx -- authentication messages
+             2xx -- grabbing and parsing messages
+             3xx -- torrents downloading messages
+             4xx -- torrents check messages
+    
+    Last message in log must be ended with "Stopped" word.
+
+    Information starts from x00, success messages from x20, warnings from x40 and errors from x66.
+    """
+    log = open(curdir + log_file, 'a')
+    log.write(imessage)
+    log.close()
+
+def cnsl_message(imessage, *args):
+    if imessage == "invpyver":
+        print "Ваша версия Python не поддерживается. Используйте Python 2.7!"
+        write_to_log("[E] #066: версия Python не поддерживается. Завершено.\n")
+        sys.exit(666)
+    elif imessage == "nocurl":
+        print "У вас не установлен PycURL. Обратитесь к справке вашего дистрибутива для подробностей, как его установить."
+        write_to_log("[E] #067: не установлен cURL. Завершено\n")
+        sys.exit(666)
+    elif imessage == "help":
+        print "massdl.sh — скрипт для пакетной загрузки торрент-файлов из разделов NNM-club.ru.\nИспользование:\n\n\tpython massdl.py [режим] [номер_раздела]\n\nгде [режим] это режим загрузки (от имени пользователя, указанного в конфигурации скрипта — значение user, или же от имени фрилич-пользователя — значение freeleech), а [номер_форума] — номер раздела на NNM-club.ru (например, для форума '*Nix Игры' это 316).\nПримеры:\n\n\tpython massdl.py freeleech 316 — скачивание форума '*Nix Игры' от фрилич-пользователя\n\tpython massdl.py user 332 — скачивание форума 'Русский рок' от собственного, указанного в скрипте, имени\n\nАвтор: Valdos 'fat0troll' Sine.\nСкрипт использует Python версии 2.7. При запуске убедитесь, что используете правильный интерпретатор Python!\nВерсия %s\n" % version
+        sys.exit(0)
+    elif imessage == "start":
+        print "Выполняется скрипт %s.\nВерсия скрипта %s." % (sys.argv[0], version)
+        print "Параметры командной строки: %s %s" % (sys.argv[1], sys.argv[2])
+        write_to_log("[I] #020: окружение скрипта в порядке.\n")
+    elif imessage == "login_ok":
+        print "Вы залогинились успешно как пользователь %s." % set_username()[0]
+        write_to_log("[I] #120: выполнен успешный вход на сайт. Cookie лежит по адресу %s.\n" % cookie[1])
+    elif imessage == "login_failed":
+        print "Не удалось залогиниться как пользователь %s. Проверьте ваши данные внутри скрипта!" % set_username()[0]
+        write_to_log("[E] #166: неправильное имя пользователя или пароль. Завершено.\n")
+        sys.exit(666)
+    elif imessage == "parsing_started":
+        print "Начинается загрузка форума..."
+    elif imessage == "forum":
+        print "Скачиваем форум " + args[0]
+        write_to_log('[I] #220: успешно загружена стартовая страница форума "%s".\n' % args[0])
+    elif imessage == "pages":
+        print "Скачиваем страницу " + args[0]
+        write_to_log('[I] #200: загружаем страницу ' + args[0] + '...\n')
+    elif imessage == "found_topics":
+        print "Скачивание страниц завершено. Найдено топиков: " + args[0] + "."
+        write_to_log('[I] #221: успешно загружены все страницы форума. Найдено топиков: ' + args[0] + '.\n')
+    elif imessage == "download_started":
+        print "Начинаем загрузку торрентов..."
+    elif imessage == "downloading_torrent":
+        print "Скачиваю топик " + args[0] + "/" + args[1] + ": " + args[2] + "."
+        write_to_log("[I] #300 загружаем торрент " + args[0] + " из " + args[1] + " по ссылке " + args[3] + ".\n")
+    elif imessage == "done":
+        print "Скрипт завершен успешно."
+        write_to_log("[I] #021: работа завершена успешно. Завершено.\n")
+        sys.exit(0)
+
+def check_requirements():
+    req_py_version = (2,7)
+    unsupported_py_version = (3,0)
+    cur_version = sys.version_info
+
+    if cur_version >= req_py_version:
+        if cur_version < unsupported_py_version:
+            pass;
+        else:
+            cnsl_message("invpyver")
+    else:
+        cnsl_message("invpyver")
+
+    try:
+        import curl
+        import pycurl
+    except ImportError:
+        cnsl_message("nocurl")
+
+def check_params(params):
+    """
+    Checking script parameters: if there is less or more than 2 params -- show help. If there's 2 params: check it's consistency!
+    """
+    if len(params) != 3:
+        cnsl_message("help")
+    else:
+        if params[1] in ["freeleech", "user"]:
+            if str(params[2]).isdigit():
+                pass;
+            else:
+                cnsl_message("help")
+        else:
+            cnsl_message("help")
+
+def start_log():
+    log = open(curdir + log_file, 'w')
+    log.write('================================================================================\n= Скрипт скачки раздела NNM-Club для Linux и Unix-like ОС.                     =\n= Версия: %s                                                                 =\n================================================================================\n' % version)
+    log.close()
+
+def set_username():
+    if sys.argv[1] == "freeleech":
+        username = "freeleech_user"
+        password = "freeleech_password"
+    else:
+        username = "username"
+        password = "password"
+    return (username, password)
+
+def connect():
+    cookiefile = open(cookie[1], 'r+w')
+    write_to_log("[I] #100: пытаемся войти на " + domain + "...\n")
+    cookie_txt = commands.getoutput('curl -c - -d "username=' + set_username()[0] + '&password=' + set_username()[1] + '&autologin=on&login=%C2%F5%EE%E4&redirect=index.php" "' + domain + '/forum/login.php" 2>>' + curdir + log_file )
+    if cookie_txt.find("Cookie") != -1:
+        # If it's -1, then there is no cookie!
+        cookiefile.write(cookie_txt)
+        print cookiefile.read()
+        cnsl_message("login_ok")
+    else:
+        cnsl_message("login_failed")
+        sys.exit(666)
+
+def iteratorium(links_array, offset):
+    """
+    Links array -- array with links to topics containing torrents. Offset -- load parameter (for pages)
+    """
+    cnsl_message("pages", str((offset / 50) + 1))
+    forum_page = commands.getoutput('curl -b ' + cookie[1] + ' "' + domain + '/forum/viewforum.php?f=' + sys.argv[2] + '&start=' + str(offset) + '" | iconv -f cp1251 -t utf-8')
+    for line in forum_page.splitlines():
+        if 'viewtopic.php' in line:
+            if 'DL:' in line:
+                for substring in line.split('"'):
+                    if 'viewtopic.php' in substring:
+                        links_array.append(substring.split('&')[0])
+    # Check for the next page
+    if forum_page.find("След.") != -1:
+        iteratorium(links_array, offset + 50)
+    clean_array = []
+    for item in links_array:
+        if item not in clean_array:
+            clean_array.append(item)
+    return clean_array
+
+def download():
+    """
+    And now that's main part of the script. We need to:
+    
+        * iterate over each other page of the forum
+        * collect all forum topics
+        * collect all torrents from topics
+        * check the integrity of downloaded stuff (or why we want to see the log?)
+    The magic begins...
+    """
+    # Firstly, we need to download forum's startpage
+    cnsl_message("parsing_started")
+    startpage = commands.getoutput('curl -b ' + cookie[1] + ' ' + domain + '/forum/viewforum.php?f=' + sys.argv[2] + ' | iconv -f cp1251 -t utf-8')
+    # Finding forum name :D
+    for line in startpage.splitlines():
+        if 'maintitle' in line:
+            fname_raw = line
+    forumname = fname_raw.split('>')[3].split('<')[0]
+    cnsl_message("forum", forumname)
+    # And now we start our "good iteratorium"
+    topics = iteratorium([], 0)
+    cnsl_message("found_topics", str(len(topics)))
+    # Aaaand... the first real actions is here
+    torrentsdir = "%s/%s" % (curdir, sys.argv[2])
+    os.mkdir(torrentsdir)
+    get_torrent(topics, torrentsdir, forumname)
+
+def parse_topic(topic, forumname, topics, torrents_count, torrentsdir):
+    topicpage = commands.getoutput('curl -b ' + cookie[1] + ' "' + domain + '/forum/' + topic + '" | iconv -f cp1251 -t utf-8')
+    for line in topicpage.splitlines():
+        if 'maintitle' in line:
+            tname_raw = line
+    topicname = tname_raw.split('>')[3].split('<')[0]
+    for line in topicpage.splitlines():
+        if 'download.php' in line:
+            for lvl2 in line.split('>'):
+                if 'download.php' in lvl2:
+                    dl_raw = lvl2
+    downlink = dl_raw.split('"')[1]
+    cnsl_message("downloading_torrent", str(torrents_count), str(len(topics)), topicname, (domain + "/" + topic))
+    os.chdir(torrentsdir)
+    if not os.path.exists("./%i00" % (torrents_count / 100)):
+        os.makedirs("./%i00" % (torrents_count / 100))
+    os.chdir("./%i00" % (torrents_count / 100))
+    download_torrent(downlink)
+
+def download_torrent(downlink):
+    os.system('curl -b ' + cookie[1] + ' -O -J -L "' + domain + '/forum/' + downlink + '" 2>>' + curdir + log_file)
+
+def get_torrent(topics, torrentsdir, forumname):
+    cnsl_message("download_started")
+    torrents_count = 0
+    for topic in topics:
+        torrents_count = torrents_count + 1
+        parse_topic(topic, forumname, topics, torrents_count, torrentsdir)
+
+def cleanup():
+    os.remove(cookie[1])
+    cnsl_message("done")
+
+
+# That's da fuckin' magic here ;-)
+
+check_params(sys.argv)
+start_log()
+check_requirements()
+
+import curl, pycurl
+
+cnsl_message("start")
+connect()
+download()
+cleanup()
diff --git a/massdl.sh b/massdl.sh
deleted file mode 100755
index 82f59ef..0000000
--- a/massdl.sh
+++ /dev/null
@@ -1,197 +0,0 @@
-#!/bin/bash
-# For NNM-Club Uploaders
-# Copyright (c) 2012 Valdos Sine <fat0troll at yandex dot ru>
-#
-# Usage:
-#
-#     ./massdl.sh [mode] [forum]
-#
-# For getting help, execute ./massdl.sh without params
-
-UNIXTIME=`date +%s`
-TMPDIR=`mktemp -d`
-CURDIR=`pwd`
-LOG_FILE="$UNIXTIME-massdl.log"
-SUBFORUMSDL=0
-
-wget_func () {
-  # Wget helper, which makes many wget usages more stable
-  # Usage wget_func [path_to_redirect] [anything (wget params, such as -O...)]
-  wget -t 99 --wait=1 --post-data="username=$USERNAME&password=$PASSWORD&autologin=on&login=%C2%F5%EE%E4&redirect=${1}" "http://nnm-club.me/forum/login.php" ${2} -a $CURDIR/$LOG_FILE
-  if [[ $? -gt 0 ]] ; then
-    echo -ne "..ошибка загрузки, повтор.."
-    wget_func "${1}" "${2}"
-  fi
-}
-
-# Checking if we have parameter, otherwise show help in Russian
-if [[ ${@} == "" ]] ; then
-  echo "massdl.sh — скрипт для пакетной загрузки торрент-файлов из разделов NNM-club.ru."
-  echo "Использование:"
-  echo -ne "\n\t./massdl.sh [режим] [номер_раздела]\n\n"
-  echo "где [режим] это режим загрузки (от имени пользователя, указанного в конфигурации скрипта — значение user, или же от имени фрилич-пользователя — значение freeleech), а [номер_форума] — номер раздела на NNM-club.ru (например, для форума '*Nix Игры' это 316)."
-  echo -ne "Примеры:\n\n\t./massdl.sh freeleech 316 — скачивание форума '*Nix Игры' от фрилич-пользователя\n\t./massdl.sh user 332 — скачивание форума 'Русский рок' от собственного, указанного в скрипте, имени\n\n"
-  echo "Автор: Valdos 'fat0troll' Sine."
-  echo "Основано на технологиях GNU."
-  echo "Версия 0.51"
-  exit 0
-else
-  echo "Выполняется скрипт ${0}."
-  echo "Строка параметров работы: ${@}."
-  echo -ne "Проводим разбор строки..."
-  # Script configuration
-  case "x${1}" in
-  "xuser" )
-    # TYPE YOUR USERNAME AND PASSWORD HERE!
-    USERNAME=user
-    PASSWORD=password
-    echo -ne "пользователь $USERNAME"
-  ;;
-  "xfreeleech" )
-    # Freeleech user credentials
-    USERNAME=freeleech_user
-    PASSWORD=freeleech_password
-    echo -ne "фрилич"
-  ;;
-  * )
-    # ERROR!
-    echo -ne "ошибка, неправильный режим работы скрипта! Разрешённые режимы — freeleech или же user.\nЗавершение...\n"
-    exit 1
-  ;;
-  esac
-  FORUM=${2}
-  echo -ne ", номер форума ${FORUM}.\n"
-  echo "Начинаем работу..."
-fi
-
-# Heading log file
-
-cat > $LOG_FILE << EOF
-================================================================================
-= Скрипт скачки раздела NNM-Club для Linux и Unix-like ОС.                     =
-= Построен на технологиях GNU.                                                 =
-  Дата запуска: `date`.
-================================================================================
-EOF
-
-# Downloading main page of the selected forum
-wget_func `echo 'viewforum.php?f='$FORUM` "-O $TMPDIR/page0.html"
-FORUMNAME=`cat $TMPDIR/page0.html | iconv -f cp1251 -t utf-8 | grep maintitle | cut -d '>' -f 4 | cut -d '<' -f 1 | sed 's/\//_/g'`
-echo "Скачиваем форум \"${FORUMNAME}\"..."
-ITERATOR=0
-THREADS=0
-
-# Downloading all pages
-while [ `cat $TMPDIR/page$ITERATOR.html | iconv -f cp1251 -t utf-8 | grep "След."| grep 'start' | wc -l` == "2" ]
-do
-  iconv -f cp1251 -t utf-8 $TMPDIR/page$ITERATOR.html > $TMPDIR/page$ITERATOR.utf8.html
-  cat $TMPDIR/page$ITERATOR.utf8.html | grep "download.php" -B 2 | grep topictitle | sed 's/.*<a href=\([^>]*\).*/\1/' | cut -d '"' -f 2 >> $TMPDIR/alllinkz.txt
-  THREADS=$(($THREADS + 50))
-  ITERATOR=$(($ITERATOR + 1))
-  START=$(($ITERATOR * 50))
-  echo "Скачиваем страницу $((${ITERATOR} + 1))..."
-  wget_func `echo 'viewforum.php?f='$FORUM'%26start='$START` "-O $TMPDIR/page$ITERATOR.html"
-done
-# Last loop ending :)
-iconv -f cp1251 -t utf-8 $TMPDIR/page$ITERATOR.html > $TMPDIR/page$ITERATOR.utf8.html
-cat $TMPDIR/page$ITERATOR.utf8.html | grep 'download.php' -B 2 | grep topictitle | sed 's/.*<a href=\([^>]*\).*/\1/' | cut -d '"' -f 2 >> $TMPDIR/alllinkz.txt
-
-# Detecting subforums
-cat $TMPDIR/page0.utf8.html | grep viewforum | grep forumlink | grep -v "images" | cut -d '"' -f 14 | cut -d '=' -f 2 >> $TMPDIR/subforumz.txt
-
-if [[ `cat $TMPDIR/subforumz.txt | wc -l` -gt 0 ]] ; then
-  echo "Выявлены подфорумы!"
-  while read subline
-  do
-    echo "Добавляем в очередь закачки форум $subline..."
-    echo "${0} ${1} $subline" >> $TMPDIR/$UNIXTIME-subforumsdl.sh
-  done < $TMPDIR/subforumz.txt
-  chmod +x $TMPDIR/$UNIXTIME-subforumsdl.sh
-  SUBFORUMSDL=1
-fi
-
-# You can use it as statistic :)
-THREADS=`cat $TMPDIR/alllinkz.txt | wc -l`
-echo -ne "Выявлено $THREADS топиков, содержащих торренты! Скачиваем...\n"
-# And this is what we need -- getting threads and torrents!
-# Torrents will be saved in folders like 100, 200, 300... Every folder contains
-# 100 torrents (except the last one).
-THREAD=0
-mkdir "$FORUM-$FORUMNAME"
-while read line
-do
-  pushd "$FORUM-${FORUMNAME}" >> /dev/null
-  mkdir "$(($THREAD / 100))" 2>> /dev/null
-  THREAD=$(($THREAD + 1))
-  echo -ne "Скачиваем торрент $THREAD/$THREADS."
-  # Temporary solution until will be finded way to use cookie.txt...
-  wget_func $line "-O $TMPDIR/out_$THREAD.html"
-  # :3
-  TOPICHDR=`cat $TMPDIR/out_$THREAD.html | grep maintitle | cut -d '>' -f 4 | cut -d '<' -f 1 | iconv -f cp1251 -t utf-8`
-  TOPICLNK=`cat $TMPDIR/out_$THREAD.html | grep maintitle | cut -d '"' -f 10`
-  echo -ne "."
-  wget_func `cat $TMPDIR/out_$THREAD.html | iconv -f cp1251 -t utf-8 | grep download.php | sed 's/.*<a href=\([^>]*\).*/\1/' | cut -d '"' -f 2 | tail -1 -` "-P "$(($THREAD / 100))/" --content-disposition"
-  # Check for bad wget saving...
-  if [[ -f login.php ]] ; then
-    echo -ne ".имя файла потеряно. используется сгенерированное скриптом.."
-    mv login.php "$(($THREAD / 100 ))"/$THREAD.torrent
-    # Check if file is real torrent. If not... get an error
-    if [[ `file "$(($THREAD / 100 ))"/$THREAD.torrent | grep BitTorrent` ]] ; then
-      echo -ne ".файл сохранён как $(($THREAD / 100 ))/$THREAD.torrent.."
-    else
-      echo -ne ".скачан не BitTorrent-файл! После завершения работы скрипта проверьте файл $UNIXTIME-error.log — там будет ссылка на нескачанный топик!\n"
-      echo -ne "Пишем информацию в лог-файл.."
-      echo "Топик $THREAD: ошибка скачивания $TOPICHDR [https://nnm-club.ru/forum/$TOPICLNK]" >> $CURDIR/$UNIXTIME-error.log
-      rm "$(($THREAD / 100 ))"/$THREAD.torrent
-    fi
-  fi
-  echo -ne "."
-  # Check sanity: torrents must equal threads. If not -- post a warning ;)
-  sanity_error () {
-    echo -ne ".кажется, топик не отдал нам файла. После завершения работы скрипта проверьте $UNIXTIME-error.log — там будет ссылка на проблемный топик!\n"
-    echo -ne "Пишем информацию в лог-файл.."
-    echo "Топик $THREAD: подозрительный $TOPICHDR [https://nnm-club.ru/forum/$TOPICLNK]" >> $CURDIR/$UNIXTIME-error.log
-  }
-  actualize_delta () {
-    # Actualizes delta between files count and expected count: when we switching
-    # folder delta should be nulled!
-    if [[ $(($THREAD / 100)) -gt $LAST_ERROR ]] ; then
-      ERROR_DELTA=0
-    else
-      if [[ x$ERROR_DELTA == "x" ]] ; then
-	ERROR_DELTA=0
-      fi
-    fi
-  }
-  add_delta () {
-    # Adding delta
-    LAST_ERROR=$(($THREAD / 100))
-    ERROR_DELTA=$(($ERROR_DELTA + 1))
-  }
-  actualize_delta
-  if [[ $(($THREAD / 100)) -eq '0' ]] ; then
-    if [[ `ls $(($THREAD / 100))/*.torrent | wc -l` -eq $(($THREAD - $(($THREAD / 100 * 100)) - $ERROR_DELTA)) ]] ; then
-      echo -ne ""
-    else
-      sanity_error
-      add_delta
-    fi
-  else
-    if [[ `ls $(($THREAD / 100))/*.torrent | wc -l` -eq $(($THREAD - $(($THREAD / 100 * 100)) + 1 - $ERROR_DELTA)) ]] ; then
-      echo -ne ""
-    else
-      sanity_error
-      add_delta
-    fi
-  fi
-  echo -ne ".\n"
-  popd >> /dev/null
-done < $TMPDIR/alllinkz.txt
-
-# Downloading subforums
-if [[ "x$SUBFORUMSDL" == "x1" ]] ; then
-  $TMPDIR/$UNIXTIME-subforumsdl.sh
-fi
-
-# Cleaning up...
-rm -r $TMPDIR
diff --git a/massps.sh b/massps.sh
deleted file mode 100755
index 66daf18..0000000
--- a/massps.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-# For NNM-Club Uploaders
-# Copyright (c) 2012 Valdos Sine <fat0troll at yandex dot ru>
-#
-# Usage:
-# 
-#     ./massps.sh
-#
-# Install pyrocore by executing:
-#
-#     sudo easy_install pyrocore
-
-TMPDIR=`mktemp -d`
-
-SIZE=0
-COUNT=0
-
-# Find all torrents
-find . -name *torrent > $TMPDIR/allfilez.txt
-
-while read line
-do
-  COUNT=$(($COUNT + 1))
-  echo "$COUNT"
-  INCREMENT=`lstor "$line" -qo __size__`
-  SIZE=$(($SIZE + $INCREMENT))
-done < $TMPDIR/allfilez.txt
-
-echo "$SIZE bytes -> $(($SIZE / 1024)) KB -> $(($SIZE / 1024 / 1024 )) MB -> $(($SIZE / 1024 / 1024 / 1024)) GB."
-
-rm -r $TMPDIR