Compare commits

...

16 Commits

Author SHA1 Message Date
Ruud
8d108b92bf One Up 2013-09-23 21:48:12 +02:00
Ruud
46783028b1 Merge branch 'refs/heads/develop' into desktop 2013-09-23 21:36:45 +02:00
Ruud
b5d2a41d60 Enable NewzNab bij default 2013-09-23 21:35:40 +02:00
Ruud
cc3aad49ed Remove FTDWorld 2013-09-23 21:35:29 +02:00
Ruud
2365e1859f Don't show suggestions if there aren't any. fix #2153 2013-09-22 10:47:13 +02:00
Ruud
03700e0a04 Userscript image didn't show 2013-09-22 00:43:50 +02:00
Ruud
1ff4901846 Make sure to remove listener, even after fail 2013-09-21 22:29:15 +02:00
Ruud
d70a71a12e Make nonblock debug message 2013-09-21 22:17:01 +02:00
Ruud
866d9621cb Create new listener list 2013-09-21 22:16:44 +02:00
Ruud
2d3fc03a00 Revert back to UTF8 when ss encoding fails. fix #2220 2013-09-21 13:56:17 +02:00
Ruud
19f782e4a5 Don't try to change elements that don't exist. fix #2219 2013-09-21 12:41:06 +02:00
Ruud
fdd851d29a Binsearch age parse failed for release new than 1 day. fix #2217 2013-09-21 12:14:40 +02:00
Ruud
6cd38a3469 Providers missing in wizard 2013-09-21 11:20:53 +02:00
Ruud
bfa3b87188 Only show soon and late with no releases 2013-09-21 11:07:16 +02:00
Ruud
69a9fa1193 Simplify string before checking on imdb 2013-09-20 18:08:27 +02:00
Ruud
9e0805ec89 Hide IE clear button on search 2013-09-20 18:08:12 +02:00
16 changed files with 111 additions and 203 deletions

View File

@@ -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

View File

@@ -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)):

View File

@@ -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');
}

View File

@@ -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;
},

View File

@@ -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):

View File

@@ -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) \

View File

@@ -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

View File

@@ -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');

View File

@@ -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')+"')"
});
});

View File

@@ -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({
'<br />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);

View File

@@ -56,6 +56,10 @@ class BinSearch(NZBProvider):
info = row.find('span', attrs = {'class':'d'})
size_match = re.search('size:.(?P<size>[0-9\.]+.[GMB]+)', info.text)
age = 0
try: age = re.search('(?P<size>\d+d)', row.find_all('td')[-1:][0].text).group('size')[:-1]
except: pass
def extra_check(item):
parts = re.search('available:.(?P<parts>\d+)./.(?P<total>\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<size>\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'],

View File

@@ -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 <a href="http://ftdworld.net">FTDWorld</a>',
'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.',
}
],
},
],
}]

View File

@@ -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

View File

@@ -20,6 +20,7 @@ config = [{
{
'name': 'enabled',
'type': 'enabler',
'default': True,
},
{
'name': 'use',

View File

@@ -1,5 +1,5 @@
#define MyAppName "CouchPotato"
#define MyAppVer "2.2.0"
#define MyAppVer "2.2.1"
[Setup]
AppName={#MyAppName}

View File

@@ -1,2 +1,2 @@
VERSION = '2.2.0'
VERSION = '2.2.1'
BRANCH = 'desktop'