Movie add
JSONP
This commit is contained in:
@@ -12,19 +12,21 @@ from sqlalchemy.orm.session import sessionmaker
|
||||
from werkzeug.utils import redirect
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
log = CPLog(__name__)
|
||||
|
||||
app = Flask(__name__)
|
||||
web = Module(__name__, 'web')
|
||||
|
||||
|
||||
def get_session(engine):
|
||||
def get_session(engine = None):
|
||||
engine = engine if engine else get_engine()
|
||||
return scoped_session(sessionmaker(transactional = True, bind = engine))
|
||||
return scoped_session(sessionmaker(bind = engine))
|
||||
|
||||
def get_engine():
|
||||
return create_engine(Env.get('db_path'), echo = False)
|
||||
|
||||
|
||||
""" Web view """
|
||||
@web.route('/')
|
||||
@requires_auth
|
||||
def index():
|
||||
@@ -33,5 +35,6 @@ def index():
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(error):
|
||||
index_url = url_for('web.index')
|
||||
url = request.path[len(index_url):]
|
||||
url = getattr(request, 'path')[len(index_url):]
|
||||
return redirect(index_url + '#' + url)
|
||||
|
||||
|
||||
+11
-21
@@ -1,30 +1,20 @@
|
||||
from couchpotato.core.settings.model import Resource
|
||||
from couchpotato.core.helpers.request import jsonified
|
||||
from flask import Module
|
||||
from flask.helpers import jsonify
|
||||
|
||||
api = Module(__name__)
|
||||
|
||||
def addApiView(route, func):
|
||||
api.add_url_rule(route + '/', route, func)
|
||||
api.add_url_rule(route + '/', endpoint = route if route else 'index', view_func = func)
|
||||
|
||||
|
||||
@api.route('')
|
||||
""" Api view """
|
||||
def index():
|
||||
return jsonify({'test': 'bla'})
|
||||
from couchpotato import app
|
||||
|
||||
routes = []
|
||||
for route, x in sorted(app.view_functions.iteritems()):
|
||||
if route[0:4] == 'api.':
|
||||
routes += [route[4:]]
|
||||
|
||||
@api.route('movie/')
|
||||
def movie():
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'movies': [
|
||||
{
|
||||
'name': 'Movie 1',
|
||||
'description': 'Description 1',
|
||||
},
|
||||
{
|
||||
'name': 'Movie 2',
|
||||
'description': 'Description 2',
|
||||
}
|
||||
]
|
||||
})
|
||||
return jsonified({'routes': routes})
|
||||
|
||||
addApiView('', index)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from couchpotato.core.logger import CPLog
|
||||
from axl.axel import Event
|
||||
from couchpotato.core.logger import CPLog
|
||||
import traceback
|
||||
|
||||
log = CPLog(__name__)
|
||||
events = {}
|
||||
@@ -21,7 +22,17 @@ def fireEvent(name, *args, **kwargs):
|
||||
try:
|
||||
e = events[name]
|
||||
e.asynchronous = False
|
||||
return e(*args, **kwargs)
|
||||
result = e(*args, **kwargs)
|
||||
|
||||
results = []
|
||||
for r in result:
|
||||
if r[0] == True:
|
||||
results.append(r[1])
|
||||
else:
|
||||
etype, value, tb = r[1]
|
||||
log.debug(''.join(traceback.format_exception(etype, value, tb)))
|
||||
|
||||
return results
|
||||
except Exception, e:
|
||||
log.debug(e)
|
||||
|
||||
@@ -29,7 +40,8 @@ def fireEventAsync(name, *args, **kwargs):
|
||||
try:
|
||||
e = events[name]
|
||||
e.asynchronous = True
|
||||
return e(*args, **kwargs)
|
||||
e(*args, **kwargs)
|
||||
return True
|
||||
except Exception, e:
|
||||
log.debug(e)
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
def latinToAscii(unicrap):
|
||||
xlate = {0xc0:'A', 0xc1:'A', 0xc2:'A', 0xc3:'A', 0xc4:'A', 0xc5:'A',
|
||||
0xc6:'Ae', 0xc7:'C',
|
||||
0xc8:'E', 0xc9:'E', 0xca:'E', 0xcb:'E', 0x86:'e',
|
||||
0xcc:'I', 0xcd:'I', 0xce:'I', 0xcf:'I',
|
||||
0xd0:'Th', 0xd1:'N',
|
||||
0xd2:'O', 0xd3:'O', 0xd4:'O', 0xd5:'O', 0xd6:'O', 0xd8:'O',
|
||||
0xd9:'U', 0xda:'U', 0xdb:'U', 0xdc:'U',
|
||||
0xdd:'Y', 0xde:'th', 0xdf:'ss',
|
||||
0xe0:'a', 0xe1:'a', 0xe2:'a', 0xe3:'a', 0xe4:'a', 0xe5:'a',
|
||||
0xe6:'ae', 0xe7:'c',
|
||||
0xe8:'e', 0xe9:'e', 0xea:'e', 0xeb:'e',
|
||||
0xec:'i', 0xed:'i', 0xee:'i', 0xef:'i',
|
||||
0xf0:'th', 0xf1:'n',
|
||||
0xf2:'o', 0xf3:'o', 0xf4:'o', 0xf5:'o', 0xf6:'o', 0xf8:'o',
|
||||
0xf9:'u', 0xfa:'u', 0xfb:'u', 0xfc:'u',
|
||||
0xfd:'y', 0xfe:'th', 0xff:'y',
|
||||
0xa1:'!', 0xa2:'{cent}', 0xa3:'{pound}', 0xa4:'{currency}',
|
||||
0xa5:'{yen}', 0xa6:'|', 0xa7:'{section}', 0xa8:'{umlaut}',
|
||||
0xa9:'{C}', 0xaa:'{^a}', 0xab:'<<', 0xac:'{not}',
|
||||
0xad:'-', 0xae:'{R}', 0xaf:'_', 0xb0:'{degrees}',
|
||||
0xb1:'{+/-}', 0xb2:'{^2}', 0xb3:'{^3}', 0xb4:"'",
|
||||
0xb5:'{micro}', 0xb6:'{paragraph}', 0xb7:'*', 0xb8:'{cedilla}',
|
||||
0xb9:'{^1}', 0xba:'{^o}', 0xbb:'>>',
|
||||
0xbc:'{1/4}', 0xbd:'{1/2}', 0xbe:'{3/4}', 0xbf:'?',
|
||||
0xd7:'*', 0xf7:'/'
|
||||
}
|
||||
|
||||
r = ''
|
||||
for i in unicrap:
|
||||
if xlate.has_key(ord(i)):
|
||||
r += xlate[ord(i)]
|
||||
elif ord(i) >= 0x80:
|
||||
pass
|
||||
else:
|
||||
r += str(i)
|
||||
return r
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
from couchpotato.core.logger import CPLog
|
||||
from string import ascii_letters, digits
|
||||
import re
|
||||
import unicodedata
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
def toSafeString(original):
|
||||
valid_chars = "-_.() %s%s" % (ascii_letters, digits)
|
||||
cleanedFilename = unicodedata.normalize('NFKD', toUnicode(original)).encode('ASCII', 'ignore')
|
||||
return ''.join(c for c in cleanedFilename if c in valid_chars)
|
||||
|
||||
def simplifyString(original):
|
||||
string = toSafeString(original)
|
||||
split = re.split('\W+', string.lower())
|
||||
return toUnicode(' '.join(split))
|
||||
|
||||
def toUnicode(original, *args):
|
||||
try:
|
||||
if type(original) is unicode:
|
||||
return original
|
||||
else:
|
||||
return unicode(original, *args)
|
||||
except UnicodeDecodeError:
|
||||
ascii_text = str(original).encode('string_escape')
|
||||
return unicode(ascii_text)
|
||||
@@ -0,0 +1,22 @@
|
||||
from flask.globals import current_app
|
||||
from flask.helpers import json, jsonify
|
||||
import flask
|
||||
|
||||
|
||||
def getParams():
|
||||
return getattr(flask.request, 'args')
|
||||
|
||||
def getParam(attr, default = None):
|
||||
return getattr(flask.request, 'args').get(attr, default)
|
||||
|
||||
def padded_jsonify(callback, *args, **kwargs):
|
||||
content = str(callback) + '(' + json.dumps(dict(*args, **kwargs)) + ')'
|
||||
return current_app.response_class(content, mimetype = 'text/javascript')
|
||||
|
||||
def jsonified(*args, **kwargs):
|
||||
from couchpotato.environment import Env
|
||||
callback = getParam('json_callback', None)
|
||||
if callback:
|
||||
return padded_jsonify(callback, *args, **kwargs)
|
||||
else:
|
||||
return jsonify(*args, **kwargs)
|
||||
@@ -0,0 +1,33 @@
|
||||
from couchpotato.core.logger import CPLog
|
||||
import xml.etree.ElementTree as XMLTree
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
class RSS():
|
||||
|
||||
def gettextelements(self, xml, path):
|
||||
''' Find elements and return tree'''
|
||||
|
||||
textelements = []
|
||||
try:
|
||||
elements = xml.findall(path)
|
||||
except:
|
||||
return
|
||||
for element in elements:
|
||||
textelements.append(element.text)
|
||||
return textelements
|
||||
|
||||
def gettextelement(self, xml, path):
|
||||
''' Find element and return text'''
|
||||
|
||||
try:
|
||||
return xml.find(path).text
|
||||
except:
|
||||
return
|
||||
|
||||
def getItems(self, data, path = 'channel/item'):
|
||||
try:
|
||||
return XMLTree.parse(data).findall(path)
|
||||
except Exception, e:
|
||||
log.error('Error parsing RSS. %s' % e)
|
||||
return []
|
||||
@@ -1,6 +1,5 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.environment import Env
|
||||
from flask.helpers import jsonify
|
||||
from couchpotato.core.helpers.request import getParam, jsonified
|
||||
import os
|
||||
import string
|
||||
|
||||
@@ -45,12 +44,12 @@ class FileBrowser():
|
||||
def view(self):
|
||||
|
||||
try:
|
||||
fb = FileBrowser(Env.getParam('path', '/'))
|
||||
fb = FileBrowser(getParam('path', '/'))
|
||||
dirs = fb.getDirectories()
|
||||
except:
|
||||
dirs = []
|
||||
|
||||
return jsonify({
|
||||
return jsonified({
|
||||
'empty': len(dirs) == 0,
|
||||
'dirs': dirs,
|
||||
})
|
||||
|
||||
@@ -1,30 +1,36 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import getEvent, fireEvent
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.helpers.request import getParams, jsonified
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.environment import Env
|
||||
from flask.helpers import jsonify
|
||||
|
||||
class MovieAdd(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
addApiView('movie.add.search', self.search)
|
||||
addApiView('movie.add.select', self.select)
|
||||
|
||||
def search(self):
|
||||
|
||||
a = Env.getParams()
|
||||
a = getParams()
|
||||
|
||||
print fireEvent('provider.movie.search', q = a.get('q'))
|
||||
results = fireEvent('provider.movie.search', q = a.get('q'))
|
||||
|
||||
movies = [
|
||||
{'id': 1, 'name': 'test'}
|
||||
]
|
||||
# Combine movie results
|
||||
movies = []
|
||||
for r in results:
|
||||
movies += r
|
||||
|
||||
return jsonify({
|
||||
return jsonified({
|
||||
'success': True,
|
||||
'empty': len(movies) == 0,
|
||||
'movies': movies,
|
||||
})
|
||||
|
||||
|
||||
def select(self):
|
||||
pass
|
||||
|
||||
a = getParams()
|
||||
|
||||
return jsonified({
|
||||
'success': True,
|
||||
'added': True,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
from couchpotato.core.plugins.movie_list.main import MovieList
|
||||
|
||||
def start():
|
||||
return MovieList()
|
||||
|
||||
config = []
|
||||
@@ -0,0 +1,41 @@
|
||||
from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.helpers.request import getParams, jsonified
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.settings.model import Movie, Release
|
||||
|
||||
class MovieList(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
addApiView('movie.list', self.list)
|
||||
|
||||
def list(self):
|
||||
|
||||
a = getParams()
|
||||
|
||||
results = get_session().query(Movie).filter(
|
||||
Movie.releases.any(
|
||||
Release.status.has(identifier = 'wanted')
|
||||
)
|
||||
).all()
|
||||
|
||||
movies = []
|
||||
for movie in results:
|
||||
temp = {
|
||||
'id': movie.id,
|
||||
'name': movie.id,
|
||||
'releases': [],
|
||||
}
|
||||
for release in movie.releases:
|
||||
temp['releases'].append({
|
||||
'status': release.status.label,
|
||||
'quality': release.quality.label
|
||||
})
|
||||
|
||||
movies.append(temp)
|
||||
|
||||
return jsonified({
|
||||
'success': True,
|
||||
'empty': len(movies) == 0,
|
||||
'movies': movies,
|
||||
})
|
||||
@@ -1,9 +1,4 @@
|
||||
from couchpotato.core.helpers import latinToAscii
|
||||
from couchpotato.core.logger import CPLog
|
||||
from string import ascii_letters, digits
|
||||
import re
|
||||
import unicodedata
|
||||
import xml.etree.ElementTree as XMLTree
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
@@ -15,43 +10,3 @@ class Provider():
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def toSaveString(self, string):
|
||||
string = latinToAscii(string)
|
||||
string = ''.join((c for c in unicodedata.normalize('NFD', unicode(string)) if unicodedata.category(c) != 'Mn'))
|
||||
safe_chars = ascii_letters + digits + '_ -.,\':!?'
|
||||
r = ''.join([char if char in safe_chars else ' ' for char in string])
|
||||
return re.sub('\s+' , ' ', r)
|
||||
|
||||
def toSearchString(self, string):
|
||||
string = latinToAscii(string)
|
||||
string = ''.join((c for c in unicodedata.normalize('NFD', unicode(string)) if unicodedata.category(c) != 'Mn'))
|
||||
safe_chars = ascii_letters + digits + ' \''
|
||||
r = ''.join([char if char in safe_chars else ' ' for char in string])
|
||||
return re.sub('\s+' , ' ', r).replace('\'s', 's').replace('\'', ' ')
|
||||
|
||||
def gettextelements(self, xml, path):
|
||||
''' Find elements and return tree'''
|
||||
|
||||
textelements = []
|
||||
try:
|
||||
elements = xml.findall(path)
|
||||
except:
|
||||
return
|
||||
for element in elements:
|
||||
textelements.append(element.text)
|
||||
return textelements
|
||||
|
||||
def gettextelement(self, xml, path):
|
||||
''' Find element and return text'''
|
||||
|
||||
try:
|
||||
return xml.find(path).text
|
||||
except:
|
||||
return
|
||||
|
||||
def getItems(self, data, path = 'channel/item'):
|
||||
try:
|
||||
return XMLTree.parse(data).findall(path)
|
||||
except Exception, e:
|
||||
log.error('Error parsing RSS. %s' % e)
|
||||
return []
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from __future__ import with_statement
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import simplifyString, toUnicode
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.base import Provider
|
||||
from couchpotato.environment import Env
|
||||
from urllib import quote_plus
|
||||
import urllib2
|
||||
import simplejson as json
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
@@ -21,7 +23,7 @@ class TMDB(Provider):
|
||||
def conf(self, attr):
|
||||
return Env.setting(attr, 'TheMovieDB')
|
||||
|
||||
def search(self, q, limit = 8, alternative = True):
|
||||
def search(self, q, limit = 12, alternative = True):
|
||||
''' Find movie by name '''
|
||||
|
||||
if self.isDisabled():
|
||||
@@ -29,58 +31,61 @@ class TMDB(Provider):
|
||||
|
||||
log.debug('TheMovieDB - Searching for movie: %s' % q)
|
||||
|
||||
url = "%s/%s/%s/xml/%s/%s" % (self.apiUrl, 'Movie.search', 'en', self.conf('api_key'), quote_plus(self.toSearchString(q)))
|
||||
url = "%s/%s/%s/json/%s/%s" % (self.apiUrl, 'Movie.search', 'en', self.conf('api_key'), quote_plus(simplifyString(q)))
|
||||
|
||||
log.info('Searching: %s' % url)
|
||||
|
||||
data = urllib2.urlopen(url)
|
||||
jsn = json.load(data)
|
||||
|
||||
return self.parseXML(data, limit, alternative = alternative)
|
||||
return self.parse(jsn, limit, alternative = alternative)
|
||||
|
||||
def parseXML(self, data, limit, alternative = True):
|
||||
def parse(self, data, limit, alternative = True):
|
||||
if data:
|
||||
log.debug('TheMovieDB - Parsing RSS')
|
||||
try:
|
||||
xml = self.getItems(data, 'movies/movie')
|
||||
|
||||
results = []
|
||||
nr = 0
|
||||
for movie in xml:
|
||||
id = int(self.gettextelement(movie, "id"))
|
||||
for movie in data:
|
||||
|
||||
name = self.gettextelement(movie, "name")
|
||||
imdb = self.gettextelement(movie, "imdb_id")
|
||||
year = str(self.gettextelement(movie, "released"))[:4]
|
||||
year = movie['released'][:4]
|
||||
|
||||
# Poster url
|
||||
poster = ''
|
||||
for p in movie['posters']:
|
||||
p = p['image']
|
||||
if(p['size'] == 'thumb'):
|
||||
poster = p['url']
|
||||
break
|
||||
|
||||
# 1900 is the same as None
|
||||
if year == '1900':
|
||||
year = 'None'
|
||||
year = None
|
||||
|
||||
results.append({
|
||||
'id': id,
|
||||
'name': self.toSaveString(name),
|
||||
'imdb': imdb,
|
||||
'year': year
|
||||
})
|
||||
movie_data = {
|
||||
'id': int(movie['id']),
|
||||
'name': toUnicode(movie['name']),
|
||||
'poster': poster,
|
||||
'imdb': movie['imdb_id'],
|
||||
'year': year,
|
||||
'tagline': 'This is the tagline of the movie',
|
||||
}
|
||||
results.append(movie_data)
|
||||
|
||||
alternativeName = self.gettextelement(movie, "alternative_name")
|
||||
alternativeName = movie['alternative_name']
|
||||
if alternativeName and alternative:
|
||||
if alternativeName.lower() != name.lower() and alternativeName.lower() != 'none' and alternativeName != None:
|
||||
results.append({
|
||||
'id': id,
|
||||
'name': self.toSaveString(alternativeName),
|
||||
'imdb': imdb,
|
||||
'year': year
|
||||
})
|
||||
if alternativeName.lower() != movie['name'].lower() and alternativeName.lower() != 'none' and alternativeName != None:
|
||||
movie_data['name'] = toUnicode(alternativeName)
|
||||
results.append(movie_data)
|
||||
|
||||
nr += 1
|
||||
if nr == limit:
|
||||
break
|
||||
|
||||
#log.info('TheMovieDB - Found: %s' % results)
|
||||
log.info('TheMovieDB - Found: %s' % [result['name'] + u' (' + str(result['year']) + ')' for result in results])
|
||||
return results
|
||||
except SyntaxError:
|
||||
log.error('TheMovieDB - Failed to parse XML response from TheMovieDb')
|
||||
except SyntaxError, e:
|
||||
log.error('TheMovieDB - Failed to parse XML response from TheMovieDb: %s' % e)
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from __future__ import with_statement
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent
|
||||
from flask.helpers import jsonify
|
||||
from couchpotato.core.helpers.request import getParams, jsonified
|
||||
import ConfigParser
|
||||
import os.path
|
||||
import time
|
||||
@@ -107,14 +107,14 @@ class Settings():
|
||||
|
||||
def view(self):
|
||||
|
||||
return jsonify({
|
||||
return jsonified({
|
||||
'options': self.getOptions(),
|
||||
'values': self.getValues()
|
||||
})
|
||||
|
||||
def saveView(self):
|
||||
|
||||
a = Env.getParams()
|
||||
a = getParams()
|
||||
|
||||
section = a.get('section')
|
||||
option = a.get('name')
|
||||
@@ -123,6 +123,6 @@ class Settings():
|
||||
self.set(option, section, value)
|
||||
self.save()
|
||||
|
||||
return jsonify({
|
||||
return jsonified({
|
||||
'success': True,
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ from elixir.entity import Entity
|
||||
from elixir.fields import Field
|
||||
from elixir.options import options_defaults
|
||||
from elixir.relationships import OneToMany, ManyToOne
|
||||
from sqlalchemy.types import Integer, String, Unicode
|
||||
from sqlalchemy.types import Integer, String, Unicode, UnicodeText, Boolean
|
||||
|
||||
options_defaults["shortnames"] = True
|
||||
|
||||
@@ -13,43 +13,111 @@ options_defaults["shortnames"] = True
|
||||
# http://elixir.ematia.de/trac/wiki/Recipes/MultipleDatabasesOneMetadata
|
||||
__session__ = None
|
||||
|
||||
class Resource(Entity):
|
||||
"""Represents a resource of movies.
|
||||
This resources can be online or offline."""
|
||||
name = Field(Unicode(255))
|
||||
path = Field(Unicode(255))
|
||||
|
||||
class Movie(Entity):
|
||||
"""Movie Resource a movie could have multiple releases
|
||||
The files belonging to the movie object are global for the whole movie
|
||||
such as trailers, nfo, thumbnails"""
|
||||
|
||||
mooli_id = Field(Integer)
|
||||
|
||||
profile = ManyToOne('Profile')
|
||||
releases = OneToMany('Release')
|
||||
files = OneToMany('File')
|
||||
|
||||
|
||||
class Release(Entity):
|
||||
"""Logically groups all files that belong to a certain release, such as
|
||||
parts of a movie, subtitles, nfo, trailers etc."""
|
||||
files = OneToMany('File')
|
||||
mooli_id = Field(Integer)
|
||||
resource = ManyToOne('Resource')
|
||||
parts of a movie, subtitles."""
|
||||
|
||||
movie = ManyToOne('Movie')
|
||||
status = ManyToOne('Status')
|
||||
quality = ManyToOne('Quality')
|
||||
files = OneToMany('File')
|
||||
history = OneToMany('History')
|
||||
|
||||
|
||||
class Status(Entity):
|
||||
"""The status of a release, such as Downloaded, Deleted, Wanted etc"""
|
||||
|
||||
identifier = Field(String(20), unique = True)
|
||||
label = Field(String(20))
|
||||
|
||||
releases = OneToMany('Release')
|
||||
|
||||
|
||||
class Quality(Entity):
|
||||
"""Quality name of a release, DVD, 720P, DVD-Rip etc"""
|
||||
|
||||
identifier = Field(String(20), unique = True)
|
||||
label = Field(String(20))
|
||||
|
||||
releases = OneToMany('Release')
|
||||
profile_types = ManyToOne('ProfileType')
|
||||
|
||||
class Profile(Entity):
|
||||
""""""
|
||||
|
||||
identifier = Field(String(20), unique = True)
|
||||
label = Field(Unicode(50))
|
||||
order = Field(Integer)
|
||||
wait_for = Field(Integer)
|
||||
|
||||
movie = OneToMany('Movie')
|
||||
profile_type = OneToMany('ProfileType')
|
||||
|
||||
class ProfileType(Entity):
|
||||
""""""
|
||||
|
||||
order = Field(Integer)
|
||||
mark_completed = Field(Boolean)
|
||||
wait_for = Field(Integer)
|
||||
|
||||
type = OneToMany('Quality')
|
||||
profile = ManyToOne('Profile')
|
||||
|
||||
class File(Entity):
|
||||
"""File that belongs to a release."""
|
||||
history = OneToMany('RenameHistory')
|
||||
|
||||
path = Field(Unicode(255), nullable = False, unique = True)
|
||||
# Subtitles can have multiple parts, too
|
||||
part = Field(Integer)
|
||||
|
||||
history = OneToMany('RenameHistory')
|
||||
movie = ManyToOne('Movie')
|
||||
release = ManyToOne('Release')
|
||||
# Let's remember the size so we know about offline media.
|
||||
size = Field(Integer, nullable = False)
|
||||
type = ManyToOne('FileType')
|
||||
properties = OneToMany('FileProperty')
|
||||
|
||||
|
||||
class FileType(Entity):
|
||||
"""Types could be trailer, subtitle, movie, partial movie etc."""
|
||||
|
||||
identifier = Field(String(20), unique = True)
|
||||
name = Field(Unicode(255), nullable = False)
|
||||
name = Field(Unicode(50), nullable = False)
|
||||
|
||||
files = OneToMany('File')
|
||||
|
||||
|
||||
class FileProperty(Entity):
|
||||
"""Properties that can be bound to a file for off-line usage"""
|
||||
|
||||
identifier = Field(String(20))
|
||||
value = Field(Unicode(255), nullable = False)
|
||||
|
||||
file = ManyToOne('File')
|
||||
|
||||
|
||||
class History(Entity):
|
||||
"""History of actions that are connected to a certain release,
|
||||
such as, renamed to, downloaded, deleted, download subtitles etc"""
|
||||
|
||||
message = Field(UnicodeText())
|
||||
release = ManyToOne('Release')
|
||||
|
||||
class RenameHistory(Entity):
|
||||
"""Remembers from where to where files have been moved."""
|
||||
file = ManyToOne('File')
|
||||
|
||||
old = Field(String(255))
|
||||
new = Field(String(255))
|
||||
|
||||
file = ManyToOne('File')
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from couchpotato.core.loader import Loader
|
||||
from couchpotato.core.settings import Settings
|
||||
import flask
|
||||
|
||||
class Env:
|
||||
|
||||
@@ -42,11 +41,3 @@ class Env:
|
||||
s = Env.get('settings')
|
||||
s.set(section, attr, value)
|
||||
return s
|
||||
|
||||
@staticmethod
|
||||
def getParams():
|
||||
return getattr(flask.request, 'args')
|
||||
|
||||
@staticmethod
|
||||
def getParam(attr, default = None):
|
||||
return getattr(flask.request, 'args').get(attr, default)
|
||||
|
||||
@@ -2,40 +2,131 @@ Block.Search = new Class({
|
||||
|
||||
Extends: BlockBase,
|
||||
|
||||
cache: {},
|
||||
|
||||
create: function(){
|
||||
var self = this;
|
||||
|
||||
self.el = new Element('div.search_form').adopt(
|
||||
self.input = new Element('input', {
|
||||
'events': {
|
||||
'keyup': self.autocomplete.bind(self)
|
||||
'keyup': self.keyup.bind(self)
|
||||
}
|
||||
})
|
||||
}),
|
||||
self.results = new Element('div.results')
|
||||
);
|
||||
|
||||
// Debug
|
||||
self.input.set('value', 'iron man');
|
||||
self.autocomplete(0)
|
||||
},
|
||||
|
||||
autocomplete: function(){
|
||||
|
||||
keyup: function(e){
|
||||
var self = this;
|
||||
|
||||
if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer)
|
||||
self.autocomplete_timer = self.list.delay(300, self)
|
||||
|
||||
if(['up', 'down'].indexOf(e.key) > -1){
|
||||
p('select item')
|
||||
}
|
||||
else if(self.q() != self.last_q) {
|
||||
self.autocomplete()
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
autocomplete: function(delay){
|
||||
var self = this;
|
||||
|
||||
if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer)
|
||||
self.autocomplete_timer = self.list.delay((delay || 300), self)
|
||||
},
|
||||
|
||||
list: function(){
|
||||
var self = this;
|
||||
|
||||
|
||||
if(self.api_request) self.api_request.cancel();
|
||||
self.api_request = self.api().request('movie.add.search', {
|
||||
'data': {
|
||||
'q': self.input.get('value')
|
||||
},
|
||||
'onComplete': self.fill.bind(self)
|
||||
})
|
||||
|
||||
|
||||
var q = self.q();
|
||||
var cache = self.cache[q];
|
||||
|
||||
if(!cache)
|
||||
self.api_request = self.api().request('movie.add.search', {
|
||||
'data': {
|
||||
'q': q
|
||||
},
|
||||
'onComplete': self.fill.bind(self, q)
|
||||
})
|
||||
else
|
||||
self.fill(q, cache)
|
||||
|
||||
self.last_q = q;
|
||||
|
||||
},
|
||||
|
||||
fill: function(){
|
||||
|
||||
|
||||
fill: function(q, json){
|
||||
var self = this;
|
||||
|
||||
self.cache[q] = json
|
||||
|
||||
self.movies = []
|
||||
self.results.empty()
|
||||
|
||||
Object.each(json.movies, function(movie){
|
||||
var m = new Block.Search.Item(movie);
|
||||
$(m).inject(self.results)
|
||||
|
||||
self.movies.include(m)
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
q: function(){
|
||||
return this.input.get('value').trim();
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
Block.Search.Item = new Class({
|
||||
|
||||
initialize: function(info){
|
||||
var self = this;
|
||||
|
||||
self.info = info;
|
||||
|
||||
self.create();
|
||||
},
|
||||
|
||||
create: function(){
|
||||
var self = this;
|
||||
|
||||
self.el = new Element('div.movie').adopt(
|
||||
self.name = new Element('h2', {
|
||||
'text': self.info.name
|
||||
}),
|
||||
self.tagline = new Element('span', {
|
||||
'text': self.info.tagline
|
||||
}),
|
||||
self.year = self.info.year ? new Element('span', {
|
||||
'text': self.info.year
|
||||
}) : null,
|
||||
self.director = self.info.director ? new Element('span', {
|
||||
'text': 'Director:' + self.info.director
|
||||
}) : null,
|
||||
self.starring = self.info.actors ? new Element('span', {
|
||||
'text': 'Starring:'
|
||||
}) : null
|
||||
)
|
||||
|
||||
if(self.info.actors){
|
||||
Object.each(self.info.actors, function(actor){
|
||||
new Element('span', {
|
||||
'text': actor.name
|
||||
}).inject(self.starring)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
toElement: function(){
|
||||
return this.el
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
@@ -100,16 +100,15 @@ var Api = new Class({
|
||||
var self = this
|
||||
|
||||
self.options = options;
|
||||
self.req = new Request.JSON({
|
||||
'method': 'get'
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
request: function(type, options){
|
||||
var self = this;
|
||||
|
||||
return new Request.JSON(Object.merge({
|
||||
var r_type = self.options.is_remote ? 'JSONP' : 'JSON';
|
||||
return new Request[r_type](Object.merge({
|
||||
'callbackKey': 'json_callback',
|
||||
'method': 'get',
|
||||
'url': self.createUrl(type),
|
||||
}, options)).send()
|
||||
|
||||
+349
-4206
File diff suppressed because it is too large
Load Diff
@@ -1,609 +1,70 @@
|
||||
// MooTools: the javascript framework.
|
||||
// Load this file's selection again by visiting: http://mootools.net/more/4da9e3082bf0d4194b76d7e5b3874113
|
||||
// Or build this file again with packager using: packager build More/Element.Forms More/Element.Delegation More/Element.Shortcuts
|
||||
// Load this file's selection again by visiting: http://mootools.net/more/9c0d9e00884b8329757da16ea8f1f4e1
|
||||
// Or build this file again with packager using: packager build More/Element.Forms More/Element.Delegation More/Element.Shortcuts More/Request.JSONP
|
||||
/*
|
||||
---
|
||||
copyrights:
|
||||
- [MooTools](http://mootools.net)
|
||||
|
||||
script: String.Extras.js
|
||||
|
||||
name: String.Extras
|
||||
|
||||
description: Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc).
|
||||
|
||||
license: MIT-style license
|
||||
|
||||
authors:
|
||||
- Aaron Newton
|
||||
- Guillermo Rauch
|
||||
- Christopher Pitt
|
||||
|
||||
requires:
|
||||
- Core/String
|
||||
- Core/Array
|
||||
|
||||
provides: [String.Extras]
|
||||
|
||||
licenses:
|
||||
- [MIT License](http://mootools.net/license.txt)
|
||||
...
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
var special = {
|
||||
'a': /[àáâãäåăą]/g,
|
||||
'A': /[ÀÁÂÃÄÅĂĄ]/g,
|
||||
'c': /[ćčç]/g,
|
||||
'C': /[ĆČÇ]/g,
|
||||
'd': /[ďđ]/g,
|
||||
'D': /[ĎÐ]/g,
|
||||
'e': /[èéêëěę]/g,
|
||||
'E': /[ÈÉÊËĚĘ]/g,
|
||||
'g': /[ğ]/g,
|
||||
'G': /[Ğ]/g,
|
||||
'i': /[ìíîï]/g,
|
||||
'I': /[ÌÍÎÏ]/g,
|
||||
'l': /[ĺľł]/g,
|
||||
'L': /[ĹĽŁ]/g,
|
||||
'n': /[ñňń]/g,
|
||||
'N': /[ÑŇŃ]/g,
|
||||
'o': /[òóôõöøő]/g,
|
||||
'O': /[ÒÓÔÕÖØ]/g,
|
||||
'r': /[řŕ]/g,
|
||||
'R': /[ŘŔ]/g,
|
||||
's': /[ššş]/g,
|
||||
'S': /[ŠŞŚ]/g,
|
||||
't': /[ťţ]/g,
|
||||
'T': /[ŤŢ]/g,
|
||||
'ue': /[ü]/g,
|
||||
'UE': /[Ü]/g,
|
||||
'u': /[ùúûůµ]/g,
|
||||
'U': /[ÙÚÛŮ]/g,
|
||||
'y': /[ÿý]/g,
|
||||
'Y': /[ŸÝ]/g,
|
||||
'z': /[žźż]/g,
|
||||
'Z': /[ŽŹŻ]/g,
|
||||
'th': /[þ]/g,
|
||||
'TH': /[Þ]/g,
|
||||
'dh': /[ð]/g,
|
||||
'DH': /[Ð]/g,
|
||||
'ss': /[ß]/g,
|
||||
'oe': /[œ]/g,
|
||||
'OE': /[Œ]/g,
|
||||
'ae': /[æ]/g,
|
||||
'AE': /[Æ]/g
|
||||
},
|
||||
|
||||
tidy = {
|
||||
' ': /[\xa0\u2002\u2003\u2009]/g,
|
||||
'*': /[\xb7]/g,
|
||||
'\'': /[\u2018\u2019]/g,
|
||||
'"': /[\u201c\u201d]/g,
|
||||
'...': /[\u2026]/g,
|
||||
'-': /[\u2013]/g,
|
||||
// '--': /[\u2014]/g,
|
||||
'»': /[\uFFFD]/g
|
||||
};
|
||||
|
||||
var walk = function(string, replacements){
|
||||
var result = string;
|
||||
for (key in replacements) result = result.replace(replacements[key], key);
|
||||
return result;
|
||||
};
|
||||
|
||||
var getRegexForTag = function(tag, contents){
|
||||
tag = tag || '';
|
||||
var regstr = contents ? "<" + tag + "(?!\\w)[^>]*>([\\s\\S]*?)<\/" + tag + "(?!\\w)>" : "<\/?" + tag + "([^>]+)?>";
|
||||
reg = new RegExp(regstr, "gi");
|
||||
return reg;
|
||||
};
|
||||
|
||||
String.implement({
|
||||
|
||||
standardize: function(){
|
||||
return walk(this, special);
|
||||
},
|
||||
|
||||
repeat: function(times){
|
||||
return new Array(times + 1).join(this);
|
||||
},
|
||||
|
||||
pad: function(length, str, direction){
|
||||
if (this.length >= length) return this;
|
||||
|
||||
var pad = (str == null ? ' ' : '' + str)
|
||||
.repeat(length - this.length)
|
||||
.substr(0, length - this.length);
|
||||
|
||||
if (!direction || direction == 'right') return this + pad;
|
||||
if (direction == 'left') return pad + this;
|
||||
|
||||
return pad.substr(0, (pad.length / 2).floor()) + this + pad.substr(0, (pad.length / 2).ceil());
|
||||
},
|
||||
|
||||
getTags: function(tag, contents){
|
||||
return this.match(getRegexForTag(tag, contents)) || [];
|
||||
},
|
||||
|
||||
stripTags: function(tag, contents){
|
||||
return this.replace(getRegexForTag(tag, contents), '');
|
||||
},
|
||||
|
||||
tidy: function(){
|
||||
return walk(this, tidy);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
|
||||
/*
|
||||
---
|
||||
|
||||
script: More.js
|
||||
|
||||
name: More
|
||||
|
||||
description: MooTools More
|
||||
|
||||
license: MIT-style license
|
||||
|
||||
authors:
|
||||
- Guillermo Rauch
|
||||
- Thomas Aylott
|
||||
- Scott Kyle
|
||||
- Arian Stolwijk
|
||||
- Tim Wienk
|
||||
- Christoph Pojer
|
||||
- Aaron Newton
|
||||
|
||||
requires:
|
||||
- Core/MooTools
|
||||
|
||||
provides: [MooTools.More]
|
||||
|
||||
...
|
||||
*/
|
||||
|
||||
MooTools.More = {
|
||||
'version': '1.3.0.1',
|
||||
'build': '6dce99bed2792dffcbbbb4ddc15a1fb9a41994b5'
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
---
|
||||
|
||||
script: Element.Forms.js
|
||||
|
||||
name: Element.Forms
|
||||
|
||||
description: Extends the Element native object to include methods useful in managing inputs.
|
||||
|
||||
license: MIT-style license
|
||||
|
||||
authors:
|
||||
- Aaron Newton
|
||||
|
||||
requires:
|
||||
- Core/Element
|
||||
- /String.Extras
|
||||
- /MooTools.More
|
||||
|
||||
provides: [Element.Forms]
|
||||
|
||||
...
|
||||
*/
|
||||
|
||||
Element.implement({
|
||||
|
||||
tidy: function(){
|
||||
this.set('value', this.get('value').tidy());
|
||||
},
|
||||
|
||||
getTextInRange: function(start, end){
|
||||
return this.get('value').substring(start, end);
|
||||
},
|
||||
|
||||
getSelectedText: function(){
|
||||
if (this.setSelectionRange) return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd());
|
||||
return document.selection.createRange().text;
|
||||
},
|
||||
|
||||
getSelectedRange: function(){
|
||||
if (this.selectionStart != null){
|
||||
return {
|
||||
start: this.selectionStart,
|
||||
end: this.selectionEnd
|
||||
};
|
||||
}
|
||||
|
||||
var pos = {
|
||||
start: 0,
|
||||
end: 0
|
||||
};
|
||||
var range = this.getDocument().selection.createRange();
|
||||
if (!range || range.parentElement() != this) return pos;
|
||||
var duplicate = range.duplicate();
|
||||
|
||||
if (this.type == 'text'){
|
||||
pos.start = 0 - duplicate.moveStart('character', -100000);
|
||||
pos.end = pos.start + range.text.length;
|
||||
} else {
|
||||
var value = this.get('value');
|
||||
var offset = value.length;
|
||||
duplicate.moveToElementText(this);
|
||||
duplicate.setEndPoint('StartToEnd', range);
|
||||
if (duplicate.text.length) offset -= value.match(/[\n\r]*$/)[0].length;
|
||||
pos.end = offset - duplicate.text.length;
|
||||
duplicate.setEndPoint('StartToStart', range);
|
||||
pos.start = offset - duplicate.text.length;
|
||||
}
|
||||
return pos;
|
||||
},
|
||||
|
||||
getSelectionStart: function(){
|
||||
return this.getSelectedRange().start;
|
||||
},
|
||||
|
||||
getSelectionEnd: function(){
|
||||
return this.getSelectedRange().end;
|
||||
},
|
||||
|
||||
setCaretPosition: function(pos){
|
||||
if (pos == 'end') pos = this.get('value').length;
|
||||
this.selectRange(pos, pos);
|
||||
return this;
|
||||
},
|
||||
|
||||
getCaretPosition: function(){
|
||||
return this.getSelectedRange().start;
|
||||
},
|
||||
|
||||
selectRange: function(start, end){
|
||||
if (this.setSelectionRange){
|
||||
this.focus();
|
||||
this.setSelectionRange(start, end);
|
||||
} else {
|
||||
var value = this.get('value');
|
||||
var diff = value.substr(start, end - start).replace(/\r/g, '').length;
|
||||
start = value.substr(0, start).replace(/\r/g, '').length;
|
||||
var range = this.createTextRange();
|
||||
range.collapse(true);
|
||||
range.moveEnd('character', start + diff);
|
||||
range.moveStart('character', start);
|
||||
range.select();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
insertAtCursor: function(value, select){
|
||||
var pos = this.getSelectedRange();
|
||||
var text = this.get('value');
|
||||
this.set('value', text.substring(0, pos.start) + value + text.substring(pos.end, text.length));
|
||||
if (select !== false) this.selectRange(pos.start, pos.start + value.length);
|
||||
else this.setCaretPosition(pos.start + value.length);
|
||||
return this;
|
||||
},
|
||||
|
||||
insertAroundCursor: function(options, select){
|
||||
options = Object.append({
|
||||
before: '',
|
||||
defaultMiddle: '',
|
||||
after: ''
|
||||
}, options);
|
||||
|
||||
var value = this.getSelectedText() || options.defaultMiddle;
|
||||
var pos = this.getSelectedRange();
|
||||
var text = this.get('value');
|
||||
|
||||
if (pos.start == pos.end){
|
||||
this.set('value', text.substring(0, pos.start) + options.before + value + options.after + text.substring(pos.end, text.length));
|
||||
this.selectRange(pos.start + options.before.length, pos.end + options.before.length + value.length);
|
||||
} else {
|
||||
var current = text.substring(pos.start, pos.end);
|
||||
this.set('value', text.substring(0, pos.start) + options.before + current + options.after + text.substring(pos.end, text.length));
|
||||
var selStart = pos.start + options.before.length;
|
||||
if (select !== false) this.selectRange(selStart, selStart + current.length);
|
||||
else this.setCaretPosition(selStart + text.length);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
---
|
||||
|
||||
name: Events.Pseudos
|
||||
|
||||
description: Adds the functionallity to add pseudo events
|
||||
|
||||
license: MIT-style license
|
||||
|
||||
authors:
|
||||
- Arian Stolwijk
|
||||
|
||||
requires: [Core/Class.Extras, Core/Slick.Parser, More/MooTools.More]
|
||||
|
||||
provides: [Events.Pseudos]
|
||||
|
||||
...
|
||||
*/
|
||||
|
||||
Events.Pseudos = function(pseudos, addEvent, removeEvent){
|
||||
|
||||
var storeKey = 'monitorEvents:';
|
||||
|
||||
var storageOf = function(object){
|
||||
|
||||
return {
|
||||
store: object.store ? function(key, value){
|
||||
object.store(storeKey + key, value);
|
||||
} : function(key, value){
|
||||
(object.$monitorEvents || (object.$monitorEvents = {}))[key] = value;
|
||||
},
|
||||
retrieve: object.retrieve ? function(key, dflt){
|
||||
return object.retrieve(storeKey + key, dflt);
|
||||
} : function(key, dflt){
|
||||
if (!object.$monitorEvents) return dflt;
|
||||
return object.$monitorEvents[key] || dflt;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
var splitType = function(type){
|
||||
if (type.indexOf(':') == -1) return null;
|
||||
|
||||
var parsed = Slick.parse(type).expressions[0][0],
|
||||
parsedPseudos = parsed.pseudos;
|
||||
|
||||
return (pseudos && pseudos[parsedPseudos[0].key]) ? {
|
||||
event: parsed.tag,
|
||||
value: parsedPseudos[0].value,
|
||||
pseudo: parsedPseudos[0].key,
|
||||
original: type
|
||||
} : null;
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
|
||||
addEvent: function(type, fn, internal){
|
||||
var split = splitType(type);
|
||||
if (!split) return addEvent.call(this, type, fn, internal);
|
||||
|
||||
var storage = storageOf(this),
|
||||
events = storage.retrieve(type, []),
|
||||
pseudoArgs = Array.from(pseudos[split.pseudo]),
|
||||
proxy = pseudoArgs[1];
|
||||
|
||||
var self = this;
|
||||
var monitor = function(){
|
||||
pseudoArgs[0].call(self, split, fn, arguments, proxy);
|
||||
};
|
||||
|
||||
events.include({event: fn, monitor: monitor});
|
||||
storage.store(type, events);
|
||||
|
||||
var eventType = split.event;
|
||||
if (proxy && proxy[eventType]) eventType = proxy[eventType].base;
|
||||
|
||||
addEvent.call(this, type, fn, internal);
|
||||
return addEvent.call(this, eventType, monitor, internal);
|
||||
},
|
||||
|
||||
removeEvent: function(type, fn){
|
||||
var split = splitType(type);
|
||||
if (!split) return removeEvent.call(this, type, fn);
|
||||
|
||||
var storage = storageOf(this),
|
||||
events = storage.retrieve(type),
|
||||
pseudoArgs = Array.from(pseudos[split.pseudo]),
|
||||
proxy = pseudoArgs[1];
|
||||
|
||||
if (!events) return this;
|
||||
|
||||
var eventType = split.event;
|
||||
if (proxy && proxy[eventType]) eventType = proxy[eventType].base;
|
||||
|
||||
removeEvent.call(this, type, fn);
|
||||
events.each(function(monitor, i){
|
||||
if (!fn || monitor.event == fn) removeEvent.call(this, eventType, monitor.monitor);
|
||||
delete events[i];
|
||||
}, this);
|
||||
|
||||
storage.store(type, events);
|
||||
return this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
(function(){
|
||||
|
||||
var pseudos = {
|
||||
|
||||
once: function(split, fn, args){
|
||||
fn.apply(this, args);
|
||||
this.removeEvent(split.original, fn);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Events.definePseudo = function(key, fn){
|
||||
pseudos[key] = fn;
|
||||
};
|
||||
|
||||
var proto = Events.prototype;
|
||||
Events.implement(Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent));
|
||||
|
||||
})();
|
||||
|
||||
|
||||
/*
|
||||
---
|
||||
|
||||
name: Element.Event.Pseudos
|
||||
|
||||
description: Adds the functionality to add pseudo events for Elements
|
||||
|
||||
license: MIT-style license
|
||||
|
||||
authors:
|
||||
- Arian Stolwijk
|
||||
|
||||
requires: [Core/Element.Event, Events.Pseudos]
|
||||
|
||||
provides: [Element.Event.Pseudos]
|
||||
|
||||
...
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
var pseudos = {
|
||||
|
||||
once: function(split, fn, args){
|
||||
fn.apply(this, args);
|
||||
this.removeEvent(split.original, fn);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Event.definePseudo = function(key, fn, proxy){
|
||||
pseudos[key] = [fn, proxy];
|
||||
};
|
||||
|
||||
var proto = Element.prototype;
|
||||
[Element, Window, Document].invoke('implement', Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent));
|
||||
|
||||
})();
|
||||
|
||||
|
||||
/*
|
||||
---
|
||||
|
||||
script: Element.Delegation.js
|
||||
|
||||
name: Element.Delegation
|
||||
|
||||
description: Extends the Element native object to include the delegate method for more efficient event management.
|
||||
|
||||
credits:
|
||||
- "Event checking based on the work of Daniel Steigerwald. License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"
|
||||
|
||||
license: MIT-style license
|
||||
|
||||
authors:
|
||||
- Aaron Newton
|
||||
- Daniel Steigerwald
|
||||
|
||||
requires: [/MooTools.More, Element.Event.Pseudos]
|
||||
|
||||
provides: [Element.Delegation]
|
||||
|
||||
...
|
||||
*/
|
||||
|
||||
|
||||
Event.definePseudo('relay', function(split, fn, args, proxy){
|
||||
var event = args[0];
|
||||
var check = proxy ? proxy.condition : null;
|
||||
|
||||
for (var target = event.target; target && target != this; target = target.parentNode){
|
||||
var finalTarget = document.id(target);
|
||||
if (Slick.match(target, split.value) && (!check || check.call(finalTarget, event))){
|
||||
if (finalTarget) fn.call(finalTarget, event, finalTarget);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}, {
|
||||
mouseenter: {
|
||||
base: 'mouseover',
|
||||
condition: Element.Events.mouseenter.condition
|
||||
},
|
||||
mouseleave: {
|
||||
base: 'mouseout',
|
||||
condition: Element.Events.mouseleave.condition
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
---
|
||||
|
||||
script: Element.Shortcuts.js
|
||||
|
||||
name: Element.Shortcuts
|
||||
|
||||
description: Extends the Element native object to include some shortcut methods.
|
||||
|
||||
license: MIT-style license
|
||||
|
||||
authors:
|
||||
- Aaron Newton
|
||||
|
||||
requires:
|
||||
- Core/Element.Style
|
||||
- /MooTools.More
|
||||
|
||||
provides: [Element.Shortcuts]
|
||||
|
||||
...
|
||||
*/
|
||||
|
||||
Element.implement({
|
||||
|
||||
isDisplayed: function(){
|
||||
return this.getStyle('display') != 'none';
|
||||
},
|
||||
|
||||
isVisible: function(){
|
||||
var w = this.offsetWidth,
|
||||
h = this.offsetHeight;
|
||||
return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.style.display != 'none';
|
||||
},
|
||||
|
||||
toggle: function(){
|
||||
return this[this.isDisplayed() ? 'hide' : 'show']();
|
||||
},
|
||||
|
||||
hide: function(){
|
||||
var d;
|
||||
try {
|
||||
//IE fails here if the element is not in the dom
|
||||
d = this.getStyle('display');
|
||||
} catch(e){}
|
||||
if (d == 'none') return this;
|
||||
return this.store('element:_originalDisplay', d || '').setStyle('display', 'none');
|
||||
},
|
||||
|
||||
show: function(display){
|
||||
if (!display && this.isDisplayed()) return this;
|
||||
display = display || this.retrieve('element:_originalDisplay') || 'block';
|
||||
return this.setStyle('display', (display == 'none') ? 'block' : display);
|
||||
},
|
||||
|
||||
swapClass: function(remove, add){
|
||||
return this.removeClass(remove).addClass(add);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Document.implement({
|
||||
|
||||
clearSelection: function(){
|
||||
if (document.selection && document.selection.empty){
|
||||
document.selection.empty();
|
||||
} else if (window.getSelection){
|
||||
var selection = window.getSelection();
|
||||
if (selection && selection.removeAllRanges) selection.removeAllRanges();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
MooTools.More={version:"1.3.1.1",build:"0292a3af1eea242b817fecf9daa127417d10d4ce"};(function(){var c={a:/[àáâãäåăą]/g,A:/[ÀÁÂÃÄÅĂĄ]/g,c:/[ćčç]/g,C:/[ĆČÇ]/g,d:/[ďđ]/g,D:/[ĎÐ]/g,e:/[èéêëěę]/g,E:/[ÈÉÊËĚĘ]/g,g:/[ğ]/g,G:/[Ğ]/g,i:/[ìíîï]/g,I:/[ÌÍÎÏ]/g,l:/[ĺľł]/g,L:/[ĹĽŁ]/g,n:/[ñňń]/g,N:/[ÑŇŃ]/g,o:/[òóôõöøő]/g,O:/[ÒÓÔÕÖØ]/g,r:/[řŕ]/g,R:/[ŘŔ]/g,s:/[ššş]/g,S:/[ŠŞŚ]/g,t:/[ťţ]/g,T:/[ŤŢ]/g,ue:/[ü]/g,UE:/[Ü]/g,u:/[ùúûůµ]/g,U:/[ÙÚÛŮ]/g,y:/[ÿý]/g,Y:/[ŸÝ]/g,z:/[žźż]/g,Z:/[ŽŹŻ]/g,th:/[þ]/g,TH:/[Þ]/g,dh:/[ð]/g,DH:/[Ð]/g,ss:/[ß]/g,oe:/[œ]/g,OE:/[Œ]/g,ae:/[æ]/g,AE:/[Æ]/g},b={" ":/[\xa0\u2002\u2003\u2009]/g,"*":/[\xb7]/g,"'":/[\u2018\u2019]/g,'"':/[\u201c\u201d]/g,"...":/[\u2026]/g,"-":/[\u2013]/g,"»":/[\uFFFD]/g};
|
||||
var a=function(f,h){var e=f,g;for(g in h){e=e.replace(h[g],g);}return e;};var d=function(e,g){e=e||"";var h=g?"<"+e+"(?!\\w)[^>]*>([\\s\\S]*?)</"+e+"(?!\\w)>":"</?"+e+"([^>]+)?>",f=new RegExp(h,"gi");
|
||||
return f;};String.implement({standardize:function(){return a(this,c);},repeat:function(e){return new Array(e+1).join(this);},pad:function(e,h,g){if(this.length>=e){return this;
|
||||
}var f=(h==null?" ":""+h).repeat(e-this.length).substr(0,e-this.length);if(!g||g=="right"){return this+f;}if(g=="left"){return f+this;}return f.substr(0,(f.length/2).floor())+this+f.substr(0,(f.length/2).ceil());
|
||||
},getTags:function(e,f){return this.match(d(e,f))||[];},stripTags:function(e,f){return this.replace(d(e,f),"");},tidy:function(){return a(this,b);},truncate:function(e,f,i){var h=this;
|
||||
if(f==null&&arguments.length==1){f="…";}if(h.length>e){h=h.substring(0,e);if(i){var g=h.lastIndexOf(i);if(g!=-1){h=h.substr(0,g);}}if(f){h+=f;}}return h;
|
||||
}});}).call(this);Element.implement({tidy:function(){this.set("value",this.get("value").tidy());},getTextInRange:function(b,a){return this.get("value").substring(b,a);
|
||||
},getSelectedText:function(){if(this.setSelectionRange){return this.getTextInRange(this.getSelectionStart(),this.getSelectionEnd());}return document.selection.createRange().text;
|
||||
},getSelectedRange:function(){if(this.selectionStart!=null){return{start:this.selectionStart,end:this.selectionEnd};}var e={start:0,end:0};var a=this.getDocument().selection.createRange();
|
||||
if(!a||a.parentElement()!=this){return e;}var c=a.duplicate();if(this.type=="text"){e.start=0-c.moveStart("character",-100000);e.end=e.start+a.text.length;
|
||||
}else{var b=this.get("value");var d=b.length;c.moveToElementText(this);c.setEndPoint("StartToEnd",a);if(c.text.length){d-=b.match(/[\n\r]*$/)[0].length;
|
||||
}e.end=d-c.text.length;c.setEndPoint("StartToStart",a);e.start=d-c.text.length;}return e;},getSelectionStart:function(){return this.getSelectedRange().start;
|
||||
},getSelectionEnd:function(){return this.getSelectedRange().end;},setCaretPosition:function(a){if(a=="end"){a=this.get("value").length;}this.selectRange(a,a);
|
||||
return this;},getCaretPosition:function(){return this.getSelectedRange().start;},selectRange:function(e,a){if(this.setSelectionRange){this.focus();this.setSelectionRange(e,a);
|
||||
}else{var c=this.get("value");var d=c.substr(e,a-e).replace(/\r/g,"").length;e=c.substr(0,e).replace(/\r/g,"").length;var b=this.createTextRange();b.collapse(true);
|
||||
b.moveEnd("character",e+d);b.moveStart("character",e);b.select();}return this;},insertAtCursor:function(b,a){var d=this.getSelectedRange();var c=this.get("value");
|
||||
this.set("value",c.substring(0,d.start)+b+c.substring(d.end,c.length));if(a!==false){this.selectRange(d.start,d.start+b.length);}else{this.setCaretPosition(d.start+b.length);
|
||||
}return this;},insertAroundCursor:function(b,a){b=Object.append({before:"",defaultMiddle:"",after:""},b);var c=this.getSelectedText()||b.defaultMiddle;
|
||||
var g=this.getSelectedRange();var f=this.get("value");if(g.start==g.end){this.set("value",f.substring(0,g.start)+b.before+c+b.after+f.substring(g.end,f.length));
|
||||
this.selectRange(g.start+b.before.length,g.end+b.before.length+c.length);}else{var d=f.substring(g.start,g.end);this.set("value",f.substring(0,g.start)+b.before+d+b.after+f.substring(g.end,f.length));
|
||||
var e=g.start+b.before.length;if(a!==false){this.selectRange(e,e+d.length);}else{this.setCaretPosition(e+f.length);}}return this;}});Events.Pseudos=function(g,c,e){var b="monitorEvents:";
|
||||
var a=function(h){return{store:h.store?function(i,j){h.store(b+i,j);}:function(i,j){(h.$monitorEvents||(h.$monitorEvents={}))[i]=j;},retrieve:h.retrieve?function(i,j){return h.retrieve(b+i,j);
|
||||
}:function(i,j){if(!h.$monitorEvents){return j;}return h.$monitorEvents[i]||j;}};};var f=function(j){if(j.indexOf(":")==-1||!g){return null;}var i=Slick.parse(j).expressions[0][0],m=i.pseudos,h=m.length,k=[];
|
||||
while(h--){if(g[m[h].key]){k.push({event:i.tag,value:m[h].value,pseudo:m[h].key,original:j});}}return k.length?k:null;};var d=function(h){return Object.merge.apply(this,h.map(function(i){return g[i.pseudo].options||{};
|
||||
}));};return{addEvent:function(m,p,j){var n=f(m);if(!n){return c.call(this,m,p,j);}var k=a(this),s=k.retrieve(m,[]),h=n[0].event,t=d(n),o=p,i=t[h]||{},l=Array.slice(arguments,2),r=this,q;
|
||||
if(i.args){l.append(Array.from(i.args));}if(i.base){h=i.base;}if(i.onAdd){i.onAdd(this);}n.each(function(u){var v=o;o=function(){(i.listener||g[u.pseudo].listener).call(r,u,v,arguments,q,t);
|
||||
};});q=o.bind(this);s.include({event:p,monitor:q});k.store(m,s);c.apply(this,[m,p].concat(l));return c.apply(this,[h,q].concat(l));},removeEvent:function(l,n){var m=f(l);
|
||||
if(!m){return e.call(this,l,n);}var j=a(this),o=j.retrieve(l);if(!o){return this;}var h=m[0].event,p=d(m),i=p[h]||{},k=Array.slice(arguments,2);if(i.args){k.append(Array.from(i.args));
|
||||
}if(i.base){h=i.base;}if(i.onRemove){i.onRemove(this);}e.apply(this,[l,n].concat(k));o.each(function(q,r){if(!n||q.event==n){e.apply(this,[h,q.monitor].concat(k));
|
||||
}delete o[r];},this);j.store(l,o);return this;}};};(function(){var b={once:{listener:function(e,f,d,c){f.apply(this,d);this.removeEvent(e.event,c).removeEvent(e.original,f);
|
||||
}},throttle:{listener:function(d,e,c){if(!e._throttled){e.apply(this,c);e._throttled=setTimeout(function(){e._throttled=false;},d.value||250);}}},pause:{listener:function(d,e,c){clearTimeout(e._pause);
|
||||
e._pause=e.delay(d.value||250,this,c);}}};Events.definePseudo=function(c,d){b[c]=Type.isFunction(d)?{listener:d}:d;return this;};Events.lookupPseudo=function(c){return b[c];
|
||||
};var a=Events.prototype;Events.implement(Events.Pseudos(b,a.addEvent,a.removeEvent));["Request","Fx"].each(function(c){if(this[c]){this[c].implement(Events.prototype);
|
||||
}});}).call(this);(function(){var d={},c=["once","throttle","pause"],b=c.length;while(b--){d[c[b]]=Events.lookupPseudo(c[b]);}Event.definePseudo=function(e,f){d[e]=Type.isFunction(f)?{listener:f}:f;
|
||||
return this;};var a=Element.prototype;[Element,Window,Document].invoke("implement",Events.Pseudos(d,a.addEvent,a.removeEvent));}).call(this);(function(){var b=!(window.attachEvent&&!window.addEventListener),e=Element.NativeEvents;
|
||||
e.focusin=2;e.focusout=2;var c=function(g,j,h){var i=Element.Events[g.event],k;if(i){k=i.condition;}return Slick.match(j,g.value)&&(!k||k.call(j,h));};
|
||||
var f=function(g){var h="$delegation:";return{base:"focusin",onRemove:function(i){i.retrieve(h+"forms",[]).each(function(j){j.retrieve(h+"listeners",[]).each(function(k){j.removeEvent(g,k);
|
||||
});j.eliminate(h+g+"listeners").eliminate(h+g+"originalFn");});},listener:function(q,r,p,s,t){var j=p[0],i=this.retrieve(h+"forms",[]),o=j.target,l=(o.get("tag")=="form")?o:j.target.getParent("form"),n=l.retrieve(h+"originalFn",[]),k=l.retrieve(h+"listeners",[]);
|
||||
i.include(l);this.store(h+"forms",i);if(!n.contains(r)){var m=function(u){if(c(q,this,u)){r.call(this,u);}};l.addEvent(g,m);n.push(r);k.push(m);l.store(h+g+"originalFn",n).store(h+g+"listeners",k);
|
||||
}}};};var a=function(g){return{base:"focusin",listener:function(j,k,h){var i={blur:function(){this.removeEvents(i);}};i[g]=function(l){if(c(j,this,l)){k.call(this,l);
|
||||
}};h[0].target.addEvents(i);}};};var d={mouseenter:{base:"mouseover"},mouseleave:{base:"mouseout"},focus:{base:"focus"+(b?"":"in"),args:[true]},blur:{base:b?"blur":"focusout",args:[true]}};
|
||||
if(!b){Object.append(d,{submit:f("submit"),reset:f("reset"),change:a("change"),select:a("select")});}Event.definePseudo("relay",{listener:function(j,k,i,g,h){var l=i[0];
|
||||
for(var n=l.target;n&&n!=this;n=n.parentNode){var m=document.id(n);if(c(j,m,l)){if(m){k.call(m,l,m);}return;}}},options:d});}).call(this);Element.implement({isDisplayed:function(){return this.getStyle("display")!="none";
|
||||
},isVisible:function(){var a=this.offsetWidth,b=this.offsetHeight;return(a==0&&b==0)?false:(a>0&&b>0)?true:this.style.display!="none";},toggle:function(){return this[this.isDisplayed()?"hide":"show"]();
|
||||
},hide:function(){var b;try{b=this.getStyle("display");}catch(a){}if(b=="none"){return this;}return this.store("element:_originalDisplay",b||"").setStyle("display","none");
|
||||
},show:function(a){if(!a&&this.isDisplayed()){return this;}a=a||this.retrieve("element:_originalDisplay")||"block";return this.setStyle("display",(a=="none")?"block":a);
|
||||
},swapClass:function(a,b){return this.removeClass(a).addClass(b);}});Document.implement({clearSelection:function(){if(window.getSelection){var a=window.getSelection();
|
||||
if(a&&a.removeAllRanges){a.removeAllRanges();}}else{if(document.selection&&document.selection.empty){try{document.selection.empty();}catch(b){}}}}});Request.JSONP=new Class({Implements:[Chain,Events,Options],options:{onRequest:function(a){if(this.options.log&&window.console&&console.log){console.log("JSONP retrieving script with url:"+a);
|
||||
}},onError:function(a){if(this.options.log&&window.console&&console.warn){console.warn("JSONP "+a+" will fail in Internet Explorer, which enforces a 2083 bytes length limit on URIs");
|
||||
}},url:"",callbackKey:"callback",injectScript:document.head,data:"",link:"ignore",timeout:0,log:false},initialize:function(a){this.setOptions(a);},send:function(c){if(!Request.prototype.check.call(this,c)){return this;
|
||||
}this.running=true;var d=typeOf(c);if(d=="string"||d=="element"){c={data:c};}c=Object.merge(this.options,c||{});var e=c.data;switch(typeOf(e)){case"element":e=document.id(e).toQueryString();
|
||||
break;case"object":case"hash":e=Object.toQueryString(e);}var b=this.index=Request.JSONP.counter++;var f=c.url+(c.url.test("\\?")?"&":"?")+(c.callbackKey)+"=Request.JSONP.request_map.request_"+b+(e?"&"+e:"");
|
||||
if(f.length>2083){this.fireEvent("error",f);}Request.JSONP.request_map["request_"+b]=function(){this.success(arguments,b);}.bind(this);var a=this.getScript(f).inject(c.injectScript);
|
||||
this.fireEvent("request",[f,a]);if(c.timeout){this.timeout.delay(c.timeout,this);}return this;},getScript:function(a){if(!this.script){this.script=new Element("script[type=text/javascript]",{async:true,src:a});
|
||||
}return this.script;},success:function(b,a){if(!this.running){return false;}this.clear().fireEvent("complete",b).fireEvent("success",b).callChain();},cancel:function(){if(this.running){this.clear().fireEvent("cancel");
|
||||
}return this;},isRunning:function(){return !!this.running;},clear:function(){this.running=false;if(this.script){this.script.destroy();this.script=null;
|
||||
}return this;},timeout:function(){if(this.running){this.running=false;this.fireEvent("timeout",[this.script.get("src"),this.script]).fireEvent("failure").cancel();
|
||||
}return this;}});Request.JSONP.counter=0;Request.JSONP.request_map={};
|
||||
@@ -22,7 +22,7 @@ Page.Settings = new Class({
|
||||
|
||||
if(self.current)
|
||||
self.toggleTab(self.current, true);
|
||||
|
||||
|
||||
self.toggleTab(action)
|
||||
self.current = action;
|
||||
|
||||
@@ -34,9 +34,9 @@ Page.Settings = new Class({
|
||||
var a = hide ? 'removeClass' : 'addClass';
|
||||
var c = 'active';
|
||||
|
||||
var g = self.groups[tab] || self.groups.general
|
||||
g.tab[a](c);
|
||||
g.group[a](c);
|
||||
var g = self.groups[tab] || self.groups.general;
|
||||
g.tab[a](c);
|
||||
g.group[a](c);
|
||||
|
||||
},
|
||||
|
||||
@@ -76,7 +76,7 @@ Page.Settings = new Class({
|
||||
self.containers = new Element('form.uniForm.containers')
|
||||
);
|
||||
|
||||
Object.each(json.sections, function(section, section_name){
|
||||
Object.each(json.options, function(section, section_name){
|
||||
|
||||
// Create tab
|
||||
var tab = new Element('li').adopt(
|
||||
@@ -132,13 +132,7 @@ var OptionBase = new Class({
|
||||
// Add focus events
|
||||
self.input.addEvents({
|
||||
'change': self.changed.bind(self),
|
||||
'keyup': self.changed.bind(self),
|
||||
'focus': function() {
|
||||
self.el.addClass(self.focused_class);
|
||||
},
|
||||
'blur' : function() {
|
||||
self.el.removeClass(self.focused_class);
|
||||
}
|
||||
'keyup': self.changed.bind(self)
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
var Uniform = new Class({
|
||||
Implements : [Options],
|
||||
|
||||
options : {
|
||||
focusedClass : 'focused',
|
||||
holderClass : 'ctrlHolder'
|
||||
},
|
||||
|
||||
initialize : function(options) {
|
||||
this.setOptions(options);
|
||||
|
||||
var focused = this.options.focusedClass;
|
||||
var holder = '.' + this.options.holderClass;
|
||||
|
||||
$(document.body).addEvents( {
|
||||
'focus:relay(input, select, textarea)' : function() {
|
||||
var parent = this.getParent(holder);
|
||||
if (parent)
|
||||
parent.addClass(focused);
|
||||
},
|
||||
'blur:relay(input, select, textarea)' : function() {
|
||||
var parent = this.getParent(holder);
|
||||
if (parent)
|
||||
parent.removeClass(focused);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -4,8 +4,11 @@
|
||||
<link rel="stylesheet" href="{{ url_for('.static', filename='style/main.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('.static', filename='style/uniform.generic.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('.static', filename='style/uniform.css') }}">
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/mootools.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/mootools_more.js') }}"></script>
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/uniform.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/couchpotato.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/history.js') }}"></script>
|
||||
|
||||
@@ -25,11 +28,13 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
window.addEvent('domready', function() {
|
||||
new Uniform();
|
||||
var cp = new CouchPotato({
|
||||
'base_url': '{{ request.path }}',
|
||||
'api': {
|
||||
'url': '{{ url_for('api.index') }}',
|
||||
'path_sep': '{{ sep }}'
|
||||
'path_sep': '{{ sep }}',
|
||||
'is_remote': false
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user