Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
werwolfby committed Dec 20, 2016
2 parents 174dbb7 + ff0179c commit d45eee0
Show file tree
Hide file tree
Showing 53 changed files with 10,954 additions and 3,533 deletions.
2 changes: 1 addition & 1 deletion MonitorrentInstaller/MonitorrentInstaller/Product.wxs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="Monitorrent" Language="1033" Version="1.1.0.30" Manufacturer="Monitorrent Team" UpgradeCode="dd4cf505-1e44-4311-a8f2-efcf097175a7">
<Product Id="*" Name="Monitorrent" Language="1033" Version="1.1.0.40" Manufacturer="Monitorrent Team" UpgradeCode="dd4cf505-1e44-4311-a8f2-efcf097175a7">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." AllowSameVersionUpgrades="yes"/>
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,18 @@
[![Build status](https://ci.appveyor.com/api/projects/status/emt2y0jcya73lxj3?svg=true)](https://ci.appveyor.com/project/werwolfby/monitorrent)
[![Coverage Status](https://coveralls.io/repos/werwolfby/monitorrent/badge.svg?branch=develop&service=github)](https://coveralls.io/github/werwolfby/monitorrent?branch=develop)
[![codecov.io](https://codecov.io/github/werwolfby/monitorrent/coverage.svg?branch=develop)](https://codecov.io/github/werwolfby/monitorrent?branch=develop)

Join discussion at:

[![Join the chat at https://gitter.im/werwolfby/monitorrent](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/werwolfby/monitorrent?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

## Support on Beerpay
Hey dude! Help me out for a couple of :beers:!

[![Yandex.Money](https://img.shields.io/badge/-%D0%BF%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%B0%D1%82%D1%8C-dfb317.svg?style=flat&colorA=ffffff&logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAAnFBMVEUAAAD%2F%2FwD%2FzAD%2FvyD%2FzBr%2FyBL%2FxRf2wRL6wxb6wxT7xhXuuRHmtAvvvBD7xRPuuw%2FvvBD6xRX7xhT6xRT5xBT3wRL6xRT6xRT5wxP6xRT5xRP6xRRgTAl7YQukgQ6phQ7XqwXYqwbYrAbZrAbgsgniswrltQrotwzrug7sug7tuw%2FuvA%2FvvQ%2F2whL2wxL4wxP4xBP5xBP5xRP6xRQWtWWMAAAAHHRSTlMAAQUICg4WHS8zSElcb3eKlJWyytTd7e7v%2BPv8BVgXXQAAAGNJREFUeAFdx1USwyAYBkDqpe4uRIj7d%2F%2B75R%2BGDCT7tkybHJllcX5aW91gOtq9YTo7AabjC%2BX3eW3WfErdgnz%2FjQjBqQcog9a95r7d2BNO1LWQrkxKqO6RBlkFojq%2FPu7akrWLthGpa2oo%2BAAAAABJRU5ErkJggg%3D%3D)](https://money.yandex.ru/to/410012638435097)
[![Beerpay](https://beerpay.io/werwolfby/monitorrent/badge.svg?style=beer)](https://beerpay.io/werwolfby/monitorrent)
[![Beerpay](https://beerpay.io/werwolfby/monitorrent/make-wish.svg?style=flat)](https://beerpay.io/werwolfby/monitorrent)

This app can watch for torrent updates

### Supported trackers:
Expand Down
2 changes: 1 addition & 1 deletion monitorrent/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.1.0-rc.2'
__version__ = '1.1.0-rc.4'
9 changes: 7 additions & 2 deletions monitorrent/plugins/clients/qbittorrent.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from __future__ import unicode_literals

import json
import six

import requests
from io import BytesIO
Expand All @@ -13,6 +14,7 @@

from monitorrent.db import Base, DBSession
from monitorrent.plugin_managers import register_plugin
from datetime import datetime
import dateutil.parser


Expand Down Expand Up @@ -117,15 +119,18 @@ def find_torrent(self, torrent_hash):
time = torrent.get('added_on', None)
result_date = None
if time is not None:
result_date = dateutil.parser.parse(time).replace(tzinfo=reference.LocalTimezone()).astimezone(utc)
if isinstance(time, six.string_types):
result_date = dateutil.parser.parse(time).replace(tzinfo=reference.LocalTimezone())\
.astimezone(utc)
else:
result_date = datetime.fromtimestamp(time, utc)
return {
"name": torrent['name'],
"date_added": result_date
}
except Exception as e:
return False

#TODO save path support?
def add_torrent(self, torrent, torrent_settings):
"""
:type torrent_settings: clients.TopicSettings | None
Expand Down
6 changes: 3 additions & 3 deletions monitorrent/plugins/trackers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,11 @@ def execute(self, topics, engine):
try:
engine.log.info(u"Check for changes <b>%s</b>" % topic_name)
prepared_request = self._prepare_request(topic)
download_kwargs = {}
download_kwargs = dict(self.tracker_settings.get_requests_kwargs())
if isinstance(prepared_request, tuple) and len(prepared_request) >= 2:
download_kwargs = prepared_request[1] or download_kwargs
if prepared_request[1] is not None:
download_kwargs.update(prepared_request[1])
prepared_request = prepared_request[0]
download_kwargs.setdefault('timeout', self.tracker_settings.requests_timeout)
response, filename = download(prepared_request, **download_kwargs)
if hasattr(self, 'check_download'):
status = self.check_download(response)
Expand Down
234 changes: 234 additions & 0 deletions monitorrent/plugins/trackers/anidub.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
import requests
from requests import Session
from sqlalchemy import Column, String, ForeignKey, Integer
from monitorrent.db import Base, DBSession
from monitorrent.plugin_managers import register_plugin
from monitorrent.plugins import Topic
from monitorrent.plugins.trackers import WithCredentialsMixin, ExecuteWithHashChangeMixin, TrackerPluginBase, \
LoginResult
from monitorrent.utils.soup import get_soup

PLUGIN_NAME = 'anidub.com'


class AnidubCredentials(Base):
__tablename__ = "anidub_credentials"

username = Column(String, primary_key=True)
password = Column(String, primary_key=True)
dle_uid = Column(String, nullable=True)
dle_pwd = Column(String, nullable=True)


class AnidubTopic(Topic):
__tablename__ = "anidub_topics"

id = Column(Integer, ForeignKey('topics.id'), primary_key=True)
hash = Column(String, nullable=True)
format = Column(String, nullable=False)
format_list = Column(String, nullable=False)

__mapper_args__ = {
'polymorphic_identity': PLUGIN_NAME
}


class AnidubLoginFailedException(Exception):
def __init__(self, code, message):
self.code = code
self.message = message


class AnidubTracker(object):
tracker_settings = None
_regex = re.compile(u'^http://tr\.*anidub.com/.*/\d+-.*\.html$')
root_url = "http://tr.anidub.com"

def __init__(self, dle_uid=None, dle_pwd=None):
self.dle_uid = dle_uid
self.dle_pwd = dle_pwd

def setup(self, dle_uid, dle_pwd):
self.dle_uid = dle_uid
self.dle_pwd = dle_pwd

def can_parse_url(self, url):
return self._regex.match(url) is not None

def parse_url(self, url):
match = self._regex.match(url)
if match is None:
return None

r = requests.get(url, allow_redirects=False, **self.tracker_settings.get_requests_kwargs())
# tr.anidub.com doesn't return encoding in content-type
r.encoding = 'utf-8'
soup = get_soup(r.text)
title = soup.find('span', id='news-title')
if title is None:
return None
title = title.text.strip()
result = {'original_name': title}
# Format
format_list = []
flist = self._find_format_list(soup)
for q in flist:
format_list.append(q.text.strip())
result['format_list'] = format_list
return result

def login(self, username, password):
s = Session()
data = {"login_name": username, "login_password": password, "login": "submit"}
login_result = s.post(self.root_url, data, **self.tracker_settings.get_requests_kwargs())
# tr.anidub.com doesn't return encoding in content-type
login_result.encoding = 'utf-8'
if not self._is_logged_in(login_result.text):
raise AnidubLoginFailedException(1, "Invalid login or password")
else:
dle_uid = s.cookies.get('dle_user_id')
dle_pwd = s.cookies.get('dle_password')
if not dle_uid or not dle_pwd:
raise AnidubLoginFailedException(2, "Failed to retrieve cookies")
self.dle_uid = dle_uid
self.dle_pwd = dle_pwd

def get_cookies(self):
if not self.dle_uid or not self.dle_pwd:
return False
return {'dle_user_id': self.dle_uid, 'dle_password': self.dle_pwd}

def verify(self):
cookies = self.get_cookies()
if not cookies:
return False
r = requests.get(self.root_url, cookies=cookies, **self.tracker_settings.get_requests_kwargs())
return self._is_logged_in(r.text)

def get_download_url(self, url, vformat):
cookies = self.get_cookies()
page = requests.get(url, cookies=cookies, **self.tracker_settings.get_requests_kwargs())
page_soup = get_soup(page.content)
flist = self._find_format_list(page_soup)
for f in flist:
if f.text.strip() == vformat:
href = f['href'][1:]
at = page_soup.select_one('div[class="torrent"] div#'+href+' a')
return self.root_url + at['href']
return None

@staticmethod
def _find_format_list(soup):
return soup.select('div#tabs ul[class="lcol"] a')

def _is_logged_in(self, page):
return "a href=\""+self.root_url+"/index.php?action=logout\"" in page


class AnidubPlugin(WithCredentialsMixin, ExecuteWithHashChangeMixin, TrackerPluginBase):
tracker = AnidubTracker()
topic_class = AnidubTopic
credentials_class = AnidubCredentials
topic_public_fields = ['id', 'url', 'last_update', 'display_name', 'status', 'format', 'format_list']
topic_private_fields = ['display_name', 'format', 'format_list']
topic_form = [{
'type': 'row',
'content': [{
'type': 'text',
'model': 'display_name',
'label': 'Name',
'flex': 70
}, {
'type': 'select',
'model': 'format',
'label': 'Format',
'options': [],
'flex': 30
}]
}]

def login(self):
with DBSession() as db:
cred = db.query(self.credentials_class).first()
if not cred:
return LoginResult.CredentialsNotSpecified
username = cred.username
password = cred.password
if not username or not password:
return LoginResult.CredentialsNotSpecified

try:
self.tracker.login(username, password)
with DBSession() as db:
cred = db.query(self.credentials_class).first()
cred.dle_uid = self.tracker.dle_uid
cred.dle_pwd = self.tracker.dle_pwd
return LoginResult.Ok
except AnidubLoginFailedException as e:
if e.code == 1:
return LoginResult.IncorrentLoginPassword
return LoginResult.Unknown
except Exception as e:
return LoginResult.Unknown

def verify(self):
with DBSession() as db:
cred = db.query(self.credentials_class).first()
if not cred:
return False
username = cred.username
password = cred.password
if not username or not password or not cred.dle_uid or not cred.dle_pwd:
return False
self.tracker.setup(cred.dle_uid, cred.dle_pwd)
return self.tracker.verify()

def can_parse_url(self, url):
return self.tracker.can_parse_url(url)

def parse_url(self, url):
return self.tracker.parse_url(url)

def get_topic(self, id):
result = super(AnidubPlugin, self).get_topic(id)
if result is None:
return None
# format list
self.topic_form[0]['content'][1]['options'] = result['format_list'].split(',')
return result

def prepare_add_topic(self, url):
parsed_url = self.tracker.parse_url(url)
if not parsed_url:
return None
# format list
self.topic_form[0]['content'][1]['options'] = parsed_url['format_list']
settings = {
'display_name': parsed_url['original_name'],
'format': parsed_url['format_list'][0]
}
return settings

def _set_topic_params(self, url, parsed_url, topic, params):
"""
:param url: str
:type topic: AnidubTopic
"""
super(AnidubPlugin, self)._set_topic_params(url, parsed_url, topic, params)
if parsed_url is not None:
topic.format_list = ",".join(parsed_url['format_list'])

def _prepare_request(self, topic):
url = self.tracker.get_download_url(topic.url, topic.format)
if url is None:
return None
headers = {'referer': topic.url}
cookies = self.tracker.get_cookies()
request = requests.Request('GET', url, cookies=cookies, headers=headers)
return request.prepare()


register_plugin('tracker', PLUGIN_NAME, AnidubPlugin())
6 changes: 3 additions & 3 deletions monitorrent/plugins/trackers/nnmclub.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ class NnmClubTracker(object):
tracker_settings = None
tracker_domains = [u'nnmclub.to']
title_headers = [u'torrent :: nnm-club']
_login_url = u'http://nnmclub.to/forum/login.php'
_profile_page = u"http://nnmclub.to/forum/profile.php?mode=viewprofile&u={}"
_login_url = u'https://nnmclub.to/forum/login.php'
_profile_page = u"https://nnmclub.to/forum/profile.php?mode=viewprofile&u={}"

def __init__(self, user_id=None, sid=None):
self.user_id = user_id
Expand Down Expand Up @@ -122,7 +122,7 @@ def get_download_url(self, url):
# not a free torrent
if len(da) == 0:
return None
download_url = 'http://' + self.tracker_domains[0] + '/forum/' + da[0].attrs['href']
download_url = 'https://' + self.tracker_domains[0] + '/forum/' + da[0].attrs['href']
return download_url

def get_url(self, url):
Expand Down
18 changes: 8 additions & 10 deletions monitorrent/plugins/trackers/rutracker.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from builtins import object
import re
from requests import Session
import requests
Expand All @@ -9,7 +8,6 @@
from monitorrent.plugins import Topic
from monitorrent.plugin_managers import register_plugin
from monitorrent.utils.soup import get_soup
from monitorrent.utils.bittorrent import Torrent
from monitorrent.plugins.trackers import TrackerPluginBase, WithCredentialsMixin, ExecuteWithHashChangeMixin, LoginResult

PLUGIN_NAME = 'rutracker.org'
Expand Down Expand Up @@ -43,9 +41,9 @@ def __init__(self, code, message):

class RutrackerTracker(object):
tracker_settings = None
login_url = "http://rutracker.org/forum/login.php"
profile_page = "http://rutracker.org/forum/profile.php?mode=viewprofile&u={}"
_regex = re.compile(u'^http://w*\.*rutracker.org/forum/viewtopic.php\?t=(\d+)(/.*)?$')
login_url = "https://rutracker.org/forum/login.php"
profile_page = "https://rutracker.org/forum/privmsg.php?folder=inbox"
_regex = re.compile(u'^https?://w*\.*rutracker.org/forum/viewtopic.php\?t=(\d+)(/.*)?$')
uid_regex = re.compile(u'\d*-(\d*)-.*')

def __init__(self, uid=None, bb_data=None):
Expand Down Expand Up @@ -98,10 +96,9 @@ def verify(self):
cookies = self.get_cookies()
if not cookies:
return False
profile_page_url = self.profile_page.format(self.uid)
profile_page_result = requests.get(profile_page_url, cookies=cookies,
profile_page_result = requests.get(self.profile_page, cookies=cookies,
**self.tracker_settings.get_requests_kwargs())
return profile_page_result.url == profile_page_url
return profile_page_result.url == self.profile_page

def get_cookies(self):
if not self.bb_data:
Expand All @@ -120,7 +117,8 @@ def get_download_url(self, url):
id = self.get_id(url)
if id is None:
return None
return "http://dl.rutracker.org/forum/dl.php?t=" + id

return "https://rutracker.org/forum/dl.php?t=" + id


class RutrackerPlugin(WithCredentialsMixin, ExecuteWithHashChangeMixin, TrackerPluginBase):
Expand Down Expand Up @@ -180,7 +178,7 @@ def parse_url(self, url):
return self.tracker.parse_url(url)

def _prepare_request(self, topic):
headers = {'referer': topic.url, 'host': "dl.rutracker.org"}
headers = {'referer': topic.url, 'host': "rutracker.org"}
cookies = self.tracker.get_cookies()
request = requests.Request('POST', self.tracker.get_download_url(topic.url), headers=headers, cookies=cookies)
return request.prepare()
Expand Down
Loading

0 comments on commit d45eee0

Please sign in to comment.