diff --git a/couchpotato/api.py b/couchpotato/api.py
index b6135583..091de42a 100644
--- a/couchpotato/api.py
+++ b/couchpotato/api.py
@@ -44,12 +44,13 @@ class NonBlockHandler(RequestHandler):
def onNewMessage(self, response):
if self.request.connection.stream.closed():
+ self.on_connection_close()
return
try:
self.finish(response)
except:
- log.error('Failed doing nonblock request: %s', (traceback.format_exc()))
+ log.debug('Failed doing nonblock request, probably already closed: %s', (traceback.format_exc()))
try: self.finish({'success': False, 'error': 'Failed returning results'})
except: pass
diff --git a/couchpotato/core/helpers/encoding.py b/couchpotato/core/helpers/encoding.py
index 6e864446..5fa2e2ad 100644
--- a/couchpotato/core/helpers/encoding.py
+++ b/couchpotato/core/helpers/encoding.py
@@ -38,8 +38,14 @@ def toUnicode(original, *args):
return toUnicode(ascii_text)
def ss(original, *args):
- from couchpotato.environment import Env
- return toUnicode(original, *args).encode(Env.get('encoding'))
+
+ u_original = toUnicode(original, *args)
+ try:
+ from couchpotato.environment import Env
+ return u_original.encode(Env.get('encoding'))
+ except Exception, e:
+ log.debug('Failed ss encoding char, force UTF8: %s', e)
+ return u_original.encode('UTF-8')
def ek(original, *args):
if isinstance(original, (str, unicode)):
diff --git a/couchpotato/core/media/movie/_base/static/movie.actions.js b/couchpotato/core/media/movie/_base/static/movie.actions.js
index 24bf6260..e9f6141f 100644
--- a/couchpotato/core/media/movie/_base/static/movie.actions.js
+++ b/couchpotato/core/media/movie/_base/static/movie.actions.js
@@ -18,11 +18,13 @@ var MovieAction = new Class({
create: function(){},
disable: function(){
- this.el.addClass('disable')
+ if(this.el)
+ this.el.addClass('disable')
},
enable: function(){
- this.el.removeClass('disable')
+ if(this.el)
+ this.el.removeClass('disable')
},
getTitle: function(){
@@ -252,10 +254,10 @@ MA.Release = new Class({
});
if(self.last_release)
- self.release_container.getElement('#release_'+self.last_release.id).addClass('last_release');
+ self.release_container.getElements('#release_'+self.last_release.id).addClass('last_release');
if(self.next_release)
- self.release_container.getElement('#release_'+self.next_release.id).addClass('next_release');
+ self.release_container.getElements('#release_'+self.next_release.id).addClass('next_release');
if(self.next_release || (self.last_release && ['ignored', 'failed'].indexOf(self.last_release.status.identifier) === false)){
@@ -365,21 +367,25 @@ MA.Release = new Class({
var release_el = self.release_container.getElement('#release_'+release.id),
icon = release_el.getElement('.download.icon2');
- icon.addClass('icon spinner').removeClass('download');
+ if(icon)
+ icon.addClass('icon spinner').removeClass('download');
Api.request('release.download', {
'data': {
'id': release.id
},
'onComplete': function(json){
- icon.removeClass('icon spinner');
+ if(icon)
+ icon.removeClass('icon spinner');
if(json.success){
- icon.addClass('completed');
+ if(icon)
+ icon.addClass('completed');
release_el.getElement('.release_status').set('text', 'snatched');
}
else
- icon.addClass('attention').set('title', 'Something went wrong when downloading, please check logs.');
+ if(icon)
+ icon.addClass('attention').set('title', 'Something went wrong when downloading, please check logs.');
}
});
},
@@ -393,11 +399,11 @@ MA.Release = new Class({
},
'onComplete': function(){
var el = release.el;
- if(el.hasClass('failed') || el.hasClass('ignored')){
+ if(el && (el.hasClass('failed') || el.hasClass('ignored'))){
el.removeClass('failed').removeClass('ignored');
el.getElement('.release_status').set('text', 'available');
}
- else {
+ else if(el) {
el.addClass('ignored');
el.getElement('.release_status').set('text', 'ignored');
}
diff --git a/couchpotato/core/media/movie/_base/static/movie.js b/couchpotato/core/media/movie/_base/static/movie.js
index 363d860c..6defc2ad 100644
--- a/couchpotato/core/media/movie/_base/static/movie.js
+++ b/couchpotato/core/media/movie/_base/static/movie.js
@@ -181,18 +181,18 @@ var Movie = new Class({
// Add releases
if(self.data.releases)
self.data.releases.each(function(release){
-
+
var q = self.quality.getElement('.q_id'+ release.quality_id),
status = Status.get(release.status_id);
-
+
if(!q && (status.identifier == 'snatched' || status.identifier == 'done'))
var q = self.addQuality(release.quality_id)
-
+
if (status && q && !q.hasClass(status.identifier)){
q.addClass(status.identifier);
q.set('title', (q.get('title') ? q.get('title') : '') + ' status: '+ status.label)
}
-
+
});
Object.each(self.options.actions, function(action, key){
@@ -256,7 +256,8 @@ var Movie = new Class({
self.el.removeEvents('outerClick')
setTimeout(function(){
- self.el.getElements('> :not(.data):not(.poster):not(.movie_container)').hide();
+ if(self.el)
+ self.el.getElements('> :not(.data):not(.poster):not(.movie_container)').hide();
}, 600);
self.data_container.removeClass('hide_right');
@@ -266,9 +267,10 @@ var Movie = new Class({
changeView: function(new_view){
var self = this;
- self.el
- .removeClass(self.view+'_view')
- .addClass(new_view+'_view')
+ if(self.el)
+ self.el
+ .removeClass(self.view+'_view')
+ .addClass(new_view+'_view')
self.view = new_view;
},
diff --git a/couchpotato/core/notifications/core/main.py b/couchpotato/core/notifications/core/main.py
index a9a20b0a..04acf284 100644
--- a/couchpotato/core/notifications/core/main.py
+++ b/couchpotato/core/notifications/core/main.py
@@ -198,13 +198,16 @@ class CoreNotifier(Notification):
def removeListener(self, callback):
self.m_lock.acquire()
+ new_listeners = []
for list_tuple in self.listeners:
try:
listener, last_id = list_tuple
- if listener == callback:
- self.listeners.remove(list_tuple)
+ if listener != callback:
+ new_listeners.append(list_tuple)
except:
log.debug('Failed removing listener: %s', traceback.format_exc())
+
+ self.listeners = new_listeners
self.m_lock.release()
def cleanMessages(self):
diff --git a/couchpotato/core/plugins/dashboard/main.py b/couchpotato/core/plugins/dashboard/main.py
index 2da4d8cc..f006ac41 100644
--- a/couchpotato/core/plugins/dashboard/main.py
+++ b/couchpotato/core/plugins/dashboard/main.py
@@ -4,9 +4,10 @@ from couchpotato.core.event import fireEvent
from couchpotato.core.helpers.variable import splitString, tryInt
from couchpotato.core.logger import CPLog
from couchpotato.core.plugins.base import Plugin
-from couchpotato.core.settings.model import Movie, Library, LibraryTitle
+from couchpotato.core.settings.model import Movie, Library, LibraryTitle, \
+ Release
from sqlalchemy.orm import joinedload_all
-from sqlalchemy.sql.expression import asc
+from sqlalchemy.sql.expression import asc, or_
import random as rndm
import time
@@ -48,12 +49,14 @@ class Dashboard(Plugin):
limit = tryInt(splt[0])
# Get all active movies
- active_status = fireEvent('status.get', ['active'], single = True)
+ active_status, ignored_status = fireEvent('status.get', ['active', 'ignored'], single = True)
q = db.query(Movie) \
.join(Library) \
+ .outerjoin(Movie.releases) \
.filter(Movie.status_id == active_status.get('id')) \
.with_entities(Movie.id, Movie.profile_id, Library.info, Library.year) \
- .group_by(Movie.id)
+ .group_by(Movie.id) \
+ .filter(or_(Release.id == None, Release.status_id == ignored_status.get('id')))
if not random:
q = q.join(LibraryTitle) \
diff --git a/couchpotato/core/plugins/suggestion/main.py b/couchpotato/core/plugins/suggestion/main.py
index a7d0f82a..eb31d26e 100644
--- a/couchpotato/core/plugins/suggestion/main.py
+++ b/couchpotato/core/plugins/suggestion/main.py
@@ -102,6 +102,6 @@ class Suggestion(Plugin):
if suggestions:
new_suggestions.extend(suggestions)
- self.setCache('suggestion_cached', new_suggestions, timeout = 6048000)
+ self.setCache('suggestion_cached', new_suggestions, timeout = 3024000)
return new_suggestions
diff --git a/couchpotato/core/plugins/suggestion/static/suggest.js b/couchpotato/core/plugins/suggestion/static/suggest.js
index e6226711..40fe53b9 100644
--- a/couchpotato/core/plugins/suggestion/static/suggest.js
+++ b/couchpotato/core/plugins/suggestion/static/suggest.js
@@ -58,54 +58,59 @@ var SuggestList = new Class({
var self = this;
- if(!json) return;
+ if(!json || json.count == 0){
+ self.el.hide();
+ }
+ else {
- Object.each(json.suggestions, function(movie){
+ Object.each(json.suggestions, function(movie){
- var m = new Block.Search.Item(movie, {
- 'onAdded': function(){
- self.afterAdded(m, movie)
- }
- });
- m.data_container.grab(
- new Element('div.actions').adopt(
- new Element('a.add.icon2', {
- 'title': 'Add movie with your default quality',
- 'data-add': movie.imdb,
- 'events': {
- 'click': m.showOptions.bind(m)
- }
- }),
- $(new MA.IMDB(m)),
- $(new MA.Trailer(m, {
- 'height': 150
- })),
- new Element('a.delete.icon2', {
- 'title': 'Don\'t suggest this movie again',
- 'data-ignore': movie.imdb
- }),
- new Element('a.eye-open.icon2', {
- 'title': 'Seen it, like it, don\'t add',
- 'data-seen': movie.imdb
- })
+ var m = new Block.Search.Item(movie, {
+ 'onAdded': function(){
+ self.afterAdded(m, movie)
+ }
+ });
+ m.data_container.grab(
+ new Element('div.actions').adopt(
+ new Element('a.add.icon2', {
+ 'title': 'Add movie with your default quality',
+ 'data-add': movie.imdb,
+ 'events': {
+ 'click': m.showOptions.bind(m)
+ }
+ }),
+ $(new MA.IMDB(m)),
+ $(new MA.Trailer(m, {
+ 'height': 150
+ })),
+ new Element('a.delete.icon2', {
+ 'title': 'Don\'t suggest this movie again',
+ 'data-ignore': movie.imdb
+ }),
+ new Element('a.eye-open.icon2', {
+ 'title': 'Seen it, like it, don\'t add',
+ 'data-seen': movie.imdb
+ })
+ )
+ );
+ m.data_container.removeEvents('click');
+
+ // Add rating
+ m.info_container.adopt(
+ m.rating = m.info.rating && m.info.rating.imdb.length == 2 && parseFloat(m.info.rating.imdb[0]) > 0 ? new Element('span.rating', {
+ 'text': parseFloat(m.info.rating.imdb[0]),
+ 'title': parseInt(m.info.rating.imdb[1]) + ' votes'
+ }) : null,
+ m.genre = m.info.genres && m.info.genres.length > 0 ? new Element('span.genres', {
+ 'text': m.info.genres.slice(0, 3).join(', ')
+ }) : null
)
- );
- m.data_container.removeEvents('click');
- // Add rating
- m.info_container.adopt(
- m.rating = m.info.rating && m.info.rating.imdb.length == 2 && parseFloat(m.info.rating.imdb[0]) > 0 ? new Element('span.rating', {
- 'text': parseFloat(m.info.rating.imdb[0]),
- 'title': parseInt(m.info.rating.imdb[1]) + ' votes'
- }) : null,
- m.genre = m.info.genres && m.info.genres.length > 0 ? new Element('span.genres', {
- 'text': m.info.genres.slice(0, 3).join(', ')
- }) : null
- )
+ $(m).inject(self.el);
- $(m).inject(self.el);
+ });
- });
+ }
self.fireEvent('loaded');
diff --git a/couchpotato/core/plugins/userscript/static/userscript.js b/couchpotato/core/plugins/userscript/static/userscript.js
index b7849766..2aeb7b5f 100644
--- a/couchpotato/core/plugins/userscript/static/userscript.js
+++ b/couchpotato/core/plugins/userscript/static/userscript.js
@@ -96,7 +96,7 @@ var UserscriptSettingTab = new Class({
})
)
).setStyles({
- 'background-image': "url('"+Api.createUrl('static/userscript/userscript.png')+"')"
+ 'background-image': "url('"+App.createUrl('static/plugin/userscript/userscript.png')+"')"
});
});
diff --git a/couchpotato/core/plugins/wizard/static/wizard.js b/couchpotato/core/plugins/wizard/static/wizard.js
index 71910e27..b4857abb 100644
--- a/couchpotato/core/plugins/wizard/static/wizard.js
+++ b/couchpotato/core/plugins/wizard/static/wizard.js
@@ -24,9 +24,10 @@ Page.Wizard = new Class({
'title': 'What download apps are you using?',
'description': 'CP needs an external download app to work with. Choose one below. For more downloaders check settings after you have filled in the wizard. If your download app isn\'t in the list, use the default Blackhole.'
},
- 'providers': {
+ 'searcher': {
+ 'label': 'Providers',
'title': 'Are you registered at any of these sites?',
- 'description': 'CP uses these sites to search for movies. A few free are enabled by default, but it\'s always better to have a few more. Check settings for the full list of available providers.'
+ 'description': 'CP uses these sites to search for movies. A few free are enabled by default, but it\'s always better to have more.'
},
'renamer': {
'title': 'Move & rename the movies after downloading?',
@@ -38,7 +39,7 @@ Page.Wizard = new Class({
'
Once installed, just click the bookmarklet on a movie page and watch the magic happen ;)',
'content': function(){
return App.createUserscriptButtons().setStyles({
- 'background-image': "url('"+Api.createUrl('static/userscript/userscript.png')+"')"
+ 'background-image': "url('"+App.createUrl('static/plugin/userscript/userscript.png')+"')"
})
}
},
@@ -76,7 +77,7 @@ Page.Wizard = new Class({
)
}
},
- groups: ['welcome', 'general', 'downloaders', 'searcher', 'providers', 'renamer', 'automation', 'finish'],
+ groups: ['welcome', 'general', 'downloaders', 'searcher', 'renamer', 'automation', 'finish'],
open: function(action, params){
var self = this;
@@ -195,8 +196,7 @@ Page.Wizard = new Class({
self.el.getElement('.advanced_toggle').destroy();
// Hide retention
- self.el.getElement('.tab_searcher').hide();
- self.el.getElement('.t_searcher').hide();
+ self.el.getElement('.section_nzb').hide();
// Add pointer
new Element('.tab_wrapper').wraps(tabs);
diff --git a/couchpotato/core/providers/nzb/binsearch/main.py b/couchpotato/core/providers/nzb/binsearch/main.py
index dee5fc78..770ed50a 100644
--- a/couchpotato/core/providers/nzb/binsearch/main.py
+++ b/couchpotato/core/providers/nzb/binsearch/main.py
@@ -56,6 +56,10 @@ class BinSearch(NZBProvider):
info = row.find('span', attrs = {'class':'d'})
size_match = re.search('size:.(?P[0-9\.]+.[GMB]+)', info.text)
+ age = 0
+ try: age = re.search('(?P\d+d)', row.find_all('td')[-1:][0].text).group('size')[:-1]
+ except: pass
+
def extra_check(item):
parts = re.search('available:.(?P\d+)./.(?P\d+)', info.text)
total = tryInt(parts.group('total'))
@@ -74,7 +78,7 @@ class BinSearch(NZBProvider):
results.append({
'id': nzb_id,
'name': title.text,
- 'age': tryInt(re.search('(?P\d+d)', row.find_all('td')[-1:][0].text).group('size')[:-1]),
+ 'age': tryInt(age),
'size': self.parseSize(size_match.group('size')),
'url': self.urls['download'] % nzb_id,
'detail_url': self.urls['detail'] % info.find('a')['href'],
diff --git a/couchpotato/core/providers/nzb/ftdworld/__init__.py b/couchpotato/core/providers/nzb/ftdworld/__init__.py
deleted file mode 100644
index 5a004a70..00000000
--- a/couchpotato/core/providers/nzb/ftdworld/__init__.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from .main import FTDWorld
-
-def start():
- return FTDWorld()
-
-config = [{
- 'name': 'ftdworld',
- 'groups': [
- {
- 'tab': 'searcher',
- 'list': 'nzb_providers',
- 'name': 'FTDWorld',
- 'description': 'Free provider, less accurate. See FTDWorld',
- 'wizard': True,
- 'options': [
- {
- 'name': 'enabled',
- 'type': 'enabler',
- },
- {
- 'name': 'username',
- 'default': '',
- },
- {
- 'name': 'password',
- 'default': '',
- 'type': 'password',
- },
- {
- 'name': 'extra_score',
- 'advanced': True,
- 'label': 'Extra Score',
- 'type': 'int',
- 'default': 0,
- 'description': 'Starting score for each release found via this provider.',
- }
- ],
- },
- ],
-}]
diff --git a/couchpotato/core/providers/nzb/ftdworld/main.py b/couchpotato/core/providers/nzb/ftdworld/main.py
deleted file mode 100644
index 9940cee6..00000000
--- a/couchpotato/core/providers/nzb/ftdworld/main.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from couchpotato.core.helpers.encoding import toUnicode, tryUrlencode
-from couchpotato.core.helpers.variable import tryInt
-from couchpotato.core.logger import CPLog
-from couchpotato.core.providers.nzb.base import NZBProvider
-from couchpotato.environment import Env
-import json
-import traceback
-
-log = CPLog(__name__)
-
-
-class FTDWorld(NZBProvider):
-
- urls = {
- 'search': 'http://ftdworld.net/api/index.php?%s',
- 'detail': 'http://ftdworld.net/spotinfo.php?id=%s',
- 'download': 'http://ftdworld.net/cgi-bin/nzbdown.pl?fileID=%s',
- 'login': 'http://ftdworld.net/api/login.php',
- 'login_check': 'http://ftdworld.net/api/login.php',
- }
-
- http_time_between_calls = 3 #seconds
-
- cat_ids = [
- ([4, 11], ['dvdr']),
- ([1], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr', 'brrip']),
- ([7, 10, 13, 14], ['bd50', '720p', '1080p']),
- ]
- cat_backup_id = 1
-
- def _searchOnTitle(self, title, movie, quality, results):
-
- q = '"%s" %s' % (title, movie['library']['year'])
-
- params = tryUrlencode({
- 'ctitle': q,
- 'customQuery': 'usr',
- 'cage': Env.setting('retention', 'nzb'),
- 'csizemin': quality.get('size_min'),
- 'csizemax': quality.get('size_max'),
- 'ccategory': 14,
- 'ctype': ','.join([str(x) for x in self.getCatId(quality['identifier'])]),
- })
-
- data = self.getJsonData(self.urls['search'] % params, opener = self.login_opener)
-
- if data:
- try:
-
- if data.get('numRes') == 0:
- return
-
- for item in data.get('data'):
-
- nzb_id = tryInt(item.get('id'))
- results.append({
- 'id': nzb_id,
- 'name': toUnicode(item.get('Title')),
- 'age': self.calculateAge(tryInt(item.get('Created'))),
- 'size': item.get('Size', 0),
- 'url': self.urls['download'] % nzb_id,
- 'detail_url': self.urls['detail'] % nzb_id,
- 'score': (tryInt(item.get('webPlus', 0)) - tryInt(item.get('webMin', 0))) * 3,
- })
-
- except:
- log.error('Failed to parse HTML response from FTDWorld: %s', traceback.format_exc())
-
- def getLoginParams(self):
- return tryUrlencode({
- 'userlogin': self.conf('username'),
- 'passlogin': self.conf('password'),
- 'submit': 'Log In',
- })
-
- def loginSuccess(self, output):
- try:
- return json.loads(output).get('goodToGo', False)
- except:
- return False
-
- loginCheckSuccess = loginSuccess
-
diff --git a/couchpotato/core/providers/nzb/newznab/__init__.py b/couchpotato/core/providers/nzb/newznab/__init__.py
index 3902ab13..54359275 100644
--- a/couchpotato/core/providers/nzb/newznab/__init__.py
+++ b/couchpotato/core/providers/nzb/newznab/__init__.py
@@ -20,6 +20,7 @@ config = [{
{
'name': 'enabled',
'type': 'enabler',
+ 'default': True,
},
{
'name': 'use',