Merge branch 'dz0ny-master' into develop

This commit is contained in:
Ruud
2012-07-05 23:16:39 +02:00
16 changed files with 764 additions and 2166 deletions
+156 -24
View File
@@ -1,51 +1,183 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from base64 import b64encode
from couchpotato.core.downloaders.base import Downloader
from couchpotato.core.helpers.encoding import isInt
from couchpotato.core.logger import CPLog
from libs import transmissionrpc
log = CPLog(__name__)
import urllib2
import httplib
import json
import re
class TransmissionRPC(object):
"""TransmissionRPC lite library"""
log = CPLog(__name__)
def __init__(
self,
host='localhost',
port=9091,
username=None,
password=None,
):
super(TransmissionRPC, self).__init__()
self.url = 'http://' + host + ':' + str(port) \
+ '/transmission/rpc'
self.tag = 0
self.session_id = 0
self.session = {}
if username and password:
password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
password_manager.add_password(realm=None, uri=self.url,
user=username, passwd=password)
opener = \
urllib2.build_opener(urllib2.HTTPBasicAuthHandler(password_manager),
urllib2.HTTPDigestAuthHandler(password_manager))
opener.addheaders = [('User-agent',
'couchpotato-transmission-client/1.0')]
urllib2.install_opener(opener)
elif username or password:
self.log.debug('User or password missing, not using authentication.'
)
self.session = self.get_session()
def _request(self, ojson):
self.tag += 1
headers = {'x-transmission-session-id': str(self.session_id)}
request = urllib2.Request(self.url,
json.dumps(ojson).encode('utf-8'),
headers)
try:
open_request = urllib2.urlopen(request)
response = json.loads(open_request.read())
self.log.debug('response: '
+ str(json.dumps(response).encode('utf-8')))
if response['result'] == 'success':
self.log.debug(u'Transmission action successfull')
return response['arguments']
else:
self.log.debug('Unknown failure sending command to Transmission. Return text is: '
+ response['result'])
return False
except httplib.InvalidURL, err:
self.log.error(u'Invalid Transmission host, check your config %s'
% err)
return False
except urllib2.HTTPError, err:
if err.code == 401:
self.log.error(u'Invalid Transmission Username or Password, check your config'
)
return False
elif err.code == 409:
msg = str(err.read())
try:
self.session_id = \
re.search('X-Transmission-Session-Id:\s*(\w+)',
msg).group(1)
self.log.debug('X-Transmission-Session-Id: '
+ self.session_id)
# #resend request with the updated header
return self._request(ojson)
except:
self.log.error(u'Unable to get Transmission Session-Id %s'
% err)
else:
self.log.error(u'TransmissionRPC HTTPError: %s' % err)
except urllib2.URLError, err:
self.log.error(u'Unable to connect to Transmission %s' % err)
def get_session(self):
post_data = {'method': 'session-get', 'tag': self.tag}
return self._request(post_data)
def add_torrent_uri(self, torrent, arguments):
arguments['filename'] = torrent
post_data = {'arguments': arguments, 'method': 'torrent-add',
'tag': self.tag}
return self._request(post_data)
def add_torrent_file(self, torrent, arguments):
arguments['metainfo'] = torrent
post_data = {'arguments': arguments, 'method': 'torrent-add',
'tag': self.tag}
return self._request(post_data)
def set_torrent(self, torrent_id, arguments):
arguments['ids'] = torrent_id
post_data = {'arguments': arguments, 'method': 'torrent-set',
'tag': self.tag}
return self._request(post_data)
class Transmission(Downloader):
type = ['torrent']
type = ['torrent', 'magnet']
log = CPLog(__name__)
def download(self, data = {}, movie = {}, manual = False, filedata = None):
def download(
self,
data,
movie,
manual=False,
filedata=None,
):
if self.isDisabled(manual) or not self.isCorrectType(data.get('type')):
print data
if self.isDisabled(manual) \
or not self.isCorrectType(data.get('type')):
return
log.info('Sending "%s" to Transmission.', data.get('name'))
self.log.debug('Sending "%s" to Transmission.', data.get('name'))
self.log.debug('Type "%s" to Transmission.', data.get('type'))
# Load host from config and split out port.
host = self.conf('host').split(':')
if not isInt(host[1]):
log.error("Config properties are not filled in correctly, port is missing.")
self.log.error('Config properties are not filled in correctly, port is missing.'
)
return False
# Set parameters for Transmission
params = {
'paused': self.conf('paused', default = 0),
'download_dir': self.conf('directory', default = None)
}
params = {'paused': self.conf('paused', default=0),
'download-dir': self.conf('directory', default=None)}
torrent_params = {'seedRatioLimit': self.conf('ratio'),
'seedRatioMode': (0 if self.conf('ratio'
) else 1)}
if not filedata or data.get('type') != 'magnet':
self.log.error('Failed sending torrent, no data')
# Send request to Transmission
try:
if not filedata:
log.error('Failed sending torrent to transmission, no data')
tc = transmissionrpc.Client(host[0], port = host[1], user = self.conf('username'), password = self.conf('password'))
torrent = tc.add_torrent(b64encode(filedata), **params)
trpc = TransmissionRPC(host[0], port=host[1],
username=self.conf('username'),
password=self.conf('password'))
if data.get('type') == 'magnet' or data.get('magnet') \
!= None:
remote_torrent = trpc.add_torrent_uri(data.get('magnet'),
arguments=params)
else:
remote_torrent = \
trpc.add_torrent_file(b64encode(filedata),
arguments=params)
# Change settings of added torrents
try:
torrent.seed_ratio_limit = self.conf('ratio')
torrent.seed_ratio_mode = 'single' if self.conf('ratio') else 'global'
except transmissionrpc.TransmissionError, e:
log.error('Failed to change settings for transfer in transmission: %s', e)
trpc.set_torrent(remote_torrent['torrent-added']['hashString'
], torrent_params)
return True
except transmissionrpc.TransmissionError, e:
log.error('Failed to send link to transmission: %s', e)
except Exception, err:
self.log.error('Failed to change settings for transfer: %s', err)
return False
+22
View File
@@ -3,6 +3,9 @@ from couchpotato.core.logger import CPLog
from couchpotato.core.plugins.base import Plugin
from couchpotato.environment import Env
from urlparse import urlparse
from urllib import quote_plus
from couchpotato.core.helpers.encoding import simplifyString
import re
import time
@@ -105,5 +108,24 @@ class YarrProvider(Provider):
return [self.cat_backup_id]
def imdb_match(self, url, imdb_id):
""" Searches for imdb_id in url of webpage """
log.info('Finding if imbd_id(%s) is found in url: %s' % (imdb_id, url))
try:
data = self.urlopen(url)
except:
log.error('Failed to open %s.' % url)
return False
imdb_id_alt = re.sub('tt[0]*', 'tt', imdb_id)
data = unicode(data, errors='ignore')
if 'imdb.com/title/' + imdb_id in data or 'imdb.com/title/' \
+ imdb_id_alt in data:
return True
return False
def for_search(self, string):
""" Prepare string for search, removing all characters that might confuse search engine"""
return quote_plus(simplifyString(string))
def found(self, new):
log.info('Found: score(%(score)s) on %(provider)s: %(name)s', new)
@@ -0,0 +1,23 @@
from .main import PublicHD
def start():
return PublicHD()
config = [{
'name': 'publichd',
'groups': [
{
'tab': 'searcher',
'subtab': 'providers',
'name': 'PublicHD',
'description': 'Public Torrent site with only HD content.',
'options': [
{
'name': 'enabled',
'type': 'enabler',
'default': False,
},
],
},
],
}]
@@ -0,0 +1,116 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from bs4 import BeautifulSoup
from couchpotato.core.event import fireEvent
from couchpotato.core.helpers.variable import getTitle
from couchpotato.core.logger import CPLog
from couchpotato.core.providers.torrent.base import TorrentProvider
from urlparse import parse_qs
import re
class PublicHD(TorrentProvider):
log = CPLog(__name__)
urls = {
'test': 'http://publichd.eu',
'download': 'http://publichd.eu/%s',
'detail': 'http://publichd.eu/index.php?page=torrent-details&id=%s',
'search': 'http://publichd.eu/index.php?page=torrents&search=%s&active=1&category=%d',
}
cat_ids = [([2], ['720p']), ([5], ['1080p']), ([16], ['brrip']),
([16], ['bd50'])]
cat_backup_id = 0
def search(self, movie, quality):
results = []
if self.isDisabled() and quality['hd'] != True:
return results
cache_key = 'publichd.%s.%s' % (movie['library']['identifier'],
quality.get('identifier'))
search_url = self.urls['search'] \
% (self.for_search(getTitle(movie['library'])
+ ' ' + quality['identifier']),
self.getCatId(quality['identifier'])[0])
self.log.info('searchUrl: %s', search_url)
data = self.getCache(cache_key, search_url)
if not data:
self.log.error('Failed to get data from %s.', search_url)
return results
try:
soup = BeautifulSoup(data)
results_table = soup.find('table',
attrs={'id': 'bgtorrlist2'})
entries = results_table.find_all('tr')
for result in entries[2:len(entries) - 1]:
info_url = result.find(href=re.compile('torrent-details'
))
download = result.find(href=re.compile('\.torrent'))
if info_url and download:
new = {
'type': 'torrent',
'check_nzb': False,
'description': '',
'provider': self.getName(),
}
self.log.debug('Name: %s', result.find_all('td'
)[1].string)
self.log.debug('Seeders: %s', result.find_all('td'
)[4].string)
self.log.debug('Leaches: %s', result.find_all('td'
)[5].string)
self.log.debug('Size: %s', result.find_all('td'
)[7].string)
url = parse_qs(info_url['href'])
new['name'] = info_url.string
new['id'] = url['id'][0]
new['url'] = self.urls['download'] % download['href'
]
new['size'] = self.parseSize(result.find_all('td'
)[7].string)
new['seeders'] = int(result.find_all('td'
)[4].string)
new['leechers'] = int(result.find_all('td'
)[5].string)
new['score'] = fireEvent('score.calculate', new,
movie, single=True)
is_imdb = self.imdb_match(self.urls['detail']
% new['id'], movie['library']['identifier'])
is_correct_movie = fireEvent(
'searcher.correct_movie',
nzb=new,
movie=movie,
quality=quality,
imdb_results=is_imdb,
single_category=False,
single=True,
)
if is_correct_movie:
new['download'] = self.download
results.append(new)
self.found(new)
return results
except Exception, err:
self.log.debug(err)
self.log.info('Error occured during parsing! Passing only processed entries'
)
return results
def download(self, url='', nzb_id=''):
self.log.info('Downloading: %s', url)
torrent = self.urlopen(url)
return torrent
@@ -1,6 +1,26 @@
from .main import ThePirateBay
#!/usr/bin/python
# -*- coding: utf-8 -*-
from main import ThePirateBay
from main import TPBProxy
def start():
return ThePirateBay()
config = []
config = [{'name': 'ThePirateBay', 'groups': [{
'tab': 'searcher',
'subtab': 'providers',
'name': 'ThePirateBay',
'description': 'The world\'s largest bittorrent tracker.',
'options': [{'name': 'enabled', 'type': 'enabler',
'default': False}, {
'name': 'domain_for_tpb',
'label': 'Proxy server',
'default': 'http://thepiratebay.se',
'description': 'Default domain for requests',
'type': 'dropdown',
'values': TPBProxy.list,
}],
}]}]
@@ -1,146 +1,189 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from bs4 import BeautifulSoup
from couchpotato.core.event import fireEvent
from couchpotato.core.helpers.variable import getTitle
from couchpotato.core.logger import CPLog
from couchpotato.core.providers.torrent.base import TorrentProvider
from random import sample as random
from urlparse import urlparse
log = CPLog(__name__)
import re
import time
class TPBProxy(object):
""" TPBProxy deals with failed or blocked TPB proxys.
It works as round-robin balancer, if user seleced
or default domain becomes unavaliable.
"""
list = [
('(Sweden) thepiratebay.se', 'http://thepiratebay.se'),
('(Sweden) tpb.ipredator.se (ssl)', 'https://tpb.ipredator.se'),
('(Germany) depiraatbaai.be', 'http://depiraatbaai.be'),
('(UK) piratereverse.info (ssl)', 'https://piratereverse.info'),
('(UK) tpb.pirateparty.org.uk (ssl)', 'https://tpb.pirateparty.org.uk'),
('(Netherlands) argumentomteemigreren.nl', 'http://argumentomteemigreren.nl'),
('(direct) 194.71.107.80', 'http://194.71.107.80'),
('(direct) 194.71.107.81', 'http://194.71.107.81'),
('(direct) 194.71.107.82', 'http://194.71.107.82'),
('(direct) 194.71.107.83', 'http://194.71.107.83'),
]
@staticmethod
def get_proxy(http_failed_disabled=None, current=None):
# compare lists and user/default value, exclude filter
unused = [item for item in TPBProxy.list if item not in http_failed_disabled and current
not in item]
if len(unused) > 0:
# only return uri
return random(unused, 1)[0][1]
else:
# this should disable provider for some time
raise Exception('All ThePirateBay proxies are exhausted')
class ThePirateBay(TorrentProvider):
urls = {
'download': 'http://torrents.depiraatbaai.be/%s/%s.torrent',
'nfo': 'https://depiraatbaai.be/torrent/%s',
'detail': 'https://depiraatbaai.be/torrent/%s',
'search': 'https://depiraatbaai.be/search/%s/0/7/%d',
}
log = CPLog(__name__)
cat_ids = [([207], ['720p', '1080p']), ([201], [
'cam',
'ts',
'dvdrip',
'tc',
'r5',
'scr',
'brrip',
]), ([202], ['dvdr'])]
cat_ids = [
([207], ['720p', '1080p']),
([200], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr', 'brrip']),
([202], ['dvdr'])
]
cat_backup_id = 200
ignore_string = {
'720p': ' -brrip -bdrip',
'1080p': ' -brrip -bdrip'
}
disable_provider = False
def __init__(self):
pass
super(ThePirateBay, self).__init__()
self.urls = {'test': self.api_domain(), 'detail': '%s/torrent/%s',
'search': '%s/search/%s/0/7/%d'}
def find(self, movie, quality, type):
def api_domain(self, url=''):
# default domain
domain = self.conf('domain_for_tpb', default='http://thepiratebay.se')
self.log.info('Selected domain for this request: %s', domain)
host = urlparse(domain).hostname
# Clear disabled list for default or user selected host if time expired
if self.http_failed_disabled.get(host, 0) > 0:
if self.http_failed_disabled[host] > time.time() - 900:
# get new random domain
try:
domain = TPBProxy.get_proxy(self.http_failed_disabled, domain)
except Exception, err:
self.disable_provider = True
self.log.error(err)
else:
del self.http_failed_request[host]
del self.http_failed_disabled[host]
return domain + url
def search(self, movie, quality):
results = []
if not self.enabled():
if self.isDisabled() or self.disable_provider:
return results
url = self.apiUrl % (quote_plus(self.toSearchString(movie.name + ' ' + quality) + self.makeIgnoreString(type)), self.getCatId(type))
cache_key = 'thepiratebay.%s.%s' % (movie['library']['identifier'], quality.get('identifier'
))
search_url = self.urls['search'] % (self.api_domain(),
self.for_search(getTitle(movie['library']) + ' '
+ quality['identifier']),
self.getCatId(quality['identifier'])[0])
self.log.info('searchUrl: %s', search_url)
data = self.getCache(cache_key, search_url)
log.info('Searching: %s', url)
# print data
data = self.urlopen(url)
if not data:
log.error('Failed to get data from %s.', url)
self.log.error('Failed to get data from %s.', search_url)
return results
try:
tables = SoupStrainer('table')
html = BeautifulSoup(data, parseOnlyThese = tables)
resultTable = html.find('table', attrs = {'id':'searchResult'})
for result in resultTable.findAll('tr'):
details = result.find('a', attrs = {'class':'detLink'})
if details:
href = re.search('/(?P<id>\d+)/', details['href'])
id = href.group('id')
name = self.toSaveString(details.contents[0])
desc = result.find('font', attrs = {'class':'detDesc'}).contents[0].split(',')
date = ''
size = 0
for item in desc:
# Weird date stuff
if 'uploaded' in item.lower():
date = item.replace('Uploaded', '')
date = date.replace('Today', '')
soup = BeautifulSoup(data)
results_table = soup.find('table', attrs={'id': 'searchResult'})
entries = results_table.find_all('tr')
for result in entries[1:]:
link = result.find(href=re.compile('torrent\/\d+\/'))
download = result.find(href=re.compile('magnet:'))
# Do something with yesterday
yesterdayMinus = 0
if 'Y-day' in date:
date = date.replace('Y-day', '')
yesterdayMinus = 86400
# Uploaded 06-28 02:27, Size 1.37 GiB,
datestring = date.replace('&nbsp;', ' ').strip()
date = int(time.mktime(parse(datestring).timetuple())) - yesterdayMinus
# size
elif 'size' in item.lower():
size = item.replace('Size', '')
size = re.search('Size (?P<size>.+),', unicode(result.select('font.detDesc'
)[0])).group('size')
if link and download:
new = {
'type': 'magnet',
'check_nzb': False,
'description': '',
'provider': self.getName(),
}
seedleech = []
for td in result.findAll('td'):
try:
seedleech.append(int(td.contents[0]))
except ValueError:
pass
trusted = (0, 10)[result.find('img', alt=re.compile('Trusted')) != None]
vip = (0, 20)[result.find('img', alt=re.compile('VIP')) != None]
confirmed = (0, 30)[result.find('img', alt=re.compile('Helpers')) != None]
moderated = (0, 50)[result.find('img', alt=re.compile('Moderator')) != None]
is_imdb = self.imdb_match(self.api_domain(link['href']), movie['library'
]['identifier'])
seeders = 0
leechers = 0
if len(seedleech) == 2 and seedleech[0] > 0 and seedleech[1] > 0:
seeders = seedleech[0]
leechers = seedleech[1]
self.log.info('Name: %s', link.string)
self.log.info('Seeders: %s', result.find_all('td')[2].string)
self.log.info('Leechers: %s', result.find_all('td')[3].string)
self.log.info('Size: %s', size)
self.log.info('Score(trusted + vip + moderated): %d', confirmed + trusted + vip
+ moderated)
# to item
new = self.feedItem()
new.id = id
new.type = 'torrent'
new.name = name
new.date = date
new.size = self.parseSize(size)
new.seeders = seeders
new.leechers = leechers
new.url = self.downloadLink(id, name)
new.score = self.calcScore(new, movie) + self.uploader(result) + (seeders / 10)
new['name'] = link.string
new['id'] = re.search('/(?P<id>\d+)/', link['href']).group('id')
new['url'] = self.api_domain(link['href'])
new['magnet'] = download['href']
new['size'] = self.parseSize(size)
new['seeders'] = int(result.find_all('td')[2].string)
new['leechers'] = int(result.find_all('td')[3].string)
new['extra_score'] = lambda x: confirmed + trusted + vip + moderated
new['score'] = fireEvent('score.calculate', new, movie, single=True)
if seeders > 0 and (new.date + (int(self.conf('wait')) * 60 * 60) < time.time()) and Qualities.types.get(type).get('minSize') <= new.size:
new.detailUrl = self.detailLink(id)
new.content = self.getInfo(new.detailUrl)
if self.isCorrectMovie(new, movie, type):
results.append(new)
log.info('Found: %s', new.name)
is_correct_movie = fireEvent(
'searcher.correct_movie',
nzb=new,
movie=movie,
quality=quality,
imdb_results=is_imdb,
single_category=False,
single=True,
)
if is_correct_movie:
results.append(new)
self.found(new)
return results
except Exception, error:
self.log.debug(error)
return results
except AttributeError:
log.debug('No search results found.')
return []
def makeIgnoreString(self, type):
ignore = self.ignoreString.get(type)
return ignore if ignore else ''
def uploader(self, html):
score = 0
if html.find('img', attr = {'alt':'VIP'}):
score += 3
if html.find('img', attr = {'alt':'Trusted'}):
score += 1
return score
def getInfo(self, url):
log.debug('Getting info: %s', url)
data = self.urlopen(url)
if not data:
log.error('Failed to get data from %s.', url)
return ''
div = SoupStrainer('div')
html = BeautifulSoup(data, parseOnlyThese = div)
html = html.find('div', attrs = {'class':'nfo'})
return str(html).decode("utf-8", "replace")
def downloadLink(self, id, name):
return self.downloadUrl % (id, quote_plus(name))
def isEnabled(self):
return self.conf('enabled') and TorrentProvider.isEnabled(self)
def download(self, url='', nzb_id=''):
return url