Merge branch 'refs/heads/develop'
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
*.pyc
|
||||
/data/
|
||||
/_source/
|
||||
/_source/
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
@@ -305,7 +305,7 @@ class SourceUpdater(BaseUpdater):
|
||||
if not os.path.isdir(dirname):
|
||||
self.makeDir(dirname)
|
||||
|
||||
os.rename(fromfile, tofile)
|
||||
shutil.move(fromfile, tofile)
|
||||
try:
|
||||
existing_files.remove(tofile)
|
||||
except ValueError:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from base64 import b32decode, b16encode
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import toSafeString
|
||||
from couchpotato.core.logger import CPLog
|
||||
@@ -48,7 +49,12 @@ class Downloader(Plugin):
|
||||
return is_correct
|
||||
|
||||
def magnetToTorrent(self, magnet_link):
|
||||
torrent_hash = re.findall('urn:btih:([\w]{40})', magnet_link)[0]
|
||||
torrent_hash = re.findall('urn:btih:([\w]{32,40})', magnet_link)[0]
|
||||
|
||||
# Convert base 32 to hex
|
||||
if len(torrent_hash) == 32:
|
||||
torrent_hash = b16encode(b32decode(torrent_hash))
|
||||
|
||||
url = 'http://torrage.com/torrent/%s.torrent' % torrent_hash
|
||||
|
||||
try:
|
||||
|
||||
@@ -11,7 +11,9 @@ class Blackhole(Downloader):
|
||||
type = ['nzb', 'torrent', 'torrent_magnet']
|
||||
|
||||
def download(self, data = {}, movie = {}, manual = False, filedata = None):
|
||||
if self.isDisabled(manual) or (not self.isCorrectType(data.get('type')) or (not self.conf('use_for') in ['both', data.get('type')])):
|
||||
if self.isDisabled(manual) or \
|
||||
(not self.isCorrectType(data.get('type')) or \
|
||||
(not self.conf('use_for') in ['both', 'torrent' if 'torrent' in data.get('type') else data.get('type')])):
|
||||
return
|
||||
|
||||
directory = self.conf('directory')
|
||||
|
||||
@@ -92,10 +92,9 @@ class Sabnzbd(Downloader):
|
||||
return False
|
||||
|
||||
for slot in history['queue']['slots']:
|
||||
if slot['cat'] == self.conf('category'):
|
||||
log.debug('Found %s in SabNZBd queue, which is %s, with %s left', (slot['filename'], slot['status'], slot['timeleft']))
|
||||
if slot['filename'] == nzbname:
|
||||
return slot['status'].lower()
|
||||
log.debug('Found %s in SabNZBd queue, which is %s, with %s left', (slot['filename'], slot['status'], slot['timeleft']))
|
||||
if slot['filename'] == nzbname:
|
||||
return slot['status'].lower()
|
||||
|
||||
# Go through history items
|
||||
params = {
|
||||
@@ -119,44 +118,44 @@ class Sabnzbd(Downloader):
|
||||
return
|
||||
|
||||
for slot in history['history']['slots']:
|
||||
if slot['category'] == self.conf('category'):
|
||||
log.debug('Found %s in SabNZBd history, which has %s', (slot['name'], slot['status']))
|
||||
if slot['name'] == nzbname:
|
||||
if slot['status'] == 'Failed' or slot['fail_message'].strip():
|
||||
log.debug('Found %s in SabNZBd history, which has %s', (slot['name'], slot['status']))
|
||||
if slot['name'] == nzbname:
|
||||
# Note: if post process even if failed is on in SabNZBd, it will complete with a fail message
|
||||
if slot['status'] == 'Failed' or (slot['status'] == 'Completed' and slot['fail_message'].strip()):
|
||||
|
||||
# Delete failed download
|
||||
if self.conf('delete_failed', default = True):
|
||||
# Delete failed download
|
||||
if self.conf('delete_failed', default = True):
|
||||
|
||||
log.info('%s failed downloading, deleting...', slot['name'])
|
||||
params = {
|
||||
'apikey': self.conf('api_key'),
|
||||
'mode': 'history',
|
||||
'name': 'delete',
|
||||
'del_files': '1',
|
||||
'value': slot['nzo_id']
|
||||
}
|
||||
url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params)
|
||||
log.info('%s failed downloading, deleting...', slot['name'])
|
||||
params = {
|
||||
'apikey': self.conf('api_key'),
|
||||
'mode': 'history',
|
||||
'name': 'delete',
|
||||
'del_files': '1',
|
||||
'value': slot['nzo_id']
|
||||
}
|
||||
url = cleanHost(self.conf('host')) + "api?" + tryUrlencode(params)
|
||||
|
||||
try:
|
||||
sab = self.urlopen(url, timeout = 60, show_error = False)
|
||||
except:
|
||||
log.error('Failed deleting: %s', traceback.format_exc())
|
||||
return False
|
||||
try:
|
||||
sab = self.urlopen(url, timeout = 60, show_error = False)
|
||||
except:
|
||||
log.error('Failed deleting: %s', traceback.format_exc())
|
||||
return False
|
||||
|
||||
result = sab.strip()
|
||||
if not result:
|
||||
log.error("SABnzbd didn't return anything.")
|
||||
result = sab.strip()
|
||||
if not result:
|
||||
log.error("SABnzbd didn't return anything.")
|
||||
|
||||
log.debug("Result text from SAB: " + result[:40])
|
||||
if result == "ok":
|
||||
log.info('SabNZBd deleted failed release %s successfully.', slot['name'])
|
||||
elif result == "Missing authentication":
|
||||
log.error("Incorrect username/password or API?.")
|
||||
else:
|
||||
log.error("Unknown error: " + result[:40])
|
||||
log.debug("Result text from SAB: " + result[:40])
|
||||
if result == "ok":
|
||||
log.info('SabNZBd deleted failed release %s successfully.', slot['name'])
|
||||
elif result == "Missing authentication":
|
||||
log.error("Incorrect username/password or API?.")
|
||||
else:
|
||||
log.error("Unknown error: " + result[:40])
|
||||
|
||||
return 'failed'
|
||||
else:
|
||||
return slot['status'].lower()
|
||||
return 'failed'
|
||||
else:
|
||||
return slot['status'].lower()
|
||||
|
||||
return 'not_found'
|
||||
|
||||
@@ -2,6 +2,7 @@ from couchpotato.core.logger import CPLog
|
||||
from string import ascii_letters, digits
|
||||
from urllib import quote_plus
|
||||
import re
|
||||
import traceback
|
||||
import unicodedata
|
||||
|
||||
log = CPLog(__name__)
|
||||
@@ -30,8 +31,8 @@ def toUnicode(original, *args):
|
||||
return ek(original, *args)
|
||||
except:
|
||||
raise
|
||||
except UnicodeDecodeError:
|
||||
log.error('Unable to decode value: %s... ', repr(original)[:20])
|
||||
except:
|
||||
log.error('Unable to decode value "%s..." : %s ', (repr(original)[:20], traceback.format_exc()))
|
||||
ascii_text = str(original).encode('string_escape')
|
||||
return toUnicode(ascii_text)
|
||||
|
||||
|
||||
@@ -314,6 +314,7 @@ class Renamer(Plugin):
|
||||
break
|
||||
|
||||
# Remove files
|
||||
delete_folders = []
|
||||
for src in remove_files:
|
||||
|
||||
if isinstance(src, File):
|
||||
@@ -329,13 +330,17 @@ class Renamer(Plugin):
|
||||
os.remove(src)
|
||||
|
||||
parent_dir = os.path.normpath(os.path.dirname(src))
|
||||
if os.path.isdir(parent_dir) and destination != parent_dir:
|
||||
self.deleteEmptyFolder(parent_dir, show_error = False)
|
||||
if delete_folders.count(parent_dir) == 0 and os.path.isdir(parent_dir) and destination != parent_dir:
|
||||
delete_folders.append(parent_dir)
|
||||
|
||||
except:
|
||||
log.error('Failed removing %s: %s', (src, traceback.format_exc()))
|
||||
self.tagDir(group, 'failed_remove')
|
||||
|
||||
# Delete leftover folder from older releases
|
||||
for delete_folder in delete_folders:
|
||||
self.deleteEmptyFolder(delete_folder, show_error = False)
|
||||
|
||||
# Rename all files marked
|
||||
group['renamed_files'] = []
|
||||
for src in rename_files:
|
||||
|
||||
@@ -23,7 +23,7 @@ class Searcher(Plugin):
|
||||
in_progress = False
|
||||
|
||||
def __init__(self):
|
||||
addEvent('searcher.all', self.all_movies)
|
||||
addEvent('searcher.all', self.allMovies)
|
||||
addEvent('searcher.single', self.single)
|
||||
addEvent('searcher.correct_movie', self.correctMovie)
|
||||
addEvent('searcher.download', self.download)
|
||||
@@ -37,11 +37,11 @@ class Searcher(Plugin):
|
||||
})
|
||||
|
||||
# Schedule cronjob
|
||||
fireEvent('schedule.cron', 'searcher.all', self.all_movies, day = self.conf('cron_day'), hour = self.conf('cron_hour'), minute = self.conf('cron_minute'))
|
||||
fireEvent('schedule.cron', 'searcher.all', self.allMovies, day = self.conf('cron_day'), hour = self.conf('cron_hour'), minute = self.conf('cron_minute'))
|
||||
fireEvent('schedule.interval', 'searcher.check_snatched', self.checkSnatched, minutes = self.conf('run_every'))
|
||||
|
||||
|
||||
def all_movies(self):
|
||||
def allMovies(self):
|
||||
|
||||
if self.in_progress:
|
||||
log.info('Search already in progress')
|
||||
@@ -328,10 +328,6 @@ class Searcher(Plugin):
|
||||
if len(movie_words) <= 2 and self.correctYear([nzb['name']], movie['library']['year'], 0):
|
||||
return True
|
||||
|
||||
# Get the nfo and see if it contains the proper imdb url
|
||||
if self.checkNFO(nzb['name'], movie['library']['identifier']):
|
||||
return True
|
||||
|
||||
log.info("Wrong: %s, undetermined naming. Looking for '%s (%s)'" % (nzb['name'], movie_name, movie['library']['year']))
|
||||
return False
|
||||
|
||||
@@ -406,19 +402,6 @@ class Searcher(Plugin):
|
||||
|
||||
return False
|
||||
|
||||
def checkNFO(self, check_name, imdb_id):
|
||||
cache_key = 'srrdb.com %s' % simplifyString(check_name)
|
||||
|
||||
nfo = self.getCache(cache_key)
|
||||
if not nfo:
|
||||
try:
|
||||
nfo = self.urlopen('http://www.srrdb.com/showfile.php?release=%s' % check_name, show_error = False)
|
||||
self.setCache(cache_key, nfo)
|
||||
except:
|
||||
pass
|
||||
|
||||
return nfo and getImdb(nfo) == imdb_id
|
||||
|
||||
def couldBeReleased(self, wanted_quality, dates, pre_releases):
|
||||
|
||||
now = int(time.time())
|
||||
@@ -453,6 +436,8 @@ class Searcher(Plugin):
|
||||
ignored_status = fireEvent('status.get', 'ignored', single = True)
|
||||
failed_status = fireEvent('status.get', 'failed', single = True)
|
||||
|
||||
done_status = fireEvent('status.get', 'done', single = True)
|
||||
|
||||
db = get_session()
|
||||
rels = db.query(Release).filter_by(status_id = snatched_status.get('id'))
|
||||
|
||||
@@ -470,6 +455,13 @@ class Searcher(Plugin):
|
||||
|
||||
log.debug('Checking snatched movie: %s' , default_title)
|
||||
|
||||
# Check if movie has already completed and is manage tab (legacy db correction)
|
||||
if rel.movie.status_id == done_status.get('id'):
|
||||
log.debug('Found a completed movie with a snatched release : %s. Setting release status to ignored...' , default_title)
|
||||
rel.status_id = ignored_status.get('id')
|
||||
db.commit()
|
||||
continue
|
||||
|
||||
item = {}
|
||||
for info in rel.info:
|
||||
item[info.identifier] = info.value
|
||||
@@ -520,8 +512,6 @@ class Searcher(Plugin):
|
||||
ignored_status = fireEvent('status.get', 'ignored', single = True)
|
||||
|
||||
try:
|
||||
movie_dict = fireEvent('movie.get', movie_id, single = True)
|
||||
|
||||
db = get_session()
|
||||
rels = db.query(Release).filter_by(
|
||||
status_id = snatched_status.get('id'),
|
||||
@@ -532,6 +522,7 @@ class Searcher(Plugin):
|
||||
rel.status_id = ignored_status.get('id')
|
||||
db.commit()
|
||||
|
||||
movie_dict = fireEvent('movie.get', movie_id, single = True)
|
||||
log.info('Trying next release for: %s', getTitle(movie_dict['library']))
|
||||
fireEvent('searcher.single', movie_dict)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ log = CPLog(__name__)
|
||||
|
||||
class Userscript(Plugin):
|
||||
|
||||
version = 2
|
||||
version = 3
|
||||
|
||||
def __init__(self):
|
||||
addApiView('userscript.get/<random>/<path:filename>', self.getUserScript, static = True)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name CouchPotato UserScript
|
||||
// @description Add movies like a real CouchPotato
|
||||
// @grant none
|
||||
// @version {{version}}
|
||||
|
||||
// @match {{host}}*
|
||||
@@ -44,21 +45,19 @@ function create() {
|
||||
return A;
|
||||
}
|
||||
|
||||
if (typeof GM_addStyle == 'undefined'){
|
||||
GM_addStyle = function(css) {
|
||||
var head = document.getElementsByTagName('head')[0],
|
||||
style = document.createElement('style');
|
||||
if (!head)
|
||||
return;
|
||||
var addStyle = function(css) {
|
||||
var head = document.getElementsByTagName('head')[0],
|
||||
style = document.createElement('style');
|
||||
if (!head)
|
||||
return;
|
||||
|
||||
style.type = 'text/css';
|
||||
style.textContent = css;
|
||||
head.appendChild(style);
|
||||
}
|
||||
style.type = 'text/css';
|
||||
style.textContent = css;
|
||||
head.appendChild(style);
|
||||
}
|
||||
|
||||
// Styles
|
||||
GM_addStyle('\
|
||||
addStyle('\
|
||||
#cp_popup { font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif; -moz-border-radius: 6px 0px 0px 6px; -webkit-border-radius: 6px 0px 0px 6px; border-radius: 6px 0px 0px 6px; -moz-box-shadow: 0 0 20px rgba(0,0,0,0.5); -webkit-box-shadow: 0 0 20px rgba(0,0,0,0.5); box-shadow: 0 0 20px rgba(0,0,0,0.5); position:fixed; z-index:9999; bottom:0; right:0; font-size:15px; margin: 20px 0; display: block; background:#4E5969; } \
|
||||
#cp_popup.opened { width: 492px; } \
|
||||
#cp_popup a#add_to { cursor:pointer; text-align:center; text-decoration:none; color: #000; display:block; padding:5px 0 5px 5px; } \
|
||||
|
||||
@@ -31,7 +31,7 @@ class MovieResultModifier(Plugin):
|
||||
order.append(imdb)
|
||||
|
||||
if item.get('via_imdb'):
|
||||
if order.index(imdb):
|
||||
if order.count(imdb):
|
||||
order.remove(imdb)
|
||||
order.insert(0, imdb)
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class IMDBAPI(MovieProvider):
|
||||
|
||||
name_year = fireEvent('scanner.name_year', q, single = True)
|
||||
|
||||
if not q or not name_year.get('name'):
|
||||
if not q or not name_year or (name_year and not name_year.get('name')):
|
||||
return []
|
||||
|
||||
cache_key = 'imdbapi.cache.%s' % q
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
from main import PassThePopcorn
|
||||
|
||||
def start():
|
||||
return PassThePopcorn()
|
||||
|
||||
config = [{
|
||||
'name': 'passthepopcorn',
|
||||
'groups': [{
|
||||
'tab': 'searcher',
|
||||
'subtab': 'providers',
|
||||
'name': 'PassThePopcorn',
|
||||
'description': 'See <a href="http://passthepopcorn.me">PassThePopcorn.me</a>',
|
||||
'options': [
|
||||
{
|
||||
'name': 'enabled',
|
||||
'type': 'enabler',
|
||||
'default': False
|
||||
},
|
||||
{
|
||||
'name': 'domain',
|
||||
'advanced': True,
|
||||
'label': 'Proxy server',
|
||||
'description': 'Domain for requests (HTTPS only!), keep empty to use default (tls.passthepopcorn.me).',
|
||||
},
|
||||
{
|
||||
'name': 'username',
|
||||
'default': '',
|
||||
},
|
||||
{
|
||||
'name': 'password',
|
||||
'default': '',
|
||||
'type': 'password',
|
||||
}
|
||||
],
|
||||
}]
|
||||
}]
|
||||
254
couchpotato/core/providers/torrent/passthepopcorn/main.py
Normal file
254
couchpotato/core/providers/torrent/passthepopcorn/main.py
Normal file
@@ -0,0 +1,254 @@
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.helpers.variable import getTitle, tryInt, mergeDicts
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.torrent.base import TorrentProvider
|
||||
from dateutil.parser import parse
|
||||
import cookielib
|
||||
import htmlentitydefs
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
import traceback
|
||||
import urllib2
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class PassThePopcorn(TorrentProvider):
|
||||
|
||||
urls = {
|
||||
'domain': 'https://tls.passthepopcorn.me',
|
||||
'detail': 'https://tls.passthepopcorn.me/torrents.php?torrentid=%s',
|
||||
'torrent': 'https://tls.passthepopcorn.me/torrents.php',
|
||||
'login': 'https://tls.passthepopcorn.me/login.php',
|
||||
'search': 'https://tls.passthepopcorn.me/search/%s/0/7/%d'
|
||||
}
|
||||
|
||||
quality_search_params = {
|
||||
'bd50': {'media': 'Blu-ray', 'format': 'BD50'},
|
||||
'1080p': {'resolution': '1080p'},
|
||||
'720p': {'resolution': '720p'},
|
||||
'brrip': {'media': 'Blu-ray'},
|
||||
'dvdr': {'resolution': 'anysd'},
|
||||
'dvdrip': {'media': 'DVD'},
|
||||
'scr': {'media': 'DVD-Screener'},
|
||||
'r5': {'media': 'R5'},
|
||||
'tc': {'media': 'TC'},
|
||||
'ts': {'media': 'TS'},
|
||||
'cam': {'media': 'CAM'}
|
||||
}
|
||||
|
||||
post_search_filters = {
|
||||
'bd50': {'Codec': ['BD50']},
|
||||
'1080p': {'Resolution': ['1080p']},
|
||||
'720p': {'Resolution': ['720p']},
|
||||
'brrip': {'Source': ['Blu-ray'], 'Quality': ['High Definition'], 'Container': ['!ISO']},
|
||||
'dvdr': {'Codec': ['DVD5', 'DVD9']},
|
||||
'dvdrip': {'Source': ['DVD'], 'Codec': ['!DVD5', '!DVD9']},
|
||||
'scr': {'Source': ['DVD-Screener']},
|
||||
'r5': {'Source': ['R5']},
|
||||
'tc': {'Source': ['TC']},
|
||||
'ts': {'Source': ['TS']},
|
||||
'cam': {'Source': ['CAM']}
|
||||
}
|
||||
|
||||
class NotLoggedInHTTPError(urllib2.HTTPError):
|
||||
def __init__(self, url, code, msg, headers, fp):
|
||||
urllib2.HTTPError.__init__(self, url, code, msg, headers, fp)
|
||||
|
||||
class PTPHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
|
||||
def http_error_302(self, req, fp, code, msg, headers):
|
||||
log.debug("302 detected; redirected to %s" % headers['Location'])
|
||||
if (headers['Location'] != 'login.php'):
|
||||
return urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
|
||||
else:
|
||||
raise PassThePopcorn.NotLoggedInHTTPError(req.get_full_url(), code, msg, headers, fp)
|
||||
|
||||
def search(self, movie, quality):
|
||||
|
||||
results = []
|
||||
|
||||
if self.isDisabled():
|
||||
return results
|
||||
|
||||
movie_title = getTitle(movie['library'])
|
||||
quality_id = quality['identifier']
|
||||
|
||||
log.info('Searching for %s at quality %s' % (movie_title, quality_id))
|
||||
|
||||
params = mergeDicts(self.quality_search_params[quality_id].copy(), {
|
||||
'order_by': 'relevance',
|
||||
'order_way': 'descending',
|
||||
'searchstr': movie['library']['identifier']
|
||||
})
|
||||
|
||||
# Do login for the cookies
|
||||
if not self.login_opener and not self.login():
|
||||
return results
|
||||
|
||||
try:
|
||||
url = '%s?json=noredirect&%s' % (self.urls['torrent'], tryUrlencode(params))
|
||||
txt = self.urlopen(url, opener = self.login_opener)
|
||||
res = json.loads(txt)
|
||||
except:
|
||||
log.error('Search on PassThePopcorn.me (%s) failed (could not decode JSON)' % params)
|
||||
return []
|
||||
|
||||
try:
|
||||
if not 'Movies' in res:
|
||||
log.info("PTP search returned nothing for '%s' at quality '%s' with search parameters %s" % (movie_title, quality_id, params))
|
||||
return []
|
||||
|
||||
authkey = res['AuthKey']
|
||||
passkey = res['PassKey']
|
||||
|
||||
for ptpmovie in res['Movies']:
|
||||
if not 'Torrents' in ptpmovie:
|
||||
log.debug('Movie %s (%s) has NO torrents' % (ptpmovie['Title'], ptpmovie['Year']))
|
||||
continue
|
||||
|
||||
log.debug('Movie %s (%s) has %d torrents' % (ptpmovie['Title'], ptpmovie['Year'], len(ptpmovie['Torrents'])))
|
||||
for torrent in ptpmovie['Torrents']:
|
||||
torrent_id = tryInt(torrent['Id'])
|
||||
torrentdesc = '%s %s %s' % (torrent['Resolution'], torrent['Source'], torrent['Codec'])
|
||||
|
||||
if 'GoldenPopcorn' in torrent and torrent['GoldenPopcorn']:
|
||||
torrentdesc += ' HQ'
|
||||
if 'Scene' in torrent and torrent['Scene']:
|
||||
torrentdesc += ' Scene'
|
||||
if 'RemasterTitle' in torrent and torrent['RemasterTitle']:
|
||||
# eliminate odd characters...
|
||||
torrentdesc += self.htmlToASCII(' %s' % torrent['RemasterTitle'])
|
||||
|
||||
torrentdesc += ' (%s)' % quality_id
|
||||
torrent_name = re.sub('[^A-Za-z0-9\-_ \(\).]+', '', '%s (%s) - %s' % (movie_title, ptpmovie['Year'], torrentdesc))
|
||||
|
||||
def extra_check(item):
|
||||
return self.torrentMeetsQualitySpec(item, type)
|
||||
|
||||
def extra_score(item):
|
||||
return 50 if torrent['GoldenPopcorn'] else 0
|
||||
|
||||
new = {
|
||||
'id': torrent_id,
|
||||
'type': 'torrent',
|
||||
'provider': self.getName(),
|
||||
'name': torrent_name,
|
||||
'description': '',
|
||||
'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,
|
||||
'date': tryInt(time.mktime(parse(torrent['UploadTime']).timetuple())),
|
||||
'size': tryInt(torrent['Size']) / (1024 * 1024),
|
||||
'provider': self.getName(),
|
||||
'seeders': tryInt(torrent['Seeders']),
|
||||
'leechers': tryInt(torrent['Leechers']),
|
||||
'extra_score': extra_score,
|
||||
'extra_check': extra_check,
|
||||
'download': self.loginDownload,
|
||||
}
|
||||
|
||||
new['score'] = fireEvent('score.calculate', new, movie, single = True)
|
||||
|
||||
if fireEvent('searcher.correct_movie', nzb = new, movie = movie, quality = quality):
|
||||
results.append(new)
|
||||
self.found(new)
|
||||
|
||||
return results
|
||||
except:
|
||||
log.error('Failed getting results from %s: %s', (self.getName(), traceback.format_exc()))
|
||||
|
||||
return []
|
||||
|
||||
def login(self):
|
||||
|
||||
cookieprocessor = urllib2.HTTPCookieProcessor(cookielib.CookieJar())
|
||||
opener = urllib2.build_opener(cookieprocessor, PassThePopcorn.PTPHTTPRedirectHandler())
|
||||
opener.addheaders = [
|
||||
('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.75 Safari/537.1'),
|
||||
('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'),
|
||||
('Accept-Language', 'en-gb,en;q=0.5'),
|
||||
('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7'),
|
||||
('Keep-Alive', '115'),
|
||||
('Connection', 'keep-alive'),
|
||||
('Cache-Control', 'max-age=0'),
|
||||
]
|
||||
|
||||
try:
|
||||
response = opener.open(self.urls['login'], self.getLoginParams())
|
||||
except urllib2.URLError as e:
|
||||
log.error('Login to PassThePopcorn failed: %s' % e)
|
||||
return False
|
||||
|
||||
if response.getcode() == 200:
|
||||
log.debug('Login HTTP status 200; seems successful')
|
||||
self.login_opener = opener
|
||||
return True
|
||||
else:
|
||||
log.error('Login to PassThePopcorn failed: returned code %d' % response.getcode())
|
||||
return False
|
||||
|
||||
def torrentMeetsQualitySpec(self, torrent, quality):
|
||||
|
||||
if not quality in self.post_search_filters:
|
||||
return True
|
||||
|
||||
for field, specs in self.post_search_filters[quality].items():
|
||||
matches_one = False
|
||||
seen_one = False
|
||||
|
||||
if not field in torrent:
|
||||
log.debug('Torrent with ID %s has no field "%s"; cannot apply post-search-filter for quality "%s"' % (torrent['Id'], field, quality))
|
||||
continue
|
||||
|
||||
for spec in specs:
|
||||
if len(spec) > 0 and spec[0] == '!':
|
||||
# a negative rule; if the field matches, return False
|
||||
if torrent[field] == spec[1:]:
|
||||
return False
|
||||
else:
|
||||
# a positive rule; if any of the possible positive values match the field, return True
|
||||
seen_one = True
|
||||
if torrent[field] == spec:
|
||||
matches_one = True
|
||||
|
||||
if seen_one and not matches_one:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def htmlToUnicode(self, text):
|
||||
def fixup(m):
|
||||
text = m.group(0)
|
||||
if text[:2] == "&#":
|
||||
# character reference
|
||||
try:
|
||||
if text[:3] == "&#x":
|
||||
return unichr(int(text[3:-1], 16))
|
||||
else:
|
||||
return unichr(int(text[2:-1]))
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
# named entity
|
||||
try:
|
||||
text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
|
||||
except KeyError:
|
||||
pass
|
||||
return text # leave as is
|
||||
return re.sub("&#?\w+;", fixup, u'%s' % text)
|
||||
|
||||
def unicodeToASCII(self, text):
|
||||
import unicodedata
|
||||
return ''.join(c for c in unicodedata.normalize('NFKD', text) if unicodedata.category(c) != 'Mn')
|
||||
|
||||
def htmlToASCII(self, text):
|
||||
return self.unicodeToASCII(self.htmlToUnicode(text))
|
||||
|
||||
def getLoginParams(self):
|
||||
return tryUrlencode({
|
||||
'username': self.conf('username'),
|
||||
'password': self.conf('password'),
|
||||
'keeplogged': '1',
|
||||
'login': 'Login'
|
||||
})
|
||||
@@ -37,6 +37,8 @@ class ThePirateBay(TorrentProvider):
|
||||
'https://piratereverse.info',
|
||||
'https://tpb.pirateparty.org.uk',
|
||||
'https://argumentomteemigreren.nl',
|
||||
'https://livepirate.com/',
|
||||
'https://www.getpirate.com/',
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
|
||||
@@ -3,7 +3,7 @@ from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.helpers.variable import mergeDicts, getTitle
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.trailer.base import TrailerProvider
|
||||
from string import letters, digits
|
||||
from string import digits, ascii_letters
|
||||
import re
|
||||
|
||||
log = CPLog(__name__)
|
||||
@@ -46,7 +46,7 @@ class HDTrailers(TrailerProvider):
|
||||
|
||||
movie_name = getTitle(group['library'])
|
||||
|
||||
url = "%s?%s" % (self.url['backup'], tryUrlencode({'s':movie_name}))
|
||||
url = "%s?%s" % (self.urls['backup'], tryUrlencode({'s':movie_name}))
|
||||
data = self.getCache('hdtrailers.alt.%s' % group['library']['identifier'], url)
|
||||
|
||||
try:
|
||||
@@ -100,7 +100,7 @@ class HDTrailers(TrailerProvider):
|
||||
return results
|
||||
|
||||
def movieUrlName(self, string):
|
||||
safe_chars = letters + digits + ' '
|
||||
safe_chars = ascii_letters + digits + ' '
|
||||
r = ''.join([char if char in safe_chars else ' ' for char in string])
|
||||
name = re.sub('\s+' , '-', r).lower()
|
||||
|
||||
|
||||
@@ -24,39 +24,36 @@
|
||||
}
|
||||
.page.settings .tabs a {
|
||||
display: block;
|
||||
padding: 11px 15px;
|
||||
padding: 7px 15px;
|
||||
font-weight: normal;
|
||||
transition: all 0.1s ease-in-out;
|
||||
transition: all 0.3s ease-in-out;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
text-shadow: none;
|
||||
}
|
||||
.page.settings .tabs a:hover, .page.settings .tabs .active a {
|
||||
.page.settings .tabs a:hover,
|
||||
.page.settings .tabs .active a {
|
||||
background: rgb(78, 89, 105);
|
||||
font-weight: bold;
|
||||
font-size: 25px;
|
||||
color: #fff;
|
||||
}
|
||||
.page.settings .tabs > li {
|
||||
border-bottom: 1px solid rgb(78, 89, 105);
|
||||
}
|
||||
|
||||
.page.settings .tabs .subtabs {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
transition: all 1s ease-in-out;
|
||||
max-height: 0;
|
||||
margin: -5px 0 10px;
|
||||
}
|
||||
.page.settings .tabs > .active .subtabs {
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.page.settings .tabs .subtabs a {
|
||||
font-size: 15px;
|
||||
padding: 1px 15px;
|
||||
font-size: 13px;
|
||||
padding: 0 15px;
|
||||
font-weight: normal;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
background: rgba(78, 89, 105, 0.4);
|
||||
transition: all .3s ease-in-out;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.page.settings .tabs .subtabs .active a {
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
background: rgb(78, 89, 105);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
<link href="{{ url_for('web.static', filename='images/favicon.ico') }}" rel="icon" type="image/x-icon" />
|
||||
<link rel="apple-touch-icon" href="{{ url_for('web.static', filename='images/homescreen.png') }}" />
|
||||
|
||||
|
||||
<script type="text/javascript" src="https://www.youtube.com/player_api" defer="defer"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -51,7 +51,6 @@
|
||||
new Uniform();
|
||||
|
||||
Api.setup({
|
||||
'host': {{ fireEvent('app.api_url', single = True)|tojson|safe }},
|
||||
'url': {{ url_for('api.index')|tojson|safe }},
|
||||
'path_sep': {{ sep|tojson|safe }},
|
||||
'is_remote': false
|
||||
|
||||
Reference in New Issue
Block a user