Merge branch 'refs/heads/develop'
This commit is contained in:
@@ -56,7 +56,7 @@ class Updater(Plugin):
|
||||
fireEventAsync('app.crappy_restart')
|
||||
else:
|
||||
if self.conf('notification'):
|
||||
fireEvent('updater.available', message = 'A new update is available', data = self.updater.getVersion())
|
||||
fireEvent('updater.available', message = 'A new update is available', data = self.updater.info())
|
||||
|
||||
def info(self):
|
||||
return self.updater.info()
|
||||
|
||||
@@ -15,7 +15,7 @@ def toSafeString(original):
|
||||
def simplifyString(original):
|
||||
string = stripAccents(original.lower())
|
||||
string = toSafeString(' '.join(re.split('\W+', string)))
|
||||
split = re.split('\W+', string.lower())
|
||||
split = re.split('\W+|_', string.lower())
|
||||
return toUnicode(' '.join(split))
|
||||
|
||||
def toUnicode(original, *args):
|
||||
|
||||
@@ -3,7 +3,7 @@ from couchpotato.core.helpers.variable import natcmp
|
||||
from flask.globals import current_app
|
||||
from flask.helpers import json
|
||||
from libs.werkzeug.urls import url_decode
|
||||
from urllib import unquote_plus
|
||||
from urllib import unquote
|
||||
import flask
|
||||
import re
|
||||
|
||||
@@ -26,7 +26,7 @@ def getParams():
|
||||
|
||||
for item in nested:
|
||||
if item is nested[-1]:
|
||||
current[item] = toUnicode(unquote_plus(value)).encode('utf-8')
|
||||
current[item] = toUnicode(unquote(value)).encode('utf-8')
|
||||
else:
|
||||
try:
|
||||
current[item]
|
||||
@@ -35,7 +35,7 @@ def getParams():
|
||||
|
||||
current = current[item]
|
||||
else:
|
||||
temp[param] = toUnicode(unquote_plus(value)).encode('utf-8')
|
||||
temp[param] = toUnicode(unquote(value)).encode('utf-8')
|
||||
|
||||
return dictToList(temp)
|
||||
|
||||
@@ -57,7 +57,7 @@ def dictToList(params):
|
||||
|
||||
def getParam(attr, default = None):
|
||||
try:
|
||||
return toUnicode(unquote_plus(getattr(flask.request, 'args').get(attr, default))).encode('utf-8')
|
||||
return toUnicode(unquote(getattr(flask.request, 'args').get(attr, default))).encode('utf-8')
|
||||
except:
|
||||
return default
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ class Plugin(object):
|
||||
log.error("Something went wrong when finishing the plugin function. Could not find the 'is_running' key")
|
||||
|
||||
|
||||
def getCache(self, cache_key, url = None, timeout = 300, url_timeout = 10):
|
||||
def getCache(self, cache_key, url = None, **kwargs):
|
||||
cache = Env.get('cache').get(cache_key)
|
||||
if cache:
|
||||
if not Env.get('dev'): log.debug('Getting cache %s' % cache_key)
|
||||
@@ -206,8 +206,14 @@ class Plugin(object):
|
||||
|
||||
if url:
|
||||
try:
|
||||
data = self.urlopen(url, timeout = url_timeout)
|
||||
self.setCache(cache_key, data, timeout = timeout)
|
||||
|
||||
cache_timeout = 300
|
||||
if kwargs.get('cache_timeout'):
|
||||
cache_timeout = kwargs.get('cache_timeout')
|
||||
del kwargs['cache_timeout']
|
||||
|
||||
data = self.urlopen(url, **kwargs)
|
||||
self.setCache(cache_key, data, timeout = cache_timeout)
|
||||
return data
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -68,6 +68,7 @@ class LibraryPlugin(Plugin):
|
||||
do_update = False
|
||||
else:
|
||||
info = fireEvent('movie.info', merge = True, identifier = identifier)
|
||||
del info['in_wanted'], info['in_library'] # Don't need those here
|
||||
if not info or len(info) == 0:
|
||||
log.error('Could not update, no movie info to work with: %s' % identifier)
|
||||
return False
|
||||
|
||||
@@ -51,6 +51,12 @@ class MoviePlugin(Plugin):
|
||||
'movies': array, movies found,
|
||||
}"""}
|
||||
})
|
||||
addApiView('movie.get', self.getView, docs = {
|
||||
'desc': 'Get a movie by id',
|
||||
'params': {
|
||||
'id': {'desc': 'The id of the movie'},
|
||||
}
|
||||
})
|
||||
addApiView('movie.refresh', self.refresh, docs = {
|
||||
'desc': 'Refresh a movie by id',
|
||||
'params': {
|
||||
@@ -88,12 +94,25 @@ class MoviePlugin(Plugin):
|
||||
addEvent('movie.list', self.list)
|
||||
addEvent('movie.restatus', self.restatus)
|
||||
|
||||
def getView(self):
|
||||
|
||||
movie_id = getParam('id')
|
||||
movie = self.get(movie_id) if movie_id else None
|
||||
|
||||
return jsonified({
|
||||
'success': movie is not None,
|
||||
'movie': movie,
|
||||
})
|
||||
|
||||
def get(self, movie_id):
|
||||
|
||||
db = get_session()
|
||||
m = db.query(Movie).filter_by(id = movie_id).first()
|
||||
|
||||
return m.to_dict(self.default_dict)
|
||||
if m:
|
||||
return m.to_dict(self.default_dict)
|
||||
|
||||
return None
|
||||
|
||||
def list(self, status = ['active'], limit_offset = None, starts_with = None, search = None):
|
||||
|
||||
@@ -169,11 +188,9 @@ class MoviePlugin(Plugin):
|
||||
status = [status]
|
||||
|
||||
q = db.query(Movie) \
|
||||
.join(Movie.library, Library.titles) \
|
||||
.join(Movie.library, Library.titles, Movie.status) \
|
||||
.options(joinedload_all('library.titles')) \
|
||||
.filter(LibraryTitle.default == True) \
|
||||
.filter(or_(*[Movie.status.has(identifier = s) for s in status])) \
|
||||
.group_by(Movie.id)
|
||||
.filter(or_(*[Movie.status.has(identifier = s) for s in status]))
|
||||
|
||||
results = q.all()
|
||||
|
||||
@@ -380,13 +397,15 @@ class MoviePlugin(Plugin):
|
||||
total_deleted = 0
|
||||
new_movie_status = None
|
||||
for release in movie.releases:
|
||||
if delete_from == 'wanted' and release.status_id != done_status.get('id'):
|
||||
db.delete(release)
|
||||
total_deleted += 1
|
||||
if delete_from == 'wanted':
|
||||
if release.status_id != done_status.get('id'):
|
||||
db.delete(release)
|
||||
total_deleted += 1
|
||||
new_movie_status = 'done'
|
||||
elif delete_from == 'manage' and release.status_id == done_status.get('id'):
|
||||
db.delete(release)
|
||||
total_deleted += 1
|
||||
elif delete_from == 'manage':
|
||||
if release.status_id == done_status.get('id'):
|
||||
db.delete(release)
|
||||
total_deleted += 1
|
||||
new_movie_status = 'active'
|
||||
db.commit()
|
||||
|
||||
@@ -397,6 +416,8 @@ class MoviePlugin(Plugin):
|
||||
new_status = fireEvent('status.get', new_movie_status, single = True)
|
||||
movie.status_id = new_status.get('id')
|
||||
db.commit()
|
||||
else:
|
||||
fireEvent('movie.restatus', movie.id, single = True)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -288,7 +288,7 @@
|
||||
text-align: left;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.movies .options .table.files .name { width: 605px; }
|
||||
.movies .options .table.files .name { width: 590px; }
|
||||
.movies .options .table .type { width: 130px; }
|
||||
.movies .options .table .is_available { width: 90px; }
|
||||
.movies .options .table .age,
|
||||
|
||||
@@ -31,7 +31,7 @@ class IMDBAPI(MovieProvider):
|
||||
return []
|
||||
|
||||
cache_key = 'imdbapi.cache.%s' % q
|
||||
cached = self.getCache(cache_key, self.urls['search'] % tryUrlencode({'t': name_year.get('name'), 'y': name_year.get('year', '')}), url_timeout = 3)
|
||||
cached = self.getCache(cache_key, self.urls['search'] % tryUrlencode({'t': name_year.get('name'), 'y': name_year.get('year', '')}), timeout = 3)
|
||||
|
||||
if cached:
|
||||
result = self.parseMovie(cached)
|
||||
@@ -49,7 +49,7 @@ class IMDBAPI(MovieProvider):
|
||||
return {}
|
||||
|
||||
cache_key = 'imdbapi.cache.%s' % identifier
|
||||
cached = self.getCache(cache_key, self.urls['info'] % identifier, url_timeout = 3)
|
||||
cached = self.getCache(cache_key, self.urls['info'] % identifier, timeout = 3)
|
||||
|
||||
if cached:
|
||||
result = self.parseMovie(cached)
|
||||
|
||||
@@ -67,7 +67,7 @@ class Mysterbin(NZBProvider):
|
||||
|
||||
description = ''
|
||||
if result.find('a', text = 'View NFO'):
|
||||
description = toUnicode(self.getCache('mysterbin.%s' % myster_id, self.urls['nfo'] % myster_id, timeout = 25920000))
|
||||
description = toUnicode(self.getCache('mysterbin.%s' % myster_id, self.urls['nfo'] % myster_id, cache_timeout = 25920000))
|
||||
|
||||
new = {
|
||||
'id': myster_id,
|
||||
|
||||
@@ -10,7 +10,7 @@ config = [{
|
||||
'tab': 'searcher',
|
||||
'subtab': 'providers',
|
||||
'name': 'newznab',
|
||||
'description': 'Enable multiple NewzNab providers such as <a href="http://nzb.su" target="_blank">NZB.su</a>',
|
||||
'description': 'Enable multiple NewzNab providers such as <a href="http://nzb.su" target="_blank">NZB.su</a> and <a href="http://nzbs.org" target="_blank">nzbs.org</a>',
|
||||
'wizard': True,
|
||||
'options': [
|
||||
{
|
||||
@@ -23,7 +23,7 @@ config = [{
|
||||
},
|
||||
{
|
||||
'name': 'host',
|
||||
'default': 'nzb.su,dognzb.cr,beta.nzbs.org',
|
||||
'default': 'nzb.su,dognzb.cr,nzbs.org',
|
||||
'description': 'The hostname of your newznab provider',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode, tryUrlencode
|
||||
from couchpotato.core.helpers.rss import RSS
|
||||
@@ -57,7 +58,10 @@ class NZBClub(NZBProvider, RSS):
|
||||
size = enclosure['length']
|
||||
date = self.getTextElement(nzb, "pubDate")
|
||||
|
||||
description = toUnicode(self.getCache('nzbclub.%s' % nzbclub_id, self.getTextElement(nzb, "link"), timeout = 25920000))
|
||||
full_description = self.getCache('nzbclub.%s' % nzbclub_id, self.getTextElement(nzb, "link"), cache_timeout = 25920000)
|
||||
html = BeautifulSoup(full_description)
|
||||
nfo_pre = html.find('pre', attrs = {'class':'nfo'})
|
||||
description = toUnicode(nfo_pre.text) if nfo_pre else ''
|
||||
|
||||
new = {
|
||||
'id': nzbclub_id,
|
||||
@@ -73,7 +77,7 @@ class NZBClub(NZBProvider, RSS):
|
||||
}
|
||||
new['score'] = fireEvent('score.calculate', new, movie, single = True)
|
||||
|
||||
if 'ARCHIVE inside ARCHIVE' in description:
|
||||
if 'ARCHIVE inside ARCHIVE' in full_description:
|
||||
log.info('Wrong: Seems to be passworded files: %s' % new['name'])
|
||||
continue
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode, tryUrlencode
|
||||
from couchpotato.core.helpers.rss import RSS
|
||||
@@ -62,9 +63,11 @@ class NzbIndex(NZBProvider, RSS):
|
||||
|
||||
try:
|
||||
description = self.getTextElement(nzb, "description")
|
||||
if ' nfo' in description.lower():
|
||||
nfo_url = re.search('href="(?P<nfo>.+)"', description).group('nfo')
|
||||
description = toUnicode(self.getCache('nzbindex.%s' % nzbindex_id, nfo_url, timeout = 25920000))
|
||||
if '/nfo/' in description.lower():
|
||||
nfo_url = re.search('href=\"(?P<nfo>.+)\" ', description).group('nfo')
|
||||
full_description = self.getCache('nzbindex.%s' % nzbindex_id, url = nfo_url, cache_timeout = 25920000)
|
||||
html = BeautifulSoup(full_description)
|
||||
description = toUnicode(html.find('pre', attrs = {'id':'nfo0'}).text)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class NZBMatrix(NZBProvider, RSS):
|
||||
cache_key = 'nzbmatrix.%s.%s' % (movie['library'].get('identifier'), cat_ids)
|
||||
single_cat = True
|
||||
|
||||
data = self.getCache(cache_key, url)
|
||||
data = self.getCache(cache_key, url, cache_timeout = 1800, headers = {'User-Agent': 'CouchPotato'})
|
||||
if data:
|
||||
try:
|
||||
try:
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
from .main import Nzbs
|
||||
|
||||
def start():
|
||||
return Nzbs()
|
||||
|
||||
config = [{
|
||||
'name': 'nzbs',
|
||||
'groups': [
|
||||
{
|
||||
'tab': 'searcher',
|
||||
'subtab': 'providers',
|
||||
'name': 'nzbs',
|
||||
'description': 'Id and Key can be found <a href="http://nzbs.org/index.php?action=rss" target="_blank">on your nzbs.org RSS page</a>.',
|
||||
'wizard': True,
|
||||
'options': [
|
||||
{
|
||||
'name': 'enabled',
|
||||
'type': 'enabler',
|
||||
},
|
||||
{
|
||||
'name': 'id',
|
||||
'label': 'Id',
|
||||
'description': 'The number after "&i="',
|
||||
},
|
||||
{
|
||||
'name': 'api_key',
|
||||
'label': 'Api Key',
|
||||
'description': 'The string after "&h="'
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}]
|
||||
@@ -1,96 +0,0 @@
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.helpers.encoding import simplifyString, tryUrlencode
|
||||
from couchpotato.core.helpers.rss import RSS
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.nzb.base import NZBProvider
|
||||
from dateutil.parser import parse
|
||||
import time
|
||||
import xml.etree.ElementTree as XMLTree
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class Nzbs(NZBProvider, RSS):
|
||||
|
||||
urls = {
|
||||
'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 = [
|
||||
([4], ['720p', '1080p']),
|
||||
([2], ['cam', 'ts', 'dvdrip', 'tc', 'brrip', 'r5', 'scr']),
|
||||
([9], ['dvdr']),
|
||||
]
|
||||
cat_backup_id = 't2'
|
||||
|
||||
http_time_between_calls = 3 # Seconds
|
||||
|
||||
def search(self, movie, quality):
|
||||
|
||||
results = []
|
||||
if self.isDisabled() or not self.isAvailable(self.urls['api'] + '?test' + self.getApiExt()):
|
||||
return results
|
||||
|
||||
cat_id = self.getCatId(quality.get('identifier'))
|
||||
arguments = tryUrlencode({
|
||||
'action':'search',
|
||||
'q': simplifyString(movie['library']['titles'][0]['title']),
|
||||
'catid': cat_id[0],
|
||||
'i': self.conf('id'),
|
||||
'h': self.conf('api_key'),
|
||||
})
|
||||
url = "%s?%s" % (self.urls['api'], arguments)
|
||||
|
||||
cache_key = 'nzbs.%s.%s' % (movie['library'].get('identifier'), str(cat_id))
|
||||
|
||||
data = self.getCache(cache_key, url)
|
||||
if data:
|
||||
try:
|
||||
try:
|
||||
data = XMLTree.fromstring(data)
|
||||
nzbs = self.getElements(data, 'channel/item')
|
||||
except Exception, e:
|
||||
log.debug('%s, %s' % (self.getName(), e))
|
||||
return results
|
||||
|
||||
for nzb in nzbs:
|
||||
|
||||
id = int(self.getTextElement(nzb, "link").partition('nzbid=')[2])
|
||||
new = {
|
||||
'id': id,
|
||||
'type': 'nzb',
|
||||
'provider': self.getName(),
|
||||
'name': self.getTextElement(nzb, "title"),
|
||||
'age': self.calculateAge(int(time.mktime(parse(self.getTextElement(nzb, "pubDate")).timetuple()))),
|
||||
'size': self.parseSize(self.getTextElement(nzb, "description").split('</a><br />')[1].split('">')[1]),
|
||||
'url': self.urls['download'] % (id, self.getApiExt()),
|
||||
'download': self.download,
|
||||
'detail_url': self.urls['detail'] % id,
|
||||
'description': self.getTextElement(nzb, "description"),
|
||||
'check_nzb': True,
|
||||
}
|
||||
new['score'] = fireEvent('score.calculate', new, movie, single = True)
|
||||
|
||||
is_correct_movie = fireEvent('searcher.correct_movie',
|
||||
nzb = new, movie = movie, quality = quality,
|
||||
imdb_results = False, single_category = False, single = True)
|
||||
|
||||
if is_correct_movie:
|
||||
results.append(new)
|
||||
self.found(new)
|
||||
|
||||
return results
|
||||
except SyntaxError:
|
||||
log.error('Failed to parse XML response from NZBMatrix.com')
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def isEnabled(self):
|
||||
return NZBProvider.isEnabled(self) and self.conf('enabled') and self.conf('id') and self.conf('api_key')
|
||||
|
||||
def getApiExt(self):
|
||||
return '&i=%s&h=%s' % (self.conf('id'), self.conf('api_key'))
|
||||
@@ -243,7 +243,7 @@ window.addEvent('domready', function(){
|
||||
create: function(){
|
||||
var self = this;
|
||||
|
||||
self.el = new Element('a.files', {
|
||||
self.el = new Element('a.directory', {
|
||||
'title': 'Available files',
|
||||
'events': {
|
||||
'click': self.showFiles.bind(self)
|
||||
|
||||
Reference in New Issue
Block a user