Merge branch 'refs/heads/develop'

This commit is contained in:
Ruud
2013-05-28 21:15:04 +02:00
12 changed files with 193 additions and 67 deletions
@@ -36,12 +36,7 @@ class Transmission(Downloader):
if len(self.conf('directory', default = '')) > 0: if len(self.conf('directory', default = '')) > 0:
folder_name = self.createFileName(data, filedata, movie)[:-len(data.get('type')) - 1] folder_name = self.createFileName(data, filedata, movie)[:-len(data.get('type')) - 1]
folder_path = os.path.join(self.conf('directory', default = ''), folder_name).rstrip(os.path.sep) params['download-dir'] = os.path.join(self.conf('directory', default = ''), folder_name).rstrip(os.path.sep)
# Create the empty folder to download too
self.makeDir(folder_path)
params['download-dir'] = folder_path
torrent_params = {} torrent_params = {}
if self.conf('ratio'): if self.conf('ratio'):
@@ -93,11 +88,13 @@ class Transmission(Downloader):
'fields': ['id', 'name', 'hashString', 'percentDone', 'status', 'eta', 'isFinished', 'downloadDir', 'uploadRatio'] 'fields': ['id', 'name', 'hashString', 'percentDone', 'status', 'eta', 'isFinished', 'downloadDir', 'uploadRatio']
} }
queue = trpc.get_alltorrents(return_params) queue = trpc.get_alltorrents(return_params)
except Exception, err: except Exception, err:
log.error('Failed getting queue: %s', err) log.error('Failed getting queue: %s', err)
return False return False
if not queue:
return []
statuses = StatusList(self) statuses = StatusList(self)
# Get torrents status # Get torrents status
+20 -38
View File
@@ -9,7 +9,6 @@ from multipartpost import MultipartPostHandler
import cookielib import cookielib
import httplib import httplib
import json import json
import os
import re import re
import time import time
import urllib import urllib
@@ -106,36 +105,6 @@ class uTorrent(Downloader):
return False return False
statuses = StatusList(self) statuses = StatusList(self)
download_folder = ''
settings_dict = {}
try:
data = self.utorrent_api.get_settings()
utorrent_settings = json.loads(data)
# Create settings dict
for item in utorrent_settings['settings']:
if item[1] == 0: # int
settings_dict[item[0]] = int(item[2] if not item[2].strip() == '' else '0')
elif item[1] == 1: # bool
settings_dict[item[0]] = True if item[2] == 'true' else False
elif item[1] == 2: # string
settings_dict[item[0]] = item[2]
log.debug('uTorrent settings: %s, %s', (settings_dict['dir_completed_download_flag'], settings_dict['dir_active_download_flag']))
# Get the download path from the uTorrent settings
if settings_dict['dir_completed_download_flag']:
download_folder = settings_dict['dir_completed_download']
elif settings_dict['dir_active_download_flag']:
download_folder = settings_dict['dir_active_download']
else:
log.info('No download folder set in uTorrent. Please set a download folder')
return False
except Exception, err:
log.error('Failed to get settings from uTorrent: %s', err)
return False
# Get torrents # Get torrents
for item in queue.get('torrents', []): for item in queue.get('torrents', []):
@@ -145,18 +114,13 @@ class uTorrent(Downloader):
if item[21] == 'Finished' or item[21] == 'Seeding': if item[21] == 'Finished' or item[21] == 'Seeding':
status = 'completed' status = 'completed'
if settings_dict['dir_add_label']:
release_folder = os.path.join(download_folder, item[11], item[2])
else:
release_folder = os.path.join(download_folder, item[2])
statuses.append({ statuses.append({
'id': item[0], 'id': item[0],
'name': item[2], 'name': item[2],
'status': status, 'status': status,
'original_status': item[1], 'original_status': item[1],
'timeleft': str(timedelta(seconds = item[10])), 'timeleft': str(timedelta(seconds = item[10])),
'folder': release_folder, 'folder': item[26],
}) })
return statuses return statuses
@@ -236,4 +200,22 @@ class uTorrentAPI(object):
def get_settings(self): def get_settings(self):
action = "action=getsettings" action = "action=getsettings"
return self._request(action) settings_dict = {}
try:
utorrent_settings = json.loads(self._request(action))
# Create settings dict
for item in utorrent_settings['settings']:
if item[1] == 0: # int
settings_dict[item[0]] = int(item[2] if not item[2].strip() == '' else '0')
elif item[1] == 1: # bool
settings_dict[item[0]] = True if item[2] == 'true' else False
elif item[1] == 2: # string
settings_dict[item[0]] = item[2]
#log.debug('uTorrent settings: %s', settings_dict)
except Exception, err:
log.error('Failed to get settings from uTorrent: %s', err)
return settings_dict
+3 -2
View File
@@ -28,6 +28,7 @@ class Plugin(object):
_needs_shutdown = False _needs_shutdown = False
user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20130519 Firefox/24.0'
http_last_use = {} http_last_use = {}
http_time_between_calls = 0 http_time_between_calls = 0
http_failed_request = {} http_failed_request = {}
@@ -106,7 +107,7 @@ class Plugin(object):
# Fill in some headers # Fill in some headers
headers['Referer'] = headers.get('Referer', urlparse(url).hostname) headers['Referer'] = headers.get('Referer', urlparse(url).hostname)
headers['Host'] = headers.get('Host', urlparse(url).hostname) headers['Host'] = headers.get('Host', urlparse(url).hostname)
headers['User-Agent'] = headers.get('User-Agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:10.0.2) Gecko/20100101 Firefox/10.0.2') headers['User-Agent'] = headers.get('User-Agent', self.user_agent)
headers['Accept-encoding'] = headers.get('Accept-encoding', 'gzip') headers['Accept-encoding'] = headers.get('Accept-encoding', 'gzip')
host = urlparse(url).hostname host = urlparse(url).hostname
@@ -138,7 +139,7 @@ class Plugin(object):
response = opener.open(request, timeout = timeout) response = opener.open(request, timeout = timeout)
else: else:
log.info('Opening url: %s, params: %s', (url, [x for x in params.iterkeys()])) log.info('Opening url: %s, params: %s', (url, [x for x in params.iterkeys()] if isinstance(params, dict) else 'with data'))
data = tryUrlencode(params) if len(params) > 0 else None data = tryUrlencode(params) if len(params) > 0 else None
request = urllib2.Request(url, data, headers) request = urllib2.Request(url, data, headers)
+1 -2
View File
@@ -8,7 +8,6 @@ from couchpotato.core.logger import CPLog
from couchpotato.core.plugins.base import Plugin from couchpotato.core.plugins.base import Plugin
from couchpotato.core.settings.model import Quality, Profile, ProfileType from couchpotato.core.settings.model import Quality, Profile, ProfileType
from sqlalchemy.sql.expression import or_ from sqlalchemy.sql.expression import or_
import os.path
import re import re
import time import time
@@ -25,7 +24,7 @@ class QualityPlugin(Plugin):
{'identifier': 'dvdr', 'size': (3000, 10000), 'label': 'DVD-R', 'alternative': [], 'allow': [], 'ext':['iso', 'img'], 'tags': ['pal', 'ntsc', 'video_ts', 'audio_ts']}, {'identifier': 'dvdr', 'size': (3000, 10000), 'label': 'DVD-R', 'alternative': [], 'allow': [], 'ext':['iso', 'img'], 'tags': ['pal', 'ntsc', 'video_ts', 'audio_ts']},
{'identifier': 'dvdrip', 'size': (600, 2400), 'label': 'DVD-Rip', 'width': 720, 'alternative': ['dvdrip'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg'], 'tags': [('dvd', 'rip'), ('dvd', 'xvid'), ('dvd', 'divx')]}, {'identifier': 'dvdrip', 'size': (600, 2400), 'label': 'DVD-Rip', 'width': 720, 'alternative': ['dvdrip'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg'], 'tags': [('dvd', 'rip'), ('dvd', 'xvid'), ('dvd', 'divx')]},
{'identifier': 'scr', 'size': (600, 1600), 'label': 'Screener', 'alternative': ['screener', 'dvdscr', 'ppvrip', 'dvdscreener'], 'allow': ['dvdr', 'dvd'], 'ext':['avi', 'mpg', 'mpeg']}, {'identifier': 'scr', 'size': (600, 1600), 'label': 'Screener', 'alternative': ['screener', 'dvdscr', 'ppvrip', 'dvdscreener'], 'allow': ['dvdr', 'dvd'], 'ext':['avi', 'mpg', 'mpeg']},
{'identifier': 'r5', 'size': (600, 1000), 'label': 'R5', 'alternative': [], 'allow': ['dvdr'], 'ext':['avi', 'mpg', 'mpeg']}, {'identifier': 'r5', 'size': (600, 1000), 'label': 'R5', 'alternative': ['r6'], 'allow': ['dvdr'], 'ext':['avi', 'mpg', 'mpeg']},
{'identifier': 'tc', 'size': (600, 1000), 'label': 'TeleCine', 'alternative': ['telecine'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']}, {'identifier': 'tc', 'size': (600, 1000), 'label': 'TeleCine', 'alternative': ['telecine'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']},
{'identifier': 'ts', 'size': (600, 1000), 'label': 'TeleSync', 'alternative': ['telesync', 'hdts'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']}, {'identifier': 'ts', 'size': (600, 1000), 'label': 'TeleSync', 'alternative': ['telesync', 'hdts'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']},
{'identifier': 'cam', 'size': (600, 1000), 'label': 'Cam', 'alternative': ['camrip', 'hdcam'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']} {'identifier': 'cam', 'size': (600, 1000), 'label': 'Cam', 'alternative': ['camrip', 'hdcam'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']}
+6 -3
View File
@@ -4,7 +4,7 @@ from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
from couchpotato.core.helpers.encoding import toUnicode, ss from couchpotato.core.helpers.encoding import toUnicode, ss
from couchpotato.core.helpers.request import getParams, jsonified from couchpotato.core.helpers.request import getParams, jsonified
from couchpotato.core.helpers.variable import getExt, mergeDicts, getTitle, \ from couchpotato.core.helpers.variable import getExt, mergeDicts, getTitle, \
getImdb, link, symlink getImdb, link, symlink, tryInt
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
from couchpotato.core.plugins.base import Plugin from couchpotato.core.plugins.base import Plugin
from couchpotato.core.settings.model import Library, File, Profile, Release, \ from couchpotato.core.settings.model import Library, File, Profile, Release, \
@@ -25,10 +25,10 @@ class Renamer(Plugin):
checking_snatched = False checking_snatched = False
def __init__(self): def __init__(self):
addApiView('renamer.scan', self.scanView, docs = { addApiView('renamer.scan', self.scanView, docs = {
'desc': 'For the renamer to check for new files to rename in a folder', 'desc': 'For the renamer to check for new files to rename in a folder',
'params': { 'params': {
'async': {'desc': 'Optional: Set to 1 if you dont want to fire the renamer.scan asynchronous.'},
'movie_folder': {'desc': 'Optional: The folder of the movie to scan. Keep empty for default renamer folder.'}, 'movie_folder': {'desc': 'Optional: The folder of the movie to scan. Keep empty for default renamer folder.'},
'downloader' : {'desc': 'Optional: The downloader this movie has been downloaded with'}, 'downloader' : {'desc': 'Optional: The downloader this movie has been downloaded with'},
'download_id': {'desc': 'Optional: The downloader\'s nzb/torrent ID'}, 'download_id': {'desc': 'Optional: The downloader\'s nzb/torrent ID'},
@@ -62,11 +62,14 @@ class Renamer(Plugin):
def scanView(self): def scanView(self):
params = getParams() params = getParams()
async = tryInt(params.get('async', None))
movie_folder = params.get('movie_folder', None) movie_folder = params.get('movie_folder', None)
downloader = params.get('downloader', None) downloader = params.get('downloader', None)
download_id = params.get('download_id', None) download_id = params.get('download_id', None)
fireEventAsync('renamer.scan', fire_handle = fireEvent if not async else fireEventAsync
fire_handle('renamer.scan',
movie_folder = movie_folder, movie_folder = movie_folder,
download_info = {'id': download_id, 'downloader': downloader} if download_id else None download_info = {'id': download_id, 'downloader': downloader} if download_id else None
) )
+6 -3
View File
@@ -104,7 +104,7 @@ class YarrProvider(Provider):
try: try:
cookiejar = cookielib.CookieJar() cookiejar = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar)) opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))
opener.addheaders = [] opener.addheaders = [('User-Agent', self.user_agent)]
urllib2.install_opener(opener) urllib2.install_opener(opener)
log.info2('Logging into %s', self.urls['login']) log.info2('Logging into %s', self.urls['login'])
f = opener.open(self.urls['login'], self.getLoginParams()) f = opener.open(self.urls['login'], self.getLoginParams())
@@ -114,9 +114,12 @@ class YarrProvider(Provider):
if self.loginSuccess(output): if self.loginSuccess(output):
self.login_opener = opener self.login_opener = opener
return True return True
except:
log.error('Failed to login %s: %s', (self.getName(), traceback.format_exc()))
error = 'unknown'
except:
error = traceback.format_exc()
log.error('Failed to login %s: %s', (self.getName(), error))
return False return False
def loginSuccess(self, output): def loginSuccess(self, output):
@@ -14,13 +14,13 @@ log = CPLog(__name__)
class CouchPotatoApi(MovieProvider): class CouchPotatoApi(MovieProvider):
urls = { urls = {
'search': 'https://couchpota.to/api/search/%s/', 'search': 'https://api.couchpota.to/search/%s/',
'info': 'https://couchpota.to/api/info/%s/', 'info': 'https://api.couchpota.to/info/%s/',
'is_movie': 'https://couchpota.to/api/ismovie/%s/', 'is_movie': 'https://api.couchpota.to/ismovie/%s/',
'eta': 'https://couchpota.to/api/eta/%s/', 'eta': 'https://api.couchpota.to/eta/%s/',
'suggest': 'https://couchpota.to/api/suggest/', 'suggest': 'https://api.couchpota.to/suggest/',
'updater': 'https://couchpota.to/api/updater/?%s', 'updater': 'https://api.couchpota.to/updater/?%s',
'messages': 'https://couchpota.to/api/messages/?%s', 'messages': 'https://api.couchpota.to/messages/?%s',
} }
http_time_between_calls = 0 http_time_between_calls = 0
api_version = 1 api_version = 1
@@ -37,7 +37,7 @@ class TheMovieDb(MovieProvider):
if raw: if raw:
try: try:
results = self.parseMovie(raw) results = self.parseMovie(raw)
log.info('Found: %s', results['titles'][0] + ' (' + str(results['year']) + ')') log.info('Found: %s', results['titles'][0] + ' (' + str(results.get('year', 0)) + ')')
self.setCache(cache_key, results) self.setCache(cache_key, results)
return results return results
@@ -81,7 +81,7 @@ class TheMovieDb(MovieProvider):
if nr == limit: if nr == limit:
break break
log.info('Found: %s', [result['titles'][0] + ' (' + str(result['year']) + ')' for result in results]) log.info('Found: %s', [result['titles'][0] + ' (' + str(result.get('year', 0)) + ')' for result in results])
self.setCache(cache_key, results) self.setCache(cache_key, results)
return results return results
@@ -0,0 +1,45 @@
from .main import HDBits
def start():
return HDBits()
config = [{
'name': 'hdbits',
'groups': [
{
'tab': 'searcher',
'subtab': 'providers',
'list': 'torrent_providers',
'name': 'HDBits',
'description': 'See <a href="http://hdbits.org">HDBits</a>',
'options': [
{
'name': 'enabled',
'type': 'enabler',
'default': False,
},
{
'name': 'username',
'default': '',
},
{
'name': 'password',
'default': '',
'type': 'password',
},
{
'name': 'passkey',
'default': '',
},
{
'name': 'extra_score',
'advanced': True,
'label': 'Extra Score',
'type': 'int',
'default': 0,
'description': 'Starting score for each release found via this provider.',
},
],
},
],
}]
@@ -0,0 +1,55 @@
from bs4 import BeautifulSoup
from couchpotato.core.helpers.encoding import tryUrlencode
from couchpotato.core.helpers.variable import tryInt
from couchpotato.core.logger import CPLog
from couchpotato.core.providers.torrent.base import TorrentProvider
import traceback
log = CPLog(__name__)
class HDBits(TorrentProvider):
urls = {
'test' : 'https://hdbits.org/',
'login' : 'https://hdbits.org/login/doLogin/',
'detail' : 'https://hdbits.org/details.php?id=%s&source=browse',
'search' : 'https://hdbits.org/json_search.php?imdb=%s',
'download' : 'https://hdbits.org/download.php/%s.torrent?id=%s&passkey=%s&source=details.browse',
}
http_time_between_calls = 1 #seconds
def _search(self, movie, quality, results):
data = self.getJsonData(self.urls['search'] % movie['library']['identifier'], opener = self.login_opener)
if data:
try:
for result in data:
results.append({
'id': result['id'],
'name': result['title'],
'url': self.urls['download'] % (result['title'], result['id'], self.conf('passkey')),
'detail_url': self.urls['detail'] % result['id'],
'size': self.parseSize(result['size']),
'seeders': tryInt(result['seeder']),
'leechers': tryInt(result['leecher'])
})
except:
log.error('Failed getting results from %s: %s', (self.getName(), traceback.format_exc()))
def getLoginParams(self):
data = self.getHTMLData('https://hdbits.org/login')
bs = BeautifulSoup(data)
secret = bs.find('input', attrs = {'name': 'lol'})['value']
return tryUrlencode({
'uname': self.conf('username'),
'password': self.conf('password'),
'lol': secret
})
def loginSuccess(self, output):
return '/logout.php' in output.lower()
@@ -38,6 +38,30 @@ config = [{
'name': 'passkey', 'name': 'passkey',
'default': '', 'default': '',
}, },
{
'name': 'prefer_golden',
'advanced': True,
'type': 'bool',
'label': 'Prefer golden',
'default': 1,
'description': 'Favors Golden Popcorn-releases over all other releases.'
},
{
'name': 'prefer_scene',
'advanced': True,
'type': 'bool',
'label': 'Prefer scene',
'default': 0,
'description': 'Favors scene-releases over non-scene releases.'
},
{
'name': 'require_approval',
'advanced': True,
'type': 'bool',
'label': 'Require approval',
'default': 0,
'description': 'Require staff-approval for releases to be accepted.'
},
{ {
'name': 'extra_score', 'name': 'extra_score',
'advanced': True, 'advanced': True,
@@ -103,11 +103,16 @@ class PassThePopcorn(TorrentProvider):
for torrent in ptpmovie['Torrents']: for torrent in ptpmovie['Torrents']:
torrent_id = tryInt(torrent['Id']) torrent_id = tryInt(torrent['Id'])
torrentdesc = '%s %s %s' % (torrent['Resolution'], torrent['Source'], torrent['Codec']) torrentdesc = '%s %s %s' % (torrent['Resolution'], torrent['Source'], torrent['Codec'])
torrentscore = 0
if 'GoldenPopcorn' in torrent and torrent['GoldenPopcorn']: if 'GoldenPopcorn' in torrent and torrent['GoldenPopcorn']:
torrentdesc += ' HQ' torrentdesc += ' HQ'
if self.conf('prefer_golden'):
torrentscore += 200
if 'Scene' in torrent and torrent['Scene']: if 'Scene' in torrent and torrent['Scene']:
torrentdesc += ' Scene' torrentdesc += ' Scene'
if self.conf('prefer_scene'):
torrentscore += 50
if 'RemasterTitle' in torrent and torrent['RemasterTitle']: if 'RemasterTitle' in torrent and torrent['RemasterTitle']:
torrentdesc += self.htmlToASCII(' %s' % torrent['RemasterTitle']) torrentdesc += self.htmlToASCII(' %s' % torrent['RemasterTitle'])
@@ -115,18 +120,21 @@ class PassThePopcorn(TorrentProvider):
torrent_name = re.sub('[^A-Za-z0-9\-_ \(\).]+', '', '%s (%s) - %s' % (movie_title, ptpmovie['Year'], torrentdesc)) torrent_name = re.sub('[^A-Za-z0-9\-_ \(\).]+', '', '%s (%s) - %s' % (movie_title, ptpmovie['Year'], torrentdesc))
def extra_check(item): def extra_check(item):
return self.torrentMeetsQualitySpec(item, type) return self.torrentMeetsQualitySpec(item, quality_id)
results.append({ results.append({
'id': torrent_id, 'id': torrent_id,
'name': torrent_name, 'name': torrent_name,
'Source': torrent['Source'],
'Checked': 'true' if torrent['Checked'] else 'false',
'Resolution': torrent['Resolution'],
'url': '%s?action=download&id=%d&authkey=%s&torrent_pass=%s' % (self.urls['torrent'], torrent_id, authkey, passkey), 'url': '%s?action=download&id=%d&authkey=%s&torrent_pass=%s' % (self.urls['torrent'], torrent_id, authkey, passkey),
'detail_url': self.urls['detail'] % torrent_id, 'detail_url': self.urls['detail'] % torrent_id,
'date': tryInt(time.mktime(parse(torrent['UploadTime']).timetuple())), 'date': tryInt(time.mktime(parse(torrent['UploadTime']).timetuple())),
'size': tryInt(torrent['Size']) / (1024 * 1024), 'size': tryInt(torrent['Size']) / (1024 * 1024),
'seeders': tryInt(torrent['Seeders']), 'seeders': tryInt(torrent['Seeders']),
'leechers': tryInt(torrent['Leechers']), 'leechers': tryInt(torrent['Leechers']),
'score': 50 if torrent['GoldenPopcorn'] else 0, 'score': torrentscore,
'extra_check': extra_check, 'extra_check': extra_check,
'download': self.loginDownload, 'download': self.loginDownload,
}) })
@@ -167,7 +175,13 @@ class PassThePopcorn(TorrentProvider):
if not quality in self.post_search_filters: if not quality in self.post_search_filters:
return True return True
for field, specs in self.post_search_filters[quality].items(): reqs = self.post_search_filters[quality].copy()
if self.conf('require_approval'):
log.debug('Config: Require staff-approval activated')
reqs['Checked'] = ['true']
for field, specs in reqs.items():
matches_one = False matches_one = False
seen_one = False seen_one = False
@@ -182,11 +196,14 @@ class PassThePopcorn(TorrentProvider):
return False return False
else: else:
# a positive rule; if any of the possible positive values match the field, return True # a positive rule; if any of the possible positive values match the field, return True
log.debug('Checking if torrents field %s equals %s' % (field, spec))
seen_one = True seen_one = True
if torrent[field] == spec: if torrent[field] == spec:
log.debug('Torrent satisfied %s == %s' % (field, spec))
matches_one = True matches_one = True
if seen_one and not matches_one: if seen_one and not matches_one:
log.debug('Torrent did not satisfy requirements, ignoring')
return False return False
return True return True