1

Rewrite as standard Python CLI app and more

Now this app supports configuration via lib/config.py. No more
hardcode!

Now torrent files sorted by last moderator check date, and torrents
that haven't been checked will be skipped with log message.
This commit is contained in:
Valdos Sine 2014-12-26 23:08:52 +04:00 committed by Vladimir Hodakov
parent 795d7846bf
commit d391c3ddea
3 changed files with 118 additions and 72 deletions

10
lib/config.py.example Executable file
View File

@ -0,0 +1,10 @@
# user editable values
username = 'username'
password = 'password'
# system values, edit at your own risk
domain = 'http://nnm-club.me'

49
lib/messages.py Executable file
View File

@ -0,0 +1,49 @@
# *-* encoding: utf-8 *-*
"""
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 code numbers starts from x00, success messages from x20, warnings from x40 and errors from x66. Console messages have same numbers starting from prefix c. Log messages start from prefix m
"""
c000 = "massdl.sh — скрипт для пакетной загрузки торрент-файлов из разделов NNM-club.ru.\nИспользование:\n\n\tpython massdl.py [номер_раздела]\n\nгде [номерорума] — номер раздела на NNM-club.ru (например, для форума '*Nix Игры' это 316).\nПримеры:\n\n\tpython massdl.py 316 — скачивание форума '*Nix Игры'\n\tpython massdl.py 332 — скачивание форума 'Русский рок'\n\nАвтор: Valdos 'fat0troll' Sine.\nСкрипт использует Python версии 2.7. При запуске убедитесь, что используете правильный интерпретатор Python!\nКонфигурация скрипта производится в конфигурационном файле lib/config.py, имеющем опции с очевидными названиями.\nВерсия %s\n"
c010 = "Выполняется скрипт %s.\nВерсия скрипта %s."
c020 = "Параметры командной строки: %s"
c021 = "Скрипт завершен успешно."
c066 = "Ваша версия Python не поддерживается. Используйте Python 2.7!"
c067 = "У вас не установлен PycURL. Обратитесь к справке вашего дистрибутива для подробностей, как его установить."
c120 = "Вы залогинились успешно как пользователь %s."
c166 = "Не удалось залогиниться как пользователь %s. Проверьте ваши данные в файле lib/config.py!"
c200 = "Начинается загрузка форума..."
c210 = "Скачиваем страницу %s..."
c220 = "Скачиваем форум %s"
c221 = "Скачивание страниц завершено. Найдено топиков: %s."
c300 = "Начинаем загрузку торрентов..."
c310 = "Скачиваю топик %s/%s: %s (обновлён %s)."
c340 = "Топик %s/%s: %s не проверен модератором. Пропускается."
m000 = '================================================================================\n= Скрипт скачки раздела NNM-Club для Linux и Unix-like ОС. =\n= Версия: %s =\n================================================================================\n'
m020 = "[I] #020: окружение скрипта в порядке.\n"
m021 = "[I] #021: работа завершена успешно. Завершено.\n"
m025 = "[I] #100: пытаемся войти на %s...\n"
m066 = "[E] #066: версия Python не поддерживается. Завершено.\n"
m067 = "[E] #067: не установлен cURL. Завершено\n"
m120 = "[I] #120: выполнен успешный вход на сайт. Cookie лежит по адресу %s.\n"
m166 = "[E] #166: неправильное имя пользователя или пароль. Завершено.\n"
m210 = "[I] #200: загружаем страницу %s..."
m220 = "[I] #220: успешно загружена стартовая страница форума '%s'.\n"
m221 = "[I] #221: успешно загружены все страницы форума. Найдено топиков: %s.\n"
m310 = "[I] #310 загружаем торрент %s из %s по ссылке %s.\n"
m340 = "[W] #340 топик %s из %s [%s, %s] не проверен модераторами. Пропуск.\n"

129
massdl.py Normal file → Executable file
View File

@ -1,90 +1,76 @@
# *-* encoding: utf-8 *-* # *-* encoding: utf-8 *-*
# For NNM-Club Uploaders # For NNM-Club Uploaders
# Copyright (c) 2012-2013 Valdos Sine <iam@toofat.ru> # Copyright (c) 2012-2014 Valdos Sine <iam@toofat.ru>
# #
# Usage: # Usage:
# #
# python2 ./massdl.py [user] [forum_number] # python2 ./massdl.py [forum_number]
# #
# For getting help, execute "python2 ./massdl.py" without params # For getting help, execute "python2 ./massdl.py" without params
import time, tempfile, os, sys, commands import time, tempfile, os, sys, commands
version = 0.61 import lib.config as uconfig
import lib.messages as umessages
import dateutil
from dateutil.parser import *
version = 0.80
unixtime = int(time.time()) unixtime = int(time.time())
cookie = tempfile.mkstemp() cookie = tempfile.mkstemp()
curdir = os.getcwd() curdir = os.getcwd()
log_file = "/%i-massdl.log" % unixtime log_file = "/%i-massdl.log" % unixtime
# Anti-Zapret domain = uconfig.domain
domain = "http://nnm-club.me"
def write_to_log(imessage): 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 = open(curdir + log_file, 'a')
log.write(imessage) log.write(imessage)
log.close() log.close()
def cnsl_message(imessage, *args): def cnsl_message(imessage, *args):
if imessage == "invpyver": if imessage == "invpyver":
print "Ваша версия Python не поддерживается. Используйте Python 2.7!" print umessages.c066
write_to_log("[E] #066: версия Python не поддерживается. Завершено.\n") write_to_log(umessages.m066)
sys.exit(666) sys.exit(666)
elif imessage == "nocurl": elif imessage == "nocurl":
print "У вас не установлен PycURL. Обратитесь к справке вашего дистрибутива для подробностей, как его установить." print umessages.c067
write_to_log("[E] #067: не установлен cURL. Завершено\n") write_to_log(umessages.m067)
sys.exit(666) sys.exit(666)
elif imessage == "help": 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 print umessages.c000 % version
sys.exit(0) sys.exit(0)
elif imessage == "start": elif imessage == "start":
print "Выполняется скрипт %s.\nВерсия скрипта %s." % (sys.argv[0], version) print umessages.c010 % (sys.argv[0], version)
print "Параметры командной строки: %s %s" % (sys.argv[1], sys.argv[2]) print umessages.c020 % sys.argv[1]
write_to_log("[I] #020: окружение скрипта в порядке.\n") write_to_log(umessages.m020)
elif imessage == "login_ok": elif imessage == "login_ok":
print "Вы залогинились успешно как пользователь %s." % set_username()[0] print umessages.c120 % set_username()[0]
write_to_log("[I] #120: выполнен успешный вход на сайт. Cookie лежит по адресу %s.\n" % cookie[1]) write_to_log(umessages.m120 % cookie[1])
elif imessage == "login_failed": elif imessage == "login_failed":
print "Не удалось залогиниться как пользователь %s. Проверьте ваши данные внутри скрипта!" % set_username()[0] print umessages.c166 % set_username()[0]
write_to_log("[E] #166: неправильное имя пользователя или пароль. Завершено.\n") write_to_log(umessages.m166)
sys.exit(666) sys.exit(666)
elif imessage == "parsing_started": elif imessage == "parsing_started":
print "Начинается загрузка форума..." print umessages.c200
elif imessage == "forum": elif imessage == "forum":
print "Скачиваем форум " + args[0] print umessages.c220 % args[0]
write_to_log('[I] #220: успешно загружена стартовая страница форума "%s".\n' % args[0]) write_to_log(umessages.m220 % args[0])
elif imessage == "pages": elif imessage == "pages":
print "Скачиваем страницу " + args[0] print umessages.c210 % args[0]
write_to_log('[I] #200: загружаем страницу ' + args[0] + '...\n') write_to_log(umessages.m210 % args[0])
elif imessage == "found_topics": elif imessage == "found_topics":
print "Скачивание страниц завершено. Найдено топиков: " + args[0] + "." print umessages.c221 % args[0]
write_to_log('[I] #221: успешно загружены все страницы форума. Найдено топиков: ' + args[0] + '.\n') write_to_log(umessages.m221 % args[0])
elif imessage == "download_started": elif imessage == "download_started":
print "Начинаем загрузку торрентов..." print umessages.c300
elif imessage == "downloading_torrent": elif imessage == "downloading_torrent":
print "Скачиваю топик " + args[0] + "/" + args[1] + ": " + args[2] + "." print umessages.c310 % (args[0], args[1], args[2], args[4])
write_to_log("[I] #300 загружаем торрент " + args[0] + " из " + args[1] + " по ссылке " + args[3] + ".\n") write_to_log(umessages.m310 % (args[0], args[1], args[3]))
elif imessage == "skip_non_approved":
print umessages.c340 % (args[0], args[1], args[2])
write_to_log(umessages.m340 % (args[0], args[1], args[2], args[3]))
elif imessage == "done": elif imessage == "done":
print "Скрипт завершен успешно." print umessages.c021
write_to_log("[I] #021: работа завершена успешно. Завершено.\n") write_to_log(umessages.m021)
sys.exit(0) sys.exit(0)
def check_requirements(): def check_requirements():
@ -108,36 +94,27 @@ def check_requirements():
def check_params(params): 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! Checking script parameters: if there is less or more than one param -- show help. If there's one param: check ones consistency!
""" """
if len(params) != 3: if len(params) != 2:
cnsl_message("help") cnsl_message("help")
else: else:
if params[1] in ["freeleech", "user"]: if str(params[1]).isdigit():
if str(params[2]).isdigit():
pass; pass;
else: else:
cnsl_message("help") cnsl_message("help")
else:
cnsl_message("help")
def start_log(): def start_log():
log = open(curdir + log_file, 'w') log = open(curdir + log_file, 'w')
log.write('================================================================================\n= Скрипт скачки раздела NNM-Club для Linux и Unix-like ОС. =\n= Версия: %s =\n================================================================================\n' % version) log.write(umessages.m000 % version)
log.close() log.close()
def set_username(): def set_username():
if sys.argv[1] == "freeleech": return (uconfig.username, uconfig.password)
username = "freeleech_user"
password = "freeleech_password"
else:
username = "username"
password = "password"
return (username, password)
def connect(): def connect():
cookiefile = open(cookie[1], 'r+w') cookiefile = open(cookie[1], 'r+w')
write_to_log("[I] #100: пытаемся войти на " + domain + "...\n") write_to_log(umessages.m025 % domain)
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 ) 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 cookie_txt.find("Cookie") != -1:
# If it's -1, then there is no cookie! # If it's -1, then there is no cookie!
@ -153,7 +130,7 @@ def iteratorium(links_array, offset):
Links array -- array with links to topics containing torrents. Offset -- load parameter (for pages) Links array -- array with links to topics containing torrents. Offset -- load parameter (for pages)
""" """
cnsl_message("pages", str((offset / 50) + 1)) 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') forum_page = commands.getoutput('curl -b ' + cookie[1] + ' "' + domain + '/forum/viewforum.php?f=' + sys.argv[1] + '&start=' + str(offset) + '" | iconv -f cp1251 -t utf-8')
for line in forum_page.splitlines(): for line in forum_page.splitlines():
if 'viewtopic.php' in line: if 'viewtopic.php' in line:
if 'DL:' in line: if 'DL:' in line:
@ -181,7 +158,7 @@ def download():
""" """
# Firstly, we need to download forum's startpage # Firstly, we need to download forum's startpage
cnsl_message("parsing_started") 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') startpage = commands.getoutput('curl -b ' + cookie[1] + ' ' + domain + '/forum/viewforum.php?f=' + sys.argv[1] + ' | iconv -f cp1251 -t utf-8')
# Finding forum name :D # Finding forum name :D
for line in startpage.splitlines(): for line in startpage.splitlines():
if 'maintitle' in line: if 'maintitle' in line:
@ -192,7 +169,7 @@ def download():
topics = iteratorium([], 0) topics = iteratorium([], 0)
cnsl_message("found_topics", str(len(topics))) cnsl_message("found_topics", str(len(topics)))
# Aaaand... the first real actions is here # Aaaand... the first real actions is here
torrentsdir = "%s/%s" % (curdir, sys.argv[2]) torrentsdir = "%s/%s" % (curdir, sys.argv[1])
os.mkdir(torrentsdir) os.mkdir(torrentsdir)
get_torrent(topics, torrentsdir, forumname) get_torrent(topics, torrentsdir, forumname)
@ -202,17 +179,27 @@ def parse_topic(topic, forumname, topics, torrents_count, torrentsdir):
if 'maintitle' in line: if 'maintitle' in line:
tname_raw = line tname_raw = line
topicname = tname_raw.split('>')[3].split('<')[0] topicname = tname_raw.split('>')[3].split('<')[0]
for line in topicpage.splitlines():
if 'не проверено модератором!' in line:
cnsl_message("skip_non_approved", str(torrents_count), str(len(topics)), topicname, (domain + "/" + topic))
return 340
for line in topicpage.splitlines():
if 'проверено модератором' in line:
tdate_raw = line
# fix for russian months names (dateutil doesn't parse it, damnnt)
tdate_raw = tdate_raw.replace('Янв', '01').replace('Фев', '02').replace('Мар', '03').replace('Апр', '04').replace('Май', '05').replace('Июн', '06').replace('Июл', '07').replace('Авг', '08').replace('Сен', '09').replace('Окт', '10').replace('Ноя', '11').replace('Дек', '12')
topicdate = dateutil.parser.parse(tdate_raw.split('>')[1].split('<')[0].split(' ',3)[3]).strftime("%Y-%m-%d")
for line in topicpage.splitlines(): for line in topicpage.splitlines():
if 'download.php' in line: if 'download.php' in line:
for lvl2 in line.split('>'): for lvl2 in line.split('>'):
if 'download.php' in lvl2: if 'download.php' in lvl2:
dl_raw = lvl2 dl_raw = lvl2
downlink = dl_raw.split('"')[1] downlink = dl_raw.split('"')[1]
cnsl_message("downloading_torrent", str(torrents_count), str(len(topics)), topicname, (domain + "/" + topic)) cnsl_message("downloading_torrent", str(torrents_count), str(len(topics)), topicname, (domain + "/" + topic), topicdate)
os.chdir(torrentsdir) os.chdir(torrentsdir)
if not os.path.exists("./%i00" % (torrents_count / 100)): if not os.path.exists("./%s" % topicdate):
os.makedirs("./%i00" % (torrents_count / 100)) os.makedirs("./%s" % topicdate)
os.chdir("./%i00" % (torrents_count / 100)) os.chdir("./%s" % topicdate)
download_torrent(downlink) download_torrent(downlink)
def download_torrent(downlink): def download_torrent(downlink):