Merge branch 'refs/heads/develop'
This commit is contained in:
@@ -15,6 +15,7 @@ from sqlalchemy.orm import scoped_session
|
||||
from sqlalchemy.orm.session import sessionmaker
|
||||
from werkzeug.utils import redirect
|
||||
import os
|
||||
import time
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
@@ -73,5 +74,10 @@ def getApiKey():
|
||||
def page_not_found(error):
|
||||
index_url = url_for('web.index')
|
||||
url = getattr(request, 'path')[len(index_url):]
|
||||
return redirect(index_url + '#' + url)
|
||||
|
||||
if url[:3] != 'api':
|
||||
return redirect(index_url + '#' + url)
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
return 'Wrong API key used', 404
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from flask.blueprints import Blueprint
|
||||
from flask.helpers import url_for
|
||||
from flask.templating import render_template
|
||||
from werkzeug.utils import redirect
|
||||
|
||||
api = Blueprint('api', __name__)
|
||||
|
||||
@@ -165,7 +165,7 @@ class Core(Plugin):
|
||||
return '%s:%d%s' % (cleanHost(host).rstrip('/'), int(port), '/' + Env.setting('url_base').lstrip('/') if Env.setting('url_base') else '')
|
||||
|
||||
def createApiUrl(self):
|
||||
return '%s/%s' % (self.createBaseUrl(), Env.setting('api_key'))
|
||||
return '%s/api/%s' % (self.createBaseUrl(), Env.setting('api_key'))
|
||||
|
||||
def version(self):
|
||||
ver = fireEvent('updater.info', single = True)
|
||||
|
||||
@@ -59,7 +59,7 @@ def getParam(attr, default = None):
|
||||
try:
|
||||
return toUnicode(unquote_plus(getattr(flask.request, 'args').get(attr, default))).encode('utf-8')
|
||||
except:
|
||||
return None
|
||||
return default
|
||||
|
||||
def padded_jsonify(callback, *args, **kwargs):
|
||||
content = str(callback) + '(' + json.dumps(dict(*args, **kwargs)) + ')'
|
||||
|
||||
@@ -58,6 +58,7 @@ class Loader(object):
|
||||
pass
|
||||
# todo:: this needs to be more descriptive.
|
||||
log.error('Import error, remove the empty folder: %s' % plugin.get('module'))
|
||||
log.debug('Can\'t import %s: %s' % (module_name, traceback.format_exc()))
|
||||
except:
|
||||
log.error('Can\'t import %s: %s' % (module_name, traceback.format_exc()))
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class CoreNotifier(Notification):
|
||||
addApiView('notification.markread', self.markAsRead, docs = {
|
||||
'desc': 'Mark notifications as read',
|
||||
'params': {
|
||||
'id': {'desc': 'Notification id you want to mark as read.', 'type': 'int (comma separated)'},
|
||||
'ids': {'desc': 'Notification id you want to mark as read.', 'type': 'int (comma separated)'},
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -22,6 +22,21 @@ config = [{
|
||||
'advanced': True,
|
||||
'description': 'Also send message when movie is snatched.',
|
||||
},
|
||||
{
|
||||
'name': 'hostname',
|
||||
'description': 'Notify growl over network. Needs restart.',
|
||||
'advanced': True,
|
||||
},
|
||||
{
|
||||
'name': 'port',
|
||||
'type': 'int',
|
||||
'advanced': True,
|
||||
},
|
||||
{
|
||||
'name': 'password',
|
||||
'type': 'password',
|
||||
'advanced': True,
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
|
||||
@@ -23,11 +23,19 @@ class Growl(Notification):
|
||||
def register(self):
|
||||
if self.registered: return
|
||||
try:
|
||||
|
||||
hostname = self.conf('hostname')
|
||||
password = self.conf('password')
|
||||
port = self.conf('port')
|
||||
|
||||
self.growl = notifier.GrowlNotifier(
|
||||
applicationName = 'CouchPotato',
|
||||
notifications = ["Updates"],
|
||||
defaultNotifications = ["Updates"],
|
||||
applicationIcon = '%s/static/images/couch.png' % fireEvent('app.api_url', single = True),
|
||||
hostname = hostname if hostname else 'localhost',
|
||||
password = password if password else None,
|
||||
port = port if port else 23053
|
||||
)
|
||||
self.growl.register()
|
||||
self.registered = True
|
||||
|
||||
@@ -55,7 +55,7 @@ class Plugin(object):
|
||||
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', self.__class__.__name__)
|
||||
class_name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
|
||||
|
||||
path = '%s/static/%s/' % (Env.setting('api_key'), class_name)
|
||||
path = 'api/%s/static/%s/' % (Env.setting('api_key'), class_name)
|
||||
addView(path + '<path:filename>', self.showStatic, static = True)
|
||||
|
||||
if add_to_head:
|
||||
|
||||
@@ -21,13 +21,24 @@ class Logging(Plugin):
|
||||
'success': True,
|
||||
'log': string, //Log file
|
||||
'total': int, //Total log files available
|
||||
}"""}
|
||||
})
|
||||
addApiView('logging.partial', self.partial, docs = {
|
||||
'desc': 'Get a partial log',
|
||||
'params': {
|
||||
'type': {'desc': 'Type of log', 'type': 'string: all(default), error, info, debug'},
|
||||
'lines': {'desc': 'Number of lines. Last to first. Default 30'},
|
||||
},
|
||||
'return': {'type': 'object', 'example': """{
|
||||
'success': True,
|
||||
'log': string, //Log file
|
||||
}"""}
|
||||
})
|
||||
addApiView('logging.clear', self.clear, docs = {
|
||||
'desc': 'Remove all the log files'
|
||||
})
|
||||
addApiView('logging.log', self.log, docs = {
|
||||
'desc': 'Get the full log file by number',
|
||||
'desc': 'Log errors',
|
||||
'params': {
|
||||
'type': {'desc': 'Type of logging, default "error"'},
|
||||
'**kwargs': {'type':'object', 'desc': 'All other params will be printed in the log string.'},
|
||||
@@ -64,6 +75,46 @@ class Logging(Plugin):
|
||||
'total': total,
|
||||
})
|
||||
|
||||
def partial(self):
|
||||
|
||||
log_type = getParam('type', 'all')
|
||||
total_lines = getParam('lines', 30)
|
||||
|
||||
log_lines = []
|
||||
|
||||
for x in range(0, 50):
|
||||
|
||||
path = '%s%s' % (Env.get('log_path'), '.%s' % x if x > 0 else '')
|
||||
|
||||
# Check see if the log exists
|
||||
if not os.path.isfile(path):
|
||||
break
|
||||
|
||||
reversed_lines = []
|
||||
f = open(path, 'r')
|
||||
reversed_lines = f.read().split('[0m\n')
|
||||
reversed_lines.reverse()
|
||||
|
||||
brk = False
|
||||
for line in reversed_lines:
|
||||
|
||||
#print '%s ' % log_type in line.lower()
|
||||
if log_type == 'all' or '%s ' % log_type.upper() in line:
|
||||
log_lines.append(line)
|
||||
|
||||
if len(log_lines) >= total_lines:
|
||||
brk = True
|
||||
break
|
||||
|
||||
if brk:
|
||||
break
|
||||
|
||||
log_lines.reverse()
|
||||
return jsonified({
|
||||
'success': True,
|
||||
'log': '[0m\n'.join(log_lines),
|
||||
})
|
||||
|
||||
def clear(self):
|
||||
|
||||
for x in range(0, 50):
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent, fireEventAsync, addEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode, tryUrlencode, \
|
||||
simplifyString
|
||||
from couchpotato.core.helpers.encoding import toUnicode, simplifyString
|
||||
from couchpotato.core.helpers.request import getParams, jsonified, getParam
|
||||
from couchpotato.core.helpers.variable import getImdb
|
||||
from couchpotato.core.logger import CPLog
|
||||
@@ -79,6 +78,7 @@ class MoviePlugin(Plugin):
|
||||
'desc': 'Delete a movie from the wanted list',
|
||||
'params': {
|
||||
'id': {'desc': 'Movie ID(s) you want to delete.', 'type': 'int (comma separated)'},
|
||||
'delete_from': {'desc': 'Delete movie from this page', 'type': 'string: all (default), wanted, manage'},
|
||||
}
|
||||
})
|
||||
|
||||
@@ -358,20 +358,45 @@ class MoviePlugin(Plugin):
|
||||
|
||||
ids = [x.strip() for x in params.get('id').split(',')]
|
||||
for movie_id in ids:
|
||||
self.delete(movie_id)
|
||||
self.delete(movie_id, delete_from = params.get('delete_from', 'all'))
|
||||
|
||||
return jsonified({
|
||||
'success': True,
|
||||
})
|
||||
|
||||
def delete(self, movie_id):
|
||||
def delete(self, movie_id, delete_from = None):
|
||||
|
||||
db = get_session()
|
||||
|
||||
movie = db.query(Movie).filter_by(id = movie_id).first()
|
||||
if movie:
|
||||
db.delete(movie)
|
||||
db.commit()
|
||||
if delete_from == 'all':
|
||||
db.delete(movie)
|
||||
db.commit()
|
||||
else:
|
||||
done_status = fireEvent('status.get', 'done', single = True)
|
||||
|
||||
total_releases = len(movie.releases)
|
||||
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
|
||||
new_movie_status = 'done'
|
||||
elif delete_from == 'manage' and release.status_id == done_status.get('id'):
|
||||
db.delete(release)
|
||||
total_deleted += 1
|
||||
new_movie_status = 'active'
|
||||
db.commit()
|
||||
|
||||
if total_releases == total_deleted:
|
||||
db.delete(movie)
|
||||
db.commit()
|
||||
elif new_movie_status:
|
||||
new_status = fireEvent('status.get', new_movie_status, single = True)
|
||||
movie.status_id = new_status.get('id')
|
||||
db.commit()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -253,7 +253,8 @@ var MovieList = new Class({
|
||||
(e).preventDefault();
|
||||
Api.request('movie.delete', {
|
||||
'data': {
|
||||
'id': ids.join(',')
|
||||
'id': ids.join(','),
|
||||
'delete_from': self.options.identifier
|
||||
},
|
||||
'onSuccess': function(){
|
||||
qObj.close();
|
||||
@@ -381,6 +382,8 @@ var MovieList = new Class({
|
||||
update: function(){
|
||||
var self = this;
|
||||
|
||||
self.reset();
|
||||
self.movie_list.empty();
|
||||
self.getMovies();
|
||||
},
|
||||
|
||||
|
||||
@@ -4,11 +4,12 @@ var Movie = new Class({
|
||||
|
||||
action: {},
|
||||
|
||||
initialize: function(self, options, data){
|
||||
initialize: function(list, options, data){
|
||||
var self = this;
|
||||
|
||||
self.data = data;
|
||||
self.view = options.view || 'thumbs';
|
||||
self.list = list;
|
||||
|
||||
self.profile = Quality.getProfile(data.profile_id) || {};
|
||||
self.parent(self, options);
|
||||
@@ -35,10 +36,10 @@ var Movie = new Class({
|
||||
}).adopt(
|
||||
self.info_container = new Element('div.info').adopt(
|
||||
self.title = new Element('div.title', {
|
||||
'text': self.getTitle()
|
||||
'text': self.getTitle() || 'n/a'
|
||||
}),
|
||||
self.year = new Element('div.year', {
|
||||
'text': self.data.library.year || 'Unknown'
|
||||
'text': self.data.library.year || 'n/a'
|
||||
}),
|
||||
self.rating = new Element('div.rating.icon', {
|
||||
'text': self.data.library.rating
|
||||
@@ -294,7 +295,7 @@ var ReleaseAction = new Class({
|
||||
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.size', {'text': (self.get(release, 'size'))}),
|
||||
new Element('span.age', {'text': self.get(release, 'age')}),
|
||||
new Element('span.score', {'text': self.get(release, 'score')}),
|
||||
new Element('span.provider', {'text': self.get(release, 'provider')}),
|
||||
@@ -332,7 +333,7 @@ var ReleaseAction = new Class({
|
||||
|
||||
return (release.info.filter(function(info){
|
||||
return type == info.identifier
|
||||
}).pick() || {}).value
|
||||
}).pick() || {}).value || 'n/a'
|
||||
},
|
||||
|
||||
download: function(release){
|
||||
|
||||
@@ -15,6 +15,8 @@ Block.Search = new Class({
|
||||
'keyup': self.keyup.bind(self),
|
||||
'focus': function(){
|
||||
self.el.addClass('focused')
|
||||
if(this.get('value'))
|
||||
self.hideResults(false)
|
||||
},
|
||||
'blur': function(){
|
||||
self.el.removeClass('focused')
|
||||
@@ -55,6 +57,7 @@ Block.Search = new Class({
|
||||
var self = this;
|
||||
(e).preventDefault();
|
||||
|
||||
self.last_q = '';
|
||||
self.input.set('value', '');
|
||||
self.input.focus()
|
||||
|
||||
@@ -319,6 +322,13 @@ Block.Search.Item = new Class({
|
||||
var self = this;
|
||||
|
||||
if(!self.options.hasClass('set')){
|
||||
|
||||
if(self.info.in_library){
|
||||
var in_library = [];
|
||||
self.info.in_library.releases.each(function(release){
|
||||
in_library.include(release.quality.label)
|
||||
});
|
||||
}
|
||||
|
||||
self.options.adopt(
|
||||
new Element('div').adopt(
|
||||
@@ -326,9 +336,9 @@ Block.Search.Item = new Class({
|
||||
'src': self.info.images.poster[0]
|
||||
}) : null,
|
||||
self.info.in_wanted ? new Element('span.in_wanted', {
|
||||
'text': 'Already in wanted list: ' + self.info.in_wanted.label
|
||||
}) : (self.info.in_library ? new Element('span.in_library', {
|
||||
'text': 'Already in library: ' + self.info.in_library.label
|
||||
'text': 'Already in wanted list: ' + self.info.in_wanted.profile.label
|
||||
}) : (in_library ? new Element('span.in_library', {
|
||||
'text': 'Already in library: ' + in_library.join(', ')
|
||||
}) : null),
|
||||
self.title_select = new Element('select', {
|
||||
'name': 'title'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode, toSafeString
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.helpers.request import jsonified, getParams
|
||||
from couchpotato.core.helpers.variable import mergeDicts, md5, getExt
|
||||
from couchpotato.core.logger import CPLog
|
||||
@@ -184,16 +184,17 @@ class QualityPlugin(Plugin):
|
||||
|
||||
# Check on unreliable stuff
|
||||
if loose:
|
||||
# Check extension + filesize
|
||||
if list(set(quality.get('ext', [])) & set(words)) and size >= quality['size_min'] and size <= quality['size_max']:
|
||||
log.debug('Found %s via ext %s in %s' % (quality['identifier'], quality.get('ext'), words))
|
||||
return self.setCache(hash, quality)
|
||||
|
||||
# Last check on resolution only
|
||||
if quality.get('width', 480) == extra.get('resolution_width', 0):
|
||||
log.debug('Found %s via resolution_width: %s == %s' % (quality['identifier'], quality.get('width', 480), extra.get('resolution_width', 0)))
|
||||
return self.setCache(hash, quality)
|
||||
|
||||
# Check extension + filesize
|
||||
if list(set(quality.get('ext', [])) & set(words)) and size >= quality['size_min'] and size <= quality['size_max']:
|
||||
log.debug('Found %s via ext and filesize %s in %s' % (quality['identifier'], quality.get('ext'), words))
|
||||
return self.setCache(hash, quality)
|
||||
|
||||
|
||||
# Try again with loose testing
|
||||
if not loose:
|
||||
@@ -202,4 +203,4 @@ class QualityPlugin(Plugin):
|
||||
return self.setCache(hash, quality)
|
||||
|
||||
log.debug('Could not identify quality for: %s' % files)
|
||||
return self.setCache(hash, self.single('dvdrip'))
|
||||
return None
|
||||
|
||||
@@ -180,15 +180,18 @@ class Scanner(Plugin):
|
||||
|
||||
# Normal identifier
|
||||
identifier = self.createStringIdentifier(file_path, folder, exclude_filename = is_dvd_file)
|
||||
identifiers = [identifier]
|
||||
|
||||
# Identifier with quality
|
||||
quality = fireEvent('quality.guess', [file_path], single = True) if not is_dvd_file else {'identifier':'dvdr'}
|
||||
identifier_with_quality = '%s %s' % (identifier, quality.get('identifier', ''))
|
||||
if quality:
|
||||
identifier_with_quality = '%s %s' % (identifier, quality.get('identifier', ''))
|
||||
identifiers = [identifier_with_quality, identifier]
|
||||
|
||||
if not movie_files.get(identifier):
|
||||
movie_files[identifier] = {
|
||||
'unsorted_files': [],
|
||||
'identifiers': [identifier_with_quality, identifier],
|
||||
'identifiers': identifiers,
|
||||
'is_dvd': is_dvd_file,
|
||||
}
|
||||
|
||||
@@ -495,7 +498,7 @@ class Scanner(Plugin):
|
||||
'identifier': imdb_id
|
||||
}, update_after = False, single = True)
|
||||
|
||||
log.error('No imdb_id found for %s.' % group['identifiers'])
|
||||
log.error('No imdb_id found for %s. Add a NFO file with IMDB id or add the year to the filename.' % group['identifiers'])
|
||||
return {}
|
||||
|
||||
def getCPImdb(self, string):
|
||||
|
||||
@@ -65,6 +65,8 @@ class Searcher(Plugin):
|
||||
|
||||
def single(self, movie):
|
||||
|
||||
db = get_session()
|
||||
|
||||
pre_releases = fireEvent('quality.pre_releases', single = True)
|
||||
release_dates = fireEvent('library.update_release_date', identifier = movie['library']['identifier'], merge = True)
|
||||
available_status = fireEvent('status.get', 'available', single = True)
|
||||
@@ -94,7 +96,6 @@ class Searcher(Plugin):
|
||||
|
||||
# Add them to this movie releases list
|
||||
for nzb in sorted_results:
|
||||
db = get_session()
|
||||
|
||||
rls = db.query(Release).filter_by(identifier = md5(nzb['url'])).first()
|
||||
if not rls:
|
||||
@@ -106,20 +107,23 @@ class Searcher(Plugin):
|
||||
)
|
||||
db.add(rls)
|
||||
db.commit()
|
||||
else:
|
||||
[db.delete(info) for info in rls.info]
|
||||
db.commit()
|
||||
|
||||
for info in nzb:
|
||||
try:
|
||||
if not isinstance(nzb[info], (str, unicode, int, long)):
|
||||
continue
|
||||
for info in nzb:
|
||||
try:
|
||||
if not isinstance(nzb[info], (str, unicode, int, long)):
|
||||
continue
|
||||
|
||||
rls_info = ReleaseInfo(
|
||||
identifier = info,
|
||||
value = toUnicode(nzb[info])
|
||||
)
|
||||
rls.info.append(rls_info)
|
||||
db.commit()
|
||||
except InterfaceError:
|
||||
log.debug('Couldn\'t add %s to ReleaseInfo: %s' % (info, traceback.format_exc()))
|
||||
rls_info = ReleaseInfo(
|
||||
identifier = info,
|
||||
value = toUnicode(nzb[info])
|
||||
)
|
||||
rls.info.append(rls_info)
|
||||
db.commit()
|
||||
except InterfaceError:
|
||||
log.debug('Couldn\'t add %s to ReleaseInfo: %s' % (info, traceback.format_exc()))
|
||||
|
||||
|
||||
for nzb in sorted_results:
|
||||
@@ -137,6 +141,7 @@ class Searcher(Plugin):
|
||||
if self.shuttingDown():
|
||||
break
|
||||
|
||||
db.remove()
|
||||
return False
|
||||
|
||||
def download(self, data, movie, manual = False):
|
||||
@@ -356,15 +361,15 @@ class Searcher(Plugin):
|
||||
else:
|
||||
if wanted_quality in pre_releases:
|
||||
# Prerelease 1 week before theaters
|
||||
if dates.get('theater') >= now - 604800 and wanted_quality in pre_releases:
|
||||
if dates.get('theater') - 604800 < now:
|
||||
return True
|
||||
else:
|
||||
# 6 weeks after theater release
|
||||
if dates.get('theater') < now - 3628800:
|
||||
if dates.get('theater') + 3628800 < now:
|
||||
return True
|
||||
|
||||
# 6 weeks before dvd release
|
||||
if dates.get('dvd') > now - 3628800:
|
||||
if dates.get('dvd') - 3628800 < now:
|
||||
return True
|
||||
|
||||
# Dvd should be released
|
||||
|
||||
@@ -28,14 +28,16 @@ class MetaDataBase(Plugin):
|
||||
except:
|
||||
log.error('Failed to update movie, before creating metadata: %s' % traceback.format_exc())
|
||||
|
||||
root = self.getRootName(release)
|
||||
root_name = self.getRootName(release)
|
||||
meta_name = os.path.basename(root_name)
|
||||
root = os.path.dirname(root_name)
|
||||
|
||||
movie_info = release['library'].get('info')
|
||||
|
||||
for file_type in ['nfo', 'thumbnail', 'fanart']:
|
||||
try:
|
||||
# Get file path
|
||||
name = getattr(self, 'get' + file_type.capitalize() + 'Name')(root)
|
||||
name = getattr(self, 'get' + file_type.capitalize() + 'Name')(meta_name, root)
|
||||
|
||||
if name and self.conf('meta_' + file_type):
|
||||
|
||||
@@ -54,13 +56,13 @@ class MetaDataBase(Plugin):
|
||||
def getRootName(self, data):
|
||||
return
|
||||
|
||||
def getFanartName(self, root):
|
||||
def getFanartName(self, name, root):
|
||||
return
|
||||
|
||||
def getThumbnailName(self, root):
|
||||
def getThumbnailName(self, name, root):
|
||||
return
|
||||
|
||||
def getNfoName(self, root):
|
||||
def getNfoName(self, name, root):
|
||||
return
|
||||
|
||||
def getNfo(self, movie_info = {}, data = {}):
|
||||
|
||||
@@ -14,14 +14,17 @@ class XBMC(MetaDataBase):
|
||||
def getRootName(self, data = {}):
|
||||
return os.path.join(data['destination_dir'], data['filename'])
|
||||
|
||||
def getFanartName(self, root):
|
||||
return self.conf('meta_fanart_name') % root
|
||||
def getFanartName(self, name, root):
|
||||
return self.createMetaName(self.conf('meta_fanart_name'), name, root)
|
||||
|
||||
def getThumbnailName(self, root):
|
||||
return self.conf('meta_thumbnail_name') % root
|
||||
def getThumbnailName(self, name, root):
|
||||
return self.createMetaName(self.conf('meta_thumbnail_name'), name, root)
|
||||
|
||||
def getNfoName(self, root):
|
||||
return self.conf('meta_nfo_name') % root
|
||||
def getNfoName(self, name, root):
|
||||
return self.createMetaName(self.conf('meta_nfo_name'), name, root)
|
||||
|
||||
def createMetaName(self, basename, name, root):
|
||||
return os.path.join(root, basename.replace('%s', name))
|
||||
|
||||
def getNfo(self, movie_info = {}, data = {}):
|
||||
nfoxml = Element('movie')
|
||||
|
||||
@@ -55,11 +55,11 @@ class MovieResultModifier(Plugin):
|
||||
|
||||
for movie in l.movies:
|
||||
if movie.status_id == active_status['id']:
|
||||
temp['in_wanted'] = movie.profile.to_dict()
|
||||
temp['in_wanted'] = fireEvent('movie.get', movie.id, single = True)
|
||||
|
||||
for release in movie.releases:
|
||||
if release.status_id == done_status['id']:
|
||||
temp['in_library'] = release.quality.to_dict()
|
||||
temp['in_library'] = fireEvent('movie.get', movie.id, single = True)
|
||||
except:
|
||||
log.error('Tried getting more info on searched movies: %s' % traceback.format_exc())
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.providers.base import YarrProvider
|
||||
import time
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ class Newznab(NZBProvider, RSS):
|
||||
'size': int(size) / 1024 / 1024,
|
||||
'url': (self.getUrl(host['host'], self.urls['download']) % id) + self.getApiExt(host),
|
||||
'download': self.download,
|
||||
'detail_url': (self.getUrl(host['host'], self.urls['detail']) % id) + self.getApiExt(host),
|
||||
'detail_url': '%sdetails/%s' % (cleanHost(host['host']), id),
|
||||
'content': self.getTextElement(nzb, "description"),
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ from elixir.entity import Entity
|
||||
from elixir.fields import Field
|
||||
from elixir.options import options_defaults, using_options
|
||||
from elixir.relationships import ManyToMany, OneToMany, ManyToOne
|
||||
from sqlalchemy.types import Integer, Unicode, UnicodeText, Boolean, Float, \
|
||||
String, TypeDecorator
|
||||
from sqlalchemy.types import Integer, Unicode, UnicodeText, Boolean, String, \
|
||||
TypeDecorator
|
||||
import json
|
||||
import time
|
||||
|
||||
@@ -41,7 +41,7 @@ class Movie(Entity):
|
||||
|
||||
last_edit = Field(Integer, default = lambda: int(time.time()))
|
||||
|
||||
library = ManyToOne('Library')
|
||||
library = ManyToOne('Library', cascade = 'delete, delete-orphan', single_parent = True)
|
||||
status = ManyToOne('Status')
|
||||
profile = ManyToOne('Profile')
|
||||
releases = OneToMany('Release', cascade = 'all, delete-orphan')
|
||||
|
||||
@@ -172,13 +172,13 @@ def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, En
|
||||
|
||||
# Static path
|
||||
app.static_folder = os.path.join(base_path, 'couchpotato', 'static')
|
||||
web.add_url_rule('%s/static/<path:filename>' % api_key,
|
||||
web.add_url_rule('api/%s/static/<path:filename>' % api_key,
|
||||
endpoint = 'static',
|
||||
view_func = app.send_static_file)
|
||||
|
||||
# Register modules
|
||||
app.register_blueprint(web, url_prefix = '%s/' % url_base)
|
||||
app.register_blueprint(api, url_prefix = '%s/%s/' % (url_base, api_key))
|
||||
app.register_blueprint(api, url_prefix = '%s/api/%s/' % (url_base, api_key))
|
||||
|
||||
# Some logging and fire load event
|
||||
try: log.info('Starting server on port %(port)s' % config)
|
||||
|
||||
@@ -117,15 +117,15 @@ var CouchPotato = new Class({
|
||||
openPage: function(url) {
|
||||
var self = this;
|
||||
|
||||
var current_url = url.replace(/^\/+|\/+$/g, '');
|
||||
if(current_url == self.current_url)
|
||||
return;
|
||||
|
||||
self.route.parse();
|
||||
var page_name = self.route.getPage().capitalize();
|
||||
var action = self.route.getAction();
|
||||
var params = self.route.getParams();
|
||||
|
||||
var current_url = self.route.getCurrentUrl();
|
||||
if(current_url == self.current_url)
|
||||
return;
|
||||
|
||||
if(self.current_page)
|
||||
self.current_page.hide()
|
||||
|
||||
@@ -287,8 +287,8 @@ var Route = new Class({
|
||||
var self = this;
|
||||
|
||||
var path = History.getPath().replace(Api.getOption('url'), '/').replace(App.getOption('base_url'), '/')
|
||||
var current = path.replace(/^\/+|\/+$/g, '')
|
||||
var url = current.split('/')
|
||||
self.current = path.replace(/^\/+|\/+$/g, '')
|
||||
var url = self.current.split('/')
|
||||
|
||||
self.page = (url.length > 0) ? url.shift() : self.defaults.page
|
||||
self.action = (url.length > 0) ? url.shift() : self.defaults.action
|
||||
@@ -324,6 +324,10 @@ var Route = new Class({
|
||||
return this.params
|
||||
},
|
||||
|
||||
getCurrentUrl: function(){
|
||||
return this.current
|
||||
},
|
||||
|
||||
get: function(param){
|
||||
return this.params[param]
|
||||
}
|
||||
|
||||
@@ -1114,7 +1114,7 @@ Option.Combined = new Class({
|
||||
self.values[nr][name] = value.trim();
|
||||
});
|
||||
|
||||
self.inputs[name].getParent('.ctrlHolder').hide();
|
||||
self.inputs[name].getParent('.ctrlHolder').setStyle('display', 'none');
|
||||
self.inputs[name].addEvent('change', self.addEmpty.bind(self))
|
||||
|
||||
});
|
||||
|
||||
@@ -206,7 +206,8 @@ window.addEvent('domready', function(){
|
||||
function(){
|
||||
Api.request('movie.delete', {
|
||||
'data': {
|
||||
'id': self.movie.get('id')
|
||||
'id': self.movie.get('id'),
|
||||
'delete_from': self.movie.list.options.identifier
|
||||
},
|
||||
'onComplete': function(){
|
||||
movie.set('tween', {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<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>
|
||||
<pre><a href="{{ url_for('web.index') }}getkey/?p=md5(password)&u=md5(username)">{{ url_for('web.index') }}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>
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
import ntpath
|
||||
import os.path
|
||||
import zipfile
|
||||
|
||||
@@ -46,7 +45,7 @@ def split_path(path):
|
||||
"""
|
||||
result = []
|
||||
while True:
|
||||
head, tail = ntpath.split(path)
|
||||
head, tail = os.path.split(path)
|
||||
|
||||
# on Unix systems, the root folder is '/'
|
||||
if head == '/' and tail == '':
|
||||
|
||||
Reference in New Issue
Block a user