Merge branch 'refs/heads/develop'
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
from couchpotato.api import api_docs, api_docs_missing
|
||||
from couchpotato.core.auth import requires_auth
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.helpers.request import getParams, jsonified
|
||||
from couchpotato.core.helpers.variable import md5
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.environment import Env
|
||||
from flask.app import Flask
|
||||
@@ -51,6 +53,22 @@ def apiDocs():
|
||||
del api_docs_missing['']
|
||||
return render_template('api.html', fireEvent = fireEvent, routes = sorted(routes), api_docs = api_docs, api_docs_missing = sorted(api_docs_missing))
|
||||
|
||||
@web.route('getkey/')
|
||||
def getApiKey():
|
||||
|
||||
api = None
|
||||
params = getParams()
|
||||
username = Env.setting('username')
|
||||
password = Env.setting('password')
|
||||
|
||||
if (params.get('u') == md5(username) or not username) and (params.get('p') == password or not password):
|
||||
api = Env.setting('api_key')
|
||||
|
||||
return jsonified({
|
||||
'success': api is not None,
|
||||
'api_key': api
|
||||
})
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(error):
|
||||
index_url = url_for('web.index')
|
||||
|
||||
@@ -55,7 +55,7 @@ config = [{
|
||||
'name': 'api_key',
|
||||
'default': uuid4().hex,
|
||||
'readonly': 1,
|
||||
'description': 'Let 3rd party app do stuff. <a target="_self" href="/docs/">Docs</a>',
|
||||
'description': 'Let 3rd party app do stuff. <a target="_self" href="../../docs/">Docs</a>',
|
||||
},
|
||||
{
|
||||
'name': 'debug',
|
||||
|
||||
@@ -96,7 +96,8 @@ class BaseUpdater(Plugin):
|
||||
'last_check': self.last_check,
|
||||
'update_version': self.update_version,
|
||||
'version': self.getVersion(),
|
||||
'repo_name': '%s/%s' % (self.repo_user, self.repo_name)
|
||||
'repo_name': '%s/%s' % (self.repo_user, self.repo_name),
|
||||
'branch': self.branch,
|
||||
}
|
||||
|
||||
def check(self):
|
||||
|
||||
@@ -79,6 +79,8 @@ var UpdaterBase = new Class({
|
||||
if(json.success){
|
||||
App.restart('Please wait while CouchPotato is being updated with more awesome stuff.', 'Updating');
|
||||
App.checkAvailable.delay(500, App);
|
||||
if(self.message)
|
||||
self.message.destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ class CoreNotifier(Notification):
|
||||
addEvent('library.update_finish', lambda data: fireEvent('notify.frontend', type = 'library.update', data = data))
|
||||
|
||||
def markAsRead(self):
|
||||
ids = getParam('ids').split(',')
|
||||
ids = [x.strip() for x in getParam('ids').split(',')]
|
||||
|
||||
db = get_session()
|
||||
|
||||
@@ -78,7 +78,7 @@ class CoreNotifier(Notification):
|
||||
q = db.query(Notif)
|
||||
|
||||
if limit_offset:
|
||||
splt = limit_offset.split(',')
|
||||
splt = [x.strip() for x in limit_offset.split(',')]
|
||||
limit = splt[0]
|
||||
offset = 0 if len(splt) is 1 else splt[1]
|
||||
q = q.limit(limit).offset(offset)
|
||||
|
||||
@@ -11,7 +11,7 @@ class NotifyMyAndroid(Notification):
|
||||
if self.isDisabled(): return
|
||||
|
||||
nma = pynma.PyNMA()
|
||||
keys = self.conf('api_key').split(',')
|
||||
keys = [x.strip() for x in self.conf('api_key').split(',')]
|
||||
nma.addkey(keys)
|
||||
nma.developerkey(self.conf('dev_key'))
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ class NotifyMyWP(Notification):
|
||||
def notify(self, message = '', data = {}):
|
||||
if self.isDisabled(): return
|
||||
|
||||
keys = self.conf('api_key').split(',')
|
||||
keys = [x.strip() for x in self.conf('api_key').split(',')]
|
||||
p = PyNMWP(keys, self.conf('dev_key'))
|
||||
|
||||
response = p.push(application = self.default_title, event = message, description = message, priority = self.conf('priority'), batch_mode = len(keys) > 1)
|
||||
|
||||
@@ -77,6 +77,6 @@ class Manage(Plugin):
|
||||
|
||||
def directories(self):
|
||||
try:
|
||||
return self.conf('library', default = '').split('::')
|
||||
return [x.strip() for x in self.conf('library', default = '').split('::')]
|
||||
except:
|
||||
return []
|
||||
|
||||
@@ -139,7 +139,7 @@ class MoviePlugin(Plugin):
|
||||
|
||||
|
||||
if limit_offset:
|
||||
splt = limit_offset.split(',')
|
||||
splt = [x.strip() for x in limit_offset.split(',')]
|
||||
limit = splt[0]
|
||||
offset = 0 if len(splt) is 1 else splt[1]
|
||||
q2 = q2.limit(limit).offset(offset)
|
||||
@@ -324,7 +324,7 @@ class MoviePlugin(Plugin):
|
||||
|
||||
available_status = fireEvent('status.get', 'available', single = True)
|
||||
|
||||
ids = params.get('id').split(',')
|
||||
ids = [x.strip() for x in params.get('id').split(',')]
|
||||
for movie_id in ids:
|
||||
|
||||
m = db.query(Movie).filter_by(id = movie_id).first()
|
||||
@@ -356,7 +356,7 @@ class MoviePlugin(Plugin):
|
||||
|
||||
params = getParams()
|
||||
|
||||
ids = params.get('id').split(',')
|
||||
ids = [x.strip() for x in params.get('id').split(',')]
|
||||
for movie_id in ids:
|
||||
self.delete(movie_id)
|
||||
|
||||
|
||||
@@ -279,7 +279,8 @@
|
||||
border: 0;
|
||||
}
|
||||
.movies .options .table .provider {
|
||||
width: 130px;
|
||||
width: 120px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.movies .options .table .name {
|
||||
width: 350px;
|
||||
@@ -290,6 +291,8 @@
|
||||
.movies .options .table.files .name { width: 605px; }
|
||||
.movies .options .table .type { width: 130px; }
|
||||
.movies .options .table .is_available { width: 90px; }
|
||||
.movies .options .table .age,
|
||||
.movies .options .table .size { width: 40px; }
|
||||
|
||||
.movies .options .table a {
|
||||
width: 30px !important;
|
||||
|
||||
@@ -270,8 +270,9 @@ var ReleaseAction = new Class({
|
||||
// Header
|
||||
new Element('div.item.head').adopt(
|
||||
new Element('span.name', {'text': 'Release name'}),
|
||||
new Element('span.status', {'text': 'Status'}),
|
||||
new Element('span.quality', {'text': 'Quality'}),
|
||||
new Element('span.size', {'text': 'Size (MB)'}),
|
||||
new Element('span.size', {'text': 'Size'}),
|
||||
new Element('span.age', {'text': 'Age'}),
|
||||
new Element('span.score', {'text': 'Score'}),
|
||||
new Element('span.provider', {'text': 'Provider'})
|
||||
@@ -288,9 +289,10 @@ var ReleaseAction = new Class({
|
||||
} catch(e){}
|
||||
|
||||
new Element('div', {
|
||||
'class': 'item ' + status.identifier
|
||||
'class': 'item'
|
||||
}).adopt(
|
||||
new Element('span.name', {'text': self.get(release, 'name'), 'title': self.get(release, 'name')}),
|
||||
new Element('span.status', {'text': status.identifier, 'class': 'release_status '+status.identifier}),
|
||||
new Element('span.quality', {'text': quality.get('label')}),
|
||||
new Element('span.size', {'text': (self.get(release, 'size') || 'unknown')}),
|
||||
new Element('span.age', {'text': self.get(release, 'age')}),
|
||||
|
||||
@@ -18,7 +18,7 @@ class QualityPlugin(Plugin):
|
||||
qualities = [
|
||||
{'identifier': 'bd50', 'hd': True, 'size': (15000, 60000), 'label': 'BR-Disk', 'alternative': ['bd25'], 'allow': ['1080p'], 'ext':[], 'tags': ['bdmv', 'certificate', ('complete', 'bluray')]},
|
||||
{'identifier': '1080p', 'hd': True, 'size': (5000, 20000), 'label': '1080P', 'width': 1920, 'alternative': [], 'allow': [], 'ext':['mkv', 'm2ts']},
|
||||
{'identifier': '720p', 'hd': True, 'size': (3500, 10000), 'label': '720P', 'width': 1280, 'alternative': [], 'allow': [], 'ext':['mkv', 'm2ts']},
|
||||
{'identifier': '720p', 'hd': True, 'size': (3500, 10000), 'label': '720P', 'width': 1280, 'alternative': [], 'allow': [], 'ext':['mkv', 'm2ts', 'ts']},
|
||||
{'identifier': 'brrip', 'hd': True, 'size': (700, 7000), 'label': 'BR-Rip', 'alternative': ['bdrip'], 'allow': ['720p'], 'ext':['avi']},
|
||||
{'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', 'alternative': ['dvdrip'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']},
|
||||
@@ -161,26 +161,25 @@ class QualityPlugin(Plugin):
|
||||
for cur_file in files:
|
||||
size = (os.path.getsize(cur_file) / 1024 / 1024) if os.path.isfile(cur_file) else 0
|
||||
words = re.split('\W+', cur_file.lower())
|
||||
safe_cur_file = toSafeString(cur_file)
|
||||
|
||||
for quality in self.all():
|
||||
|
||||
# Check tags
|
||||
if quality['identifier'] in words:
|
||||
log.debug('Found via identifier "%s" in %s' % (quality['identifier'], safe_cur_file))
|
||||
log.debug('Found via identifier "%s" in %s' % (quality['identifier'], cur_file))
|
||||
return self.setCache(hash, quality)
|
||||
|
||||
if list(set(quality.get('alternative', [])) & set(words)):
|
||||
log.debug('Found %s via alt %s in %s' % (quality['identifier'], quality.get('alternative'), safe_cur_file))
|
||||
log.debug('Found %s via alt %s in %s' % (quality['identifier'], quality.get('alternative'), cur_file))
|
||||
return self.setCache(hash, quality)
|
||||
|
||||
for tag in quality.get('tags', []):
|
||||
if isinstance(tag, tuple) and '.'.join(tag) in '.'.join(words):
|
||||
log.debug('Found %s via tag %s in %s' % (quality['identifier'], quality.get('tags'), safe_cur_file))
|
||||
log.debug('Found %s via tag %s in %s' % (quality['identifier'], quality.get('tags'), cur_file))
|
||||
return self.setCache(hash, quality)
|
||||
|
||||
if list(set(quality.get('tags', [])) & set(words)):
|
||||
log.debug('Found %s via tag %s in %s' % (quality['identifier'], quality.get('tags'), safe_cur_file))
|
||||
log.debug('Found %s via tag %s in %s' % (quality['identifier'], quality.get('tags'), cur_file))
|
||||
return self.setCache(hash, quality)
|
||||
|
||||
# Check on unreliable stuff
|
||||
|
||||
@@ -328,7 +328,7 @@ class Renamer(Plugin):
|
||||
try:
|
||||
os.remove(src)
|
||||
except:
|
||||
log.error('Failed removing %s: %s', (src, traceback.format_exc()))
|
||||
log.error('Failed removing %s: %s' % (src, traceback.format_exc()))
|
||||
|
||||
# Remove matching releases
|
||||
for release in remove_releases:
|
||||
@@ -336,14 +336,14 @@ class Renamer(Plugin):
|
||||
try:
|
||||
db.delete(release)
|
||||
except:
|
||||
log.error('Failed removing %s: %s', (release.identifier, traceback.format_exc()))
|
||||
log.error('Failed removing %s: %s' % (release.identifier, traceback.format_exc()))
|
||||
|
||||
if group['dirname'] and group['parentdir']:
|
||||
try:
|
||||
log.info('Deleting folder: %s' % group['parentdir'])
|
||||
self.deleteEmptyFolder(group['parentdir'])
|
||||
except:
|
||||
log.error('Failed removing %s: %s', (group['parentdir'], traceback.format_exc()))
|
||||
log.error('Failed removing %s: %s' % (group['parentdir'], traceback.format_exc()))
|
||||
|
||||
# Search for trailers etc
|
||||
fireEventAsync('renamer.after', group)
|
||||
|
||||
@@ -31,7 +31,7 @@ class Scanner(Plugin):
|
||||
ignored_in_path = ['_unpack', '_failed_', '_unknown_', '_exists_', '.appledouble', '.appledb', '.appledesktop', os.path.sep + '._', '.ds_store', 'cp.cpnfo'] #unpacking, smb-crap, hidden files
|
||||
ignore_names = ['extract', 'extracting', 'extracted', 'movie', 'movies', 'film', 'films', 'download', 'downloads', 'video_ts', 'audio_ts', 'bdmv', 'certificate']
|
||||
extensions = {
|
||||
'movie': ['mkv', 'wmv', 'avi', 'mpg', 'mpeg', 'mp4', 'm2ts', 'iso', 'img', 'mdf'],
|
||||
'movie': ['mkv', 'wmv', 'avi', 'mpg', 'mpeg', 'mp4', 'm2ts', 'iso', 'img', 'mdf', 'ts'],
|
||||
'movie_extra': ['mds'],
|
||||
'dvd': ['vts_*', 'vob'],
|
||||
'nfo': ['nfo', 'txt', 'tag'],
|
||||
@@ -165,6 +165,9 @@ class Scanner(Plugin):
|
||||
|
||||
for file_path in files:
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
continue
|
||||
|
||||
# Remove ignored files
|
||||
if self.isSampleFile(file_path):
|
||||
leftovers.append(file_path)
|
||||
@@ -263,7 +266,7 @@ class Scanner(Plugin):
|
||||
file_too_new = tryInt(time.time() - file_time)
|
||||
break
|
||||
|
||||
if file_too_new and not Env.get('dev'):
|
||||
if file_too_new:
|
||||
log.info('Files seem to be still unpacking or just unpacked (created on %s), ignoring for now: %s' % (time.ctime(file_time), identifier))
|
||||
continue
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ def nameScore(name, year):
|
||||
|
||||
# Contains preferred word
|
||||
nzb_words = re.split('\W+', simplifyString(name))
|
||||
preferred_words = Env.setting('preferred_words', section = 'searcher').split(',')
|
||||
preferred_words = [x.strip() for x in Env.setting('preferred_words', section = 'searcher').split(',')]
|
||||
for word in preferred_words:
|
||||
if word.strip() and word.strip().lower() in nzb_words:
|
||||
score = score + 100
|
||||
|
||||
@@ -123,7 +123,11 @@ class Searcher(Plugin):
|
||||
|
||||
|
||||
for nzb in sorted_results:
|
||||
return self.download(data = nzb, movie = movie)
|
||||
downloaded = self.download(data = nzb, movie = movie)
|
||||
if downloaded:
|
||||
return True
|
||||
else:
|
||||
break
|
||||
else:
|
||||
log.info('Better quality (%s) already available or snatched for %s' % (quality_type['quality']['label'], default_title))
|
||||
fireEvent('movie.restatus', movie['id'])
|
||||
@@ -190,19 +194,26 @@ class Searcher(Plugin):
|
||||
log.info('Wrong: Outside retention, age is %s, needs %s or lower: %s' % (nzb['age'], retention, nzb['name']))
|
||||
return False
|
||||
|
||||
nzb_words = re.split('\W+', simplifyString(nzb['name']))
|
||||
required_words = self.conf('required_words').split(',')
|
||||
movie_name = simplifyString(nzb['name'])
|
||||
nzb_words = re.split('\W+', movie_name)
|
||||
required_words = [x.strip() for x in self.conf('required_words').split(',')]
|
||||
|
||||
if self.conf('required_words') and not list(set(nzb_words) & set(required_words)):
|
||||
log.info("NZB doesn't contain any of the required words.")
|
||||
return False
|
||||
|
||||
ignored_words = self.conf('ignored_words').split(',')
|
||||
ignored_words = [x.strip() for x in self.conf('ignored_words').split(',')]
|
||||
blacklisted = list(set(nzb_words) & set(ignored_words))
|
||||
if self.conf('ignored_words') and blacklisted:
|
||||
log.info("Wrong: '%s' blacklisted words: %s" % (nzb['name'], ", ".join(blacklisted)))
|
||||
return False
|
||||
|
||||
pron_tags = ['xxx', 'sex', 'anal', 'tits', 'fuck', 'porn', 'orgy', 'milf', 'boobs']
|
||||
for p_tag in pron_tags:
|
||||
if p_tag in movie_name:
|
||||
log.info('Wrong: %s, probably pr0n' % (nzb['name']))
|
||||
return False
|
||||
|
||||
#qualities = fireEvent('quality.all', single = True)
|
||||
preferred_quality = fireEvent('quality.single', identifier = quality['identifier'], single = True)
|
||||
|
||||
@@ -313,10 +324,10 @@ class Searcher(Plugin):
|
||||
check_movie = fireEvent('scanner.name_year', check_name, single = True)
|
||||
|
||||
try:
|
||||
check_words = re.split('\W+', check_movie.get('name', ''))
|
||||
movie_words = re.split('\W+', simplifyString(movie_name))
|
||||
check_words = filter(None, re.split('\W+', check_movie.get('name', '')))
|
||||
movie_words = filter(None, re.split('\W+', simplifyString(movie_name)))
|
||||
|
||||
if len(list(set(check_words) - set(movie_words))) == 0:
|
||||
if len(check_words) > 0 and len(movie_words) > 0 and len(list(set(check_words) - set(movie_words))) == 0:
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -65,9 +65,13 @@ class YarrProvider(Provider):
|
||||
def belongsTo(self, url, host = None):
|
||||
try:
|
||||
hostname = urlparse(url).hostname
|
||||
download_url = host if host else self.urls['download']
|
||||
if hostname in download_url:
|
||||
if host and hostname in host:
|
||||
return self
|
||||
else:
|
||||
for url_type in self.urls:
|
||||
download_url = self.urls[url_type]
|
||||
if hostname in download_url:
|
||||
return self
|
||||
except:
|
||||
log.debug('Url % s doesn\'t belong to %s' % (url, self.getName()))
|
||||
|
||||
|
||||
@@ -20,19 +20,41 @@ config = [{
|
||||
},
|
||||
{
|
||||
'name': 'meta_nfo',
|
||||
'label': 'NFO',
|
||||
'default': True,
|
||||
'type': 'bool',
|
||||
},
|
||||
{
|
||||
'name': 'meta_nfo_name',
|
||||
'label': 'NFO filename',
|
||||
'default': '%s.nfo',
|
||||
'advanced': True,
|
||||
'description': '<strong>%s</strong> is the rootname of the movie. For example "/path/to/movie cd1.mkv" will be "/path/to/movie"'
|
||||
},
|
||||
{
|
||||
'name': 'meta_fanart',
|
||||
'label': 'Fanart',
|
||||
'default': True,
|
||||
'type': 'bool',
|
||||
},
|
||||
{
|
||||
'name': 'meta_fanart_name',
|
||||
'label': 'Fanart filename',
|
||||
'default': '%s-fanart.jpg',
|
||||
'advanced': True,
|
||||
},
|
||||
{
|
||||
'name': 'meta_thumbnail',
|
||||
'label': 'Thumbnail',
|
||||
'default': True,
|
||||
'type': 'bool',
|
||||
},
|
||||
{
|
||||
'name': 'meta_thumbnail_name',
|
||||
'label': 'Thumbnail filename',
|
||||
'default': '%s.tbn',
|
||||
'advanced': True,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -15,13 +15,13 @@ class XBMC(MetaDataBase):
|
||||
return os.path.join(data['destination_dir'], data['filename'])
|
||||
|
||||
def getFanartName(self, root):
|
||||
return '%s-fanart.jpg' % root
|
||||
return self.conf('meta_fanart_name') % root
|
||||
|
||||
def getThumbnailName(self, root):
|
||||
return '%s.tbn' % root
|
||||
return self.conf('meta_thumbnail_name') % root
|
||||
|
||||
def getNfoName(self, root):
|
||||
return '%s.nfo' % root
|
||||
return self.conf('meta_nfo_name') % root
|
||||
|
||||
def getNfo(self, movie_info = {}, data = {}):
|
||||
nfoxml = Element('movie')
|
||||
|
||||
@@ -27,6 +27,7 @@ class CouchPotatoApi(MovieProvider):
|
||||
|
||||
def getReleaseDate(self, identifier = None):
|
||||
|
||||
if identifier is None: return {}
|
||||
try:
|
||||
headers = {'X-CP-Version': fireEvent('app.version', single = True)}
|
||||
data = self.urlopen((self.api_url % ('eta')) + (identifier + '/'), headers = headers)
|
||||
|
||||
@@ -27,7 +27,7 @@ class IMDBAPI(MovieProvider):
|
||||
|
||||
name_year = fireEvent('scanner.name_year', q, single = True)
|
||||
|
||||
if not name_year.get('name'):
|
||||
if not q or not name_year.get('name'):
|
||||
return []
|
||||
|
||||
cache_key = 'imdbapi.cache.%s' % q
|
||||
@@ -45,6 +45,9 @@ class IMDBAPI(MovieProvider):
|
||||
|
||||
def getInfo(self, identifier = None):
|
||||
|
||||
if not identifier:
|
||||
return {}
|
||||
|
||||
cache_key = 'imdbapi.cache.%s' % identifier
|
||||
cached = self.getCache(cache_key, self.urls['info'] % identifier)
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class Newzbin(NZBProvider, RSS):
|
||||
title = self.getTextElement(nzb, "title")
|
||||
if 'error' in title.lower(): continue
|
||||
|
||||
REPORT_NS = 'http://www.newzbin.com/DTD/2007/feeds/report/';
|
||||
REPORT_NS = 'http://www.newzbin2.es/DTD/2007/feeds/report/';
|
||||
|
||||
# Add attributes to name
|
||||
try:
|
||||
|
||||
@@ -157,9 +157,9 @@ class Newznab(NZBProvider, RSS):
|
||||
|
||||
def getHosts(self):
|
||||
|
||||
uses = str(self.conf('use')).split(',')
|
||||
hosts = self.conf('host').split(',')
|
||||
api_keys = self.conf('api_key').split(',')
|
||||
uses = [x.strip() for x in str(self.conf('use')).split(',')]
|
||||
hosts = [x.strip() for x in self.conf('host').split(',')]
|
||||
api_keys = [x.strip() for x in self.conf('api_key').split(',')]
|
||||
|
||||
list = []
|
||||
for nr in range(len(hosts)):
|
||||
|
||||
@@ -13,10 +13,10 @@ log = CPLog(__name__)
|
||||
class Nzbs(NZBProvider, RSS):
|
||||
|
||||
urls = {
|
||||
'download': 'http://nzbs.org/index.php?action=getnzb&nzbid=%s%s',
|
||||
'nfo': 'http://nzbs.org/index.php?action=view&nzbid=%s&nfo=1',
|
||||
'detail': 'http://nzbs.org/index.php?action=view&nzbid=%s',
|
||||
'api': 'http://nzbs.org/rss.php',
|
||||
'download': 'https://nzbs.org/index.php?action=getnzb&nzbid=%s%s',
|
||||
'nfo': 'https://nzbs.org/index.php?action=view&nzbid=%s&nfo=1',
|
||||
'detail': 'https://nzbs.org/index.php?action=view&nzbid=%s',
|
||||
'api': 'https://nzbs.org/rss.php',
|
||||
}
|
||||
|
||||
cat_ids = [
|
||||
|
||||
@@ -17,6 +17,7 @@ class KickAssTorrents(TorrentProvider):
|
||||
'test': 'http://www.kat.ph/',
|
||||
'detail': 'http://www.kat.ph/%s-t%s.html',
|
||||
'search': 'http://www.kat.ph/%s-i%s/',
|
||||
'download': 'http://torcache.net/',
|
||||
}
|
||||
|
||||
cat_ids = [
|
||||
@@ -127,7 +128,7 @@ class KickAssTorrents(TorrentProvider):
|
||||
return tryInt(age)
|
||||
|
||||
def download(self, url = '', nzb_id = ''):
|
||||
compressed_data = super(KickAssTorrents, self).download(url = url, nzb_id = nzb_id)
|
||||
compressed_data = self.urlopen(url = url, headers = {'Referer': 'http://kat.ph/'})
|
||||
|
||||
compressedstream = StringIO.StringIO(compressed_data)
|
||||
gzipper = gzip.GzipFile(fileobj = compressedstream)
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
<br />
|
||||
You can also use the API over another domain using JSONP, the callback function should be in 'callback_func'
|
||||
<pre><a href="{{ fireEvent('app.api_url', single = True)|safe }}/updater.info/?callback_func=myfunction">{{ fireEvent('app.api_url', single = True)|safe }}/updater.info/?callback_func=myfunction</a></pre>
|
||||
<br />
|
||||
<br />
|
||||
Get the API key:
|
||||
<pre><a href="/getkey/?p=md5(password)&u=md5(username)">/getkey/?p=md5(password)&u=md5(username)</a></pre>
|
||||
Will return {"api_key": "XXXXXXXXXX", "success": true}. When username or password is empty you don't need to md5 it.
|
||||
<br />
|
||||
</div>
|
||||
|
||||
{% for route in routes %}
|
||||
|
||||
Reference in New Issue
Block a user