Browse Source

Merge branch 'refs/heads/develop'

pull/228/head
Ruud 13 years ago
parent
commit
ca87f2c231
  1. 18
      couchpotato/__init__.py
  2. 2
      couchpotato/core/_base/_core/__init__.py
  3. 3
      couchpotato/core/_base/updater/main.py
  4. 2
      couchpotato/core/_base/updater/static/updater.js
  5. 4
      couchpotato/core/notifications/core/main.py
  6. 2
      couchpotato/core/notifications/notifymyandroid/main.py
  7. 2
      couchpotato/core/notifications/notifymywp/main.py
  8. 2
      couchpotato/core/plugins/manage/main.py
  9. 6
      couchpotato/core/plugins/movie/main.py
  10. 5
      couchpotato/core/plugins/movie/static/movie.css
  11. 6
      couchpotato/core/plugins/movie/static/movie.js
  12. 11
      couchpotato/core/plugins/quality/main.py
  13. 6
      couchpotato/core/plugins/renamer/main.py
  14. 7
      couchpotato/core/plugins/scanner/main.py
  15. 2
      couchpotato/core/plugins/score/scores.py
  16. 25
      couchpotato/core/plugins/searcher/main.py
  17. 6
      couchpotato/core/providers/base.py
  18. 22
      couchpotato/core/providers/metadata/xbmc/__init__.py
  19. 6
      couchpotato/core/providers/metadata/xbmc/main.py
  20. 1
      couchpotato/core/providers/movie/couchpotatoapi/main.py
  21. 5
      couchpotato/core/providers/movie/imdbapi/main.py
  22. 2
      couchpotato/core/providers/nzb/newzbin/main.py
  23. 6
      couchpotato/core/providers/nzb/newznab/main.py
  24. 8
      couchpotato/core/providers/nzb/nzbs/main.py
  25. 3
      couchpotato/core/providers/torrent/kickasstorrents/main.py
  26. 6
      couchpotato/templates/api.html

18
couchpotato/__init__.py

@ -1,6 +1,8 @@
from couchpotato.api import api_docs, api_docs_missing from couchpotato.api import api_docs, api_docs_missing
from couchpotato.core.auth import requires_auth from couchpotato.core.auth import requires_auth
from couchpotato.core.event import fireEvent 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.core.logger import CPLog
from couchpotato.environment import Env from couchpotato.environment import Env
from flask.app import Flask from flask.app import Flask
@ -51,6 +53,22 @@ def apiDocs():
del api_docs_missing[''] 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)) 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) @app.errorhandler(404)
def page_not_found(error): def page_not_found(error):
index_url = url_for('web.index') index_url = url_for('web.index')

2
couchpotato/core/_base/_core/__init__.py

@ -55,7 +55,7 @@ config = [{
'name': 'api_key', 'name': 'api_key',
'default': uuid4().hex, 'default': uuid4().hex,
'readonly': 1, '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', 'name': 'debug',

3
couchpotato/core/_base/updater/main.py

@ -96,7 +96,8 @@ class BaseUpdater(Plugin):
'last_check': self.last_check, 'last_check': self.last_check,
'update_version': self.update_version, 'update_version': self.update_version,
'version': self.getVersion(), '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): def check(self):

2
couchpotato/core/_base/updater/static/updater.js

@ -79,6 +79,8 @@ var UpdaterBase = new Class({
if(json.success){ if(json.success){
App.restart('Please wait while CouchPotato is being updated with more awesome stuff.', 'Updating'); App.restart('Please wait while CouchPotato is being updated with more awesome stuff.', 'Updating');
App.checkAvailable.delay(500, App); App.checkAvailable.delay(500, App);
if(self.message)
self.message.destroy();
} }
} }
}); });

4
couchpotato/core/notifications/core/main.py

@ -56,7 +56,7 @@ class CoreNotifier(Notification):
addEvent('library.update_finish', lambda data: fireEvent('notify.frontend', type = 'library.update', data = data)) addEvent('library.update_finish', lambda data: fireEvent('notify.frontend', type = 'library.update', data = data))
def markAsRead(self): def markAsRead(self):
ids = getParam('ids').split(',') ids = [x.strip() for x in getParam('ids').split(',')]
db = get_session() db = get_session()
@ -78,7 +78,7 @@ class CoreNotifier(Notification):
q = db.query(Notif) q = db.query(Notif)
if limit_offset: if limit_offset:
splt = limit_offset.split(',') splt = [x.strip() for x in limit_offset.split(',')]
limit = splt[0] limit = splt[0]
offset = 0 if len(splt) is 1 else splt[1] offset = 0 if len(splt) is 1 else splt[1]
q = q.limit(limit).offset(offset) q = q.limit(limit).offset(offset)

2
couchpotato/core/notifications/notifymyandroid/main.py

@ -11,7 +11,7 @@ class NotifyMyAndroid(Notification):
if self.isDisabled(): return if self.isDisabled(): return
nma = pynma.PyNMA() nma = pynma.PyNMA()
keys = self.conf('api_key').split(',') keys = [x.strip() for x in self.conf('api_key').split(',')]
nma.addkey(keys) nma.addkey(keys)
nma.developerkey(self.conf('dev_key')) nma.developerkey(self.conf('dev_key'))

2
couchpotato/core/notifications/notifymywp/main.py

@ -10,7 +10,7 @@ class NotifyMyWP(Notification):
def notify(self, message = '', data = {}): def notify(self, message = '', data = {}):
if self.isDisabled(): return 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')) 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) response = p.push(application = self.default_title, event = message, description = message, priority = self.conf('priority'), batch_mode = len(keys) > 1)

2
couchpotato/core/plugins/manage/main.py

@ -77,6 +77,6 @@ class Manage(Plugin):
def directories(self): def directories(self):
try: try:
return self.conf('library', default = '').split('::') return [x.strip() for x in self.conf('library', default = '').split('::')]
except: except:
return [] return []

6
couchpotato/core/plugins/movie/main.py

@ -139,7 +139,7 @@ class MoviePlugin(Plugin):
if limit_offset: if limit_offset:
splt = limit_offset.split(',') splt = [x.strip() for x in limit_offset.split(',')]
limit = splt[0] limit = splt[0]
offset = 0 if len(splt) is 1 else splt[1] offset = 0 if len(splt) is 1 else splt[1]
q2 = q2.limit(limit).offset(offset) q2 = q2.limit(limit).offset(offset)
@ -324,7 +324,7 @@ class MoviePlugin(Plugin):
available_status = fireEvent('status.get', 'available', single = True) 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: for movie_id in ids:
m = db.query(Movie).filter_by(id = movie_id).first() m = db.query(Movie).filter_by(id = movie_id).first()
@ -356,7 +356,7 @@ class MoviePlugin(Plugin):
params = getParams() params = getParams()
ids = params.get('id').split(',') ids = [x.strip() for x in params.get('id').split(',')]
for movie_id in ids: for movie_id in ids:
self.delete(movie_id) self.delete(movie_id)

5
couchpotato/core/plugins/movie/static/movie.css

@ -279,7 +279,8 @@
border: 0; border: 0;
} }
.movies .options .table .provider { .movies .options .table .provider {
width: 130px; width: 120px;
text-overflow: ellipsis;
} }
.movies .options .table .name { .movies .options .table .name {
width: 350px; width: 350px;
@ -290,6 +291,8 @@
.movies .options .table.files .name { width: 605px; } .movies .options .table.files .name { width: 605px; }
.movies .options .table .type { width: 130px; } .movies .options .table .type { width: 130px; }
.movies .options .table .is_available { width: 90px; } .movies .options .table .is_available { width: 90px; }
.movies .options .table .age,
.movies .options .table .size { width: 40px; }
.movies .options .table a { .movies .options .table a {
width: 30px !important; width: 30px !important;

6
couchpotato/core/plugins/movie/static/movie.js

@ -270,8 +270,9 @@ var ReleaseAction = new Class({
// Header // Header
new Element('div.item.head').adopt( new Element('div.item.head').adopt(
new Element('span.name', {'text': 'Release name'}), new Element('span.name', {'text': 'Release name'}),
new Element('span.status', {'text': 'Status'}),
new Element('span.quality', {'text': 'Quality'}), 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.age', {'text': 'Age'}),
new Element('span.score', {'text': 'Score'}), new Element('span.score', {'text': 'Score'}),
new Element('span.provider', {'text': 'Provider'}) new Element('span.provider', {'text': 'Provider'})
@ -288,9 +289,10 @@ var ReleaseAction = new Class({
} catch(e){} } catch(e){}
new Element('div', { new Element('div', {
'class': 'item ' + status.identifier 'class': 'item'
}).adopt( }).adopt(
new Element('span.name', {'text': self.get(release, 'name'), 'title': self.get(release, 'name')}), 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.quality', {'text': quality.get('label')}),
new Element('span.size', {'text': (self.get(release, 'size') || 'unknown')}), new Element('span.size', {'text': (self.get(release, 'size') || 'unknown')}),
new Element('span.age', {'text': self.get(release, 'age')}), new Element('span.age', {'text': self.get(release, 'age')}),

11
couchpotato/core/plugins/quality/main.py

@ -18,7 +18,7 @@ class QualityPlugin(Plugin):
qualities = [ qualities = [
{'identifier': 'bd50', 'hd': True, 'size': (15000, 60000), 'label': 'BR-Disk', 'alternative': ['bd25'], 'allow': ['1080p'], 'ext':[], 'tags': ['bdmv', 'certificate', ('complete', 'bluray')]}, {'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': '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': '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': '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']}, {'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: for cur_file in files:
size = (os.path.getsize(cur_file) / 1024 / 1024) if os.path.isfile(cur_file) else 0 size = (os.path.getsize(cur_file) / 1024 / 1024) if os.path.isfile(cur_file) else 0
words = re.split('\W+', cur_file.lower()) words = re.split('\W+', cur_file.lower())
safe_cur_file = toSafeString(cur_file)
for quality in self.all(): for quality in self.all():
# Check tags # Check tags
if quality['identifier'] in words: 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) return self.setCache(hash, quality)
if list(set(quality.get('alternative', [])) & set(words)): 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) return self.setCache(hash, quality)
for tag in quality.get('tags', []): for tag in quality.get('tags', []):
if isinstance(tag, tuple) and '.'.join(tag) in '.'.join(words): 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) return self.setCache(hash, quality)
if list(set(quality.get('tags', [])) & set(words)): 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) return self.setCache(hash, quality)
# Check on unreliable stuff # Check on unreliable stuff

6
couchpotato/core/plugins/renamer/main.py

@ -328,7 +328,7 @@ class Renamer(Plugin):
try: try:
os.remove(src) os.remove(src)
except: except:
log.error('Failed removing %s: %s', (src, traceback.format_exc())) log.error('Failed removing %s: %s' % (src, traceback.format_exc()))
# Remove matching releases # Remove matching releases
for release in remove_releases: for release in remove_releases:
@ -336,14 +336,14 @@ class Renamer(Plugin):
try: try:
db.delete(release) db.delete(release)
except: 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']: if group['dirname'] and group['parentdir']:
try: try:
log.info('Deleting folder: %s' % group['parentdir']) log.info('Deleting folder: %s' % group['parentdir'])
self.deleteEmptyFolder(group['parentdir']) self.deleteEmptyFolder(group['parentdir'])
except: 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 # Search for trailers etc
fireEventAsync('renamer.after', group) fireEventAsync('renamer.after', group)

7
couchpotato/core/plugins/scanner/main.py

@ -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 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'] ignore_names = ['extract', 'extracting', 'extracted', 'movie', 'movies', 'film', 'films', 'download', 'downloads', 'video_ts', 'audio_ts', 'bdmv', 'certificate']
extensions = { 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'], 'movie_extra': ['mds'],
'dvd': ['vts_*', 'vob'], 'dvd': ['vts_*', 'vob'],
'nfo': ['nfo', 'txt', 'tag'], 'nfo': ['nfo', 'txt', 'tag'],
@ -165,6 +165,9 @@ class Scanner(Plugin):
for file_path in files: for file_path in files:
if not os.path.exists(file_path):
continue
# Remove ignored files # Remove ignored files
if self.isSampleFile(file_path): if self.isSampleFile(file_path):
leftovers.append(file_path) leftovers.append(file_path)
@ -263,7 +266,7 @@ class Scanner(Plugin):
file_too_new = tryInt(time.time() - file_time) file_too_new = tryInt(time.time() - file_time)
break 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)) log.info('Files seem to be still unpacking or just unpacked (created on %s), ignoring for now: %s' % (time.ctime(file_time), identifier))
continue continue

2
couchpotato/core/plugins/score/scores.py

@ -40,7 +40,7 @@ def nameScore(name, year):
# Contains preferred word # Contains preferred word
nzb_words = re.split('\W+', simplifyString(name)) 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: for word in preferred_words:
if word.strip() and word.strip().lower() in nzb_words: if word.strip() and word.strip().lower() in nzb_words:
score = score + 100 score = score + 100

25
couchpotato/core/plugins/searcher/main.py

@ -123,7 +123,11 @@ class Searcher(Plugin):
for nzb in sorted_results: 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: else:
log.info('Better quality (%s) already available or snatched for %s' % (quality_type['quality']['label'], default_title)) log.info('Better quality (%s) already available or snatched for %s' % (quality_type['quality']['label'], default_title))
fireEvent('movie.restatus', movie['id']) 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'])) log.info('Wrong: Outside retention, age is %s, needs %s or lower: %s' % (nzb['age'], retention, nzb['name']))
return False return False
nzb_words = re.split('\W+', simplifyString(nzb['name'])) movie_name = simplifyString(nzb['name'])
required_words = self.conf('required_words').split(',') 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)): 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.") log.info("NZB doesn't contain any of the required words.")
return False 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)) blacklisted = list(set(nzb_words) & set(ignored_words))
if self.conf('ignored_words') and blacklisted: if self.conf('ignored_words') and blacklisted:
log.info("Wrong: '%s' blacklisted words: %s" % (nzb['name'], ", ".join(blacklisted))) log.info("Wrong: '%s' blacklisted words: %s" % (nzb['name'], ", ".join(blacklisted)))
return False 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) #qualities = fireEvent('quality.all', single = True)
preferred_quality = fireEvent('quality.single', identifier = quality['identifier'], 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) check_movie = fireEvent('scanner.name_year', check_name, single = True)
try: try:
check_words = re.split('\W+', check_movie.get('name', '')) check_words = filter(None, re.split('\W+', check_movie.get('name', '')))
movie_words = re.split('\W+', simplifyString(movie_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 return True
except: except:
pass pass

6
couchpotato/core/providers/base.py

@ -65,7 +65,11 @@ class YarrProvider(Provider):
def belongsTo(self, url, host = None): def belongsTo(self, url, host = None):
try: try:
hostname = urlparse(url).hostname hostname = urlparse(url).hostname
download_url = host if host else self.urls['download'] 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: if hostname in download_url:
return self return self
except: except:

22
couchpotato/core/providers/metadata/xbmc/__init__.py

@ -20,19 +20,41 @@ config = [{
}, },
{ {
'name': 'meta_nfo', 'name': 'meta_nfo',
'label': 'NFO',
'default': True, 'default': True,
'type': 'bool', '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', 'name': 'meta_fanart',
'label': 'Fanart',
'default': True, 'default': True,
'type': 'bool', 'type': 'bool',
}, },
{ {
'name': 'meta_fanart_name',
'label': 'Fanart filename',
'default': '%s-fanart.jpg',
'advanced': True,
},
{
'name': 'meta_thumbnail', 'name': 'meta_thumbnail',
'label': 'Thumbnail',
'default': True, 'default': True,
'type': 'bool', 'type': 'bool',
}, },
{
'name': 'meta_thumbnail_name',
'label': 'Thumbnail filename',
'default': '%s.tbn',
'advanced': True,
},
], ],
}, },
], ],

6
couchpotato/core/providers/metadata/xbmc/main.py

@ -15,13 +15,13 @@ class XBMC(MetaDataBase):
return os.path.join(data['destination_dir'], data['filename']) return os.path.join(data['destination_dir'], data['filename'])
def getFanartName(self, root): def getFanartName(self, root):
return '%s-fanart.jpg' % root return self.conf('meta_fanart_name') % root
def getThumbnailName(self, root): def getThumbnailName(self, root):
return '%s.tbn' % root return self.conf('meta_thumbnail_name') % root
def getNfoName(self, root): def getNfoName(self, root):
return '%s.nfo' % root return self.conf('meta_nfo_name') % root
def getNfo(self, movie_info = {}, data = {}): def getNfo(self, movie_info = {}, data = {}):
nfoxml = Element('movie') nfoxml = Element('movie')

1
couchpotato/core/providers/movie/couchpotatoapi/main.py

@ -27,6 +27,7 @@ class CouchPotatoApi(MovieProvider):
def getReleaseDate(self, identifier = None): def getReleaseDate(self, identifier = None):
if identifier is None: return {}
try: try:
headers = {'X-CP-Version': fireEvent('app.version', single = True)} headers = {'X-CP-Version': fireEvent('app.version', single = True)}
data = self.urlopen((self.api_url % ('eta')) + (identifier + '/'), headers = headers) data = self.urlopen((self.api_url % ('eta')) + (identifier + '/'), headers = headers)

5
couchpotato/core/providers/movie/imdbapi/main.py

@ -27,7 +27,7 @@ class IMDBAPI(MovieProvider):
name_year = fireEvent('scanner.name_year', q, single = True) 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 [] return []
cache_key = 'imdbapi.cache.%s' % q cache_key = 'imdbapi.cache.%s' % q
@ -45,6 +45,9 @@ class IMDBAPI(MovieProvider):
def getInfo(self, identifier = None): def getInfo(self, identifier = None):
if not identifier:
return {}
cache_key = 'imdbapi.cache.%s' % identifier cache_key = 'imdbapi.cache.%s' % identifier
cached = self.getCache(cache_key, self.urls['info'] % identifier) cached = self.getCache(cache_key, self.urls['info'] % identifier)

2
couchpotato/core/providers/nzb/newzbin/main.py

@ -89,7 +89,7 @@ class Newzbin(NZBProvider, RSS):
title = self.getTextElement(nzb, "title") title = self.getTextElement(nzb, "title")
if 'error' in title.lower(): continue 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 # Add attributes to name
try: try:

6
couchpotato/core/providers/nzb/newznab/main.py

@ -157,9 +157,9 @@ class Newznab(NZBProvider, RSS):
def getHosts(self): def getHosts(self):
uses = str(self.conf('use')).split(',') uses = [x.strip() for x in str(self.conf('use')).split(',')]
hosts = self.conf('host').split(',') hosts = [x.strip() for x in self.conf('host').split(',')]
api_keys = self.conf('api_key').split(',') api_keys = [x.strip() for x in self.conf('api_key').split(',')]
list = [] list = []
for nr in range(len(hosts)): for nr in range(len(hosts)):

8
couchpotato/core/providers/nzb/nzbs/main.py

@ -13,10 +13,10 @@ log = CPLog(__name__)
class Nzbs(NZBProvider, RSS): class Nzbs(NZBProvider, RSS):
urls = { urls = {
'download': 'http://nzbs.org/index.php?action=getnzb&nzbid=%s%s', 'download': 'https://nzbs.org/index.php?action=getnzb&nzbid=%s%s',
'nfo': 'http://nzbs.org/index.php?action=view&nzbid=%s&nfo=1', 'nfo': 'https://nzbs.org/index.php?action=view&nzbid=%s&nfo=1',
'detail': 'http://nzbs.org/index.php?action=view&nzbid=%s', 'detail': 'https://nzbs.org/index.php?action=view&nzbid=%s',
'api': 'http://nzbs.org/rss.php', 'api': 'https://nzbs.org/rss.php',
} }
cat_ids = [ cat_ids = [

3
couchpotato/core/providers/torrent/kickasstorrents/main.py

@ -17,6 +17,7 @@ class KickAssTorrents(TorrentProvider):
'test': 'http://www.kat.ph/', 'test': 'http://www.kat.ph/',
'detail': 'http://www.kat.ph/%s-t%s.html', 'detail': 'http://www.kat.ph/%s-t%s.html',
'search': 'http://www.kat.ph/%s-i%s/', 'search': 'http://www.kat.ph/%s-i%s/',
'download': 'http://torcache.net/',
} }
cat_ids = [ cat_ids = [
@ -127,7 +128,7 @@ class KickAssTorrents(TorrentProvider):
return tryInt(age) return tryInt(age)
def download(self, url = '', nzb_id = ''): 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) compressedstream = StringIO.StringIO(compressed_data)
gzipper = gzip.GzipFile(fileobj = compressedstream) gzipper = gzip.GzipFile(fileobj = compressedstream)

6
couchpotato/templates/api.html

@ -18,6 +18,12 @@
<br /> <br />
You can also use the API over another domain using JSONP, the callback function should be in 'callback_func' 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> <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)&amp;u=md5(username)">/getkey/?p=md5(password)&amp;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> </div>
{% for route in routes %} {% for route in routes %}

Loading…
Cancel
Save