Browse Source

Searcher, settings styling

pull/1/merge
Ruud 14 years ago
parent
commit
322762d702
  1. 35
      CouchPotato.py
  2. 8
      couchpotato/cli.py
  3. 126
      couchpotato/core/__init__.py
  4. 5
      couchpotato/core/loader.py
  5. 60
      couchpotato/core/plugins/renamer/__init__.py
  6. 26
      couchpotato/core/providers/tmdb/__init__.py
  7. 14
      couchpotato/core/providers/tmdb/main.py
  8. 2
      couchpotato/core/settings/__init__.py
  9. 9
      couchpotato/core/settings/model.py
  10. 2
      couchpotato/environment.py
  11. BIN
      couchpotato/static/images/close_button.png
  12. 4
      couchpotato/static/scripts/block.js
  13. 165
      couchpotato/static/scripts/block/search.js
  14. 67
      couchpotato/static/scripts/couchpotato.js
  15. 71
      couchpotato/static/scripts/eventstack.js
  16. 30
      couchpotato/static/scripts/eventstack_outerclick.js
  17. 5478
      couchpotato/static/scripts/mootools.js
  18. 2062
      couchpotato/static/scripts/mootools_more.js
  19. 140
      couchpotato/static/scripts/mootools_tween_css3.js
  20. 4
      couchpotato/static/scripts/page.js
  21. 133
      couchpotato/static/scripts/page/settings.js
  22. 2
      couchpotato/static/scripts/page/wanted.js
  23. 24
      couchpotato/static/style/main.css
  24. 119
      couchpotato/static/style/movie_add.css
  25. 126
      couchpotato/static/style/page/settings.css
  26. 19
      couchpotato/templates/_desktop.html

35
CouchPotato.py

@ -2,11 +2,9 @@
# -*- coding: utf-8 -*-
'''Wrapper for the command line interface.'''
from os.path import dirname, isfile
from os.path import dirname
import os
import subprocess
import sys
import traceback
# Root path
base_path = dirname(os.path.abspath(__file__))
@ -17,37 +15,8 @@ sys.path.insert(0, os.path.join(base_path, 'libs'))
from couchpotato.core.logger import CPLog
log = CPLog(__name__)
try:
from couchpotato import cli
except ImportError, e:
log.info('Checking local dependencies...')
if isfile(__file__):
cwd = dirname(__file__)
log.info('Updating libraries...')
stdout, stderr = subprocess.Popen(['git', 'submodule', 'init'],
stderr = subprocess.PIPE,
stdout = subprocess.PIPE).communicate()
if stderr:
log.info('[WARNING] Git is complaining:')
log.info(stderr)
stdout, stderr = subprocess.Popen(['git', 'submodule', 'update'],
stderr = subprocess.PIPE,
stdout = subprocess.PIPE).communicate()
if stderr:
log.info('[WARNING] Git is complaining:')
log.info(stderr)
log.info('Passing execution to couchpotato...')
try:
from couchpotato import cli
except ImportError:
log.error("[ERROR]: Something's seriously wrong.")
log.error(traceback.print_exc())
sys.exit(1)
else:
# Running from Titanium
raise NotImplementedError("Don't know how to do that.")
from couchpotato import cli
if __name__ == '__main__':
try:
cli.cmd_couchpotato(base_path, sys.argv[1:])

8
couchpotato/cli.py

@ -1,7 +1,6 @@
from argparse import ArgumentParser
from couchpotato import get_engine, web
from couchpotato import web
from couchpotato.api import api
from couchpotato.core.settings.model import *
from libs.daemon import createDaemon
from logging import handlers
import logging
@ -110,9 +109,8 @@ def cmd_couchpotato(base_path, args):
upgrade(db, repo)
# Configure Database
from elixir import setup_all, create_all
setup_all()
create_all(get_engine())
from couchpotato.core.settings.model import setup
setup()
# Create app

126
couchpotato/core/__init__.py

@ -4,59 +4,81 @@ def start():
pass
config = [{
'name': 'global',
'tab': 'general',
'options': {
'debug': {
'advanced': True,
'default': False,
'type': 'bool',
'label': 'Debug',
'description': 'Enable debugging.',
},
'host': {
'advanced': True,
'default': '0.0.0.0',
'type': 'string',
'label': 'Host',
'description': 'Host that I should listen to 0.0.0.0 listens to everything.',
},
'port': {
'default': 5000,
'type': 'int',
'label': 'Port',
'description': 'The port I should listen to.',
},
'username': {
'default': '',
'type': 'string',
'label': 'Username',
},
'password': {
'default': '',
'password': True,
'type': 'string',
'label': 'Password',
},
'launch_browser': {
'default': True,
'type': 'bool',
'label': 'Launch Browser',
'description': 'Launch the browser when I start.',
'name': 'core',
'groups': [
{
'tab': 'general',
'name': 'basics',
'label': 'Basics',
'description': 'Needs restart before changes take effect.',
'options': [
{
'name': 'username',
'default': '',
'type': 'string',
'label': 'Username',
},
{
'name': 'password',
'default': '',
'password': True,
'type': 'string',
'label': 'Password',
},
{
'name': 'host',
'advanced': True,
'default': '0.0.0.0',
'type': 'string',
'label': 'Host',
'description': 'Host that I should listen to. "0.0.0.0" listens to all ips.',
},
{
'name': 'port',
'default': 5000,
'type': 'int',
'label': 'Port',
'description': 'The port I should listen to.',
},
{
'name': 'launch_browser',
'default': True,
'type': 'bool',
'label': 'Launch Browser',
'description': 'Launch the browser when I start.',
},
],
},
'url_base': {
{
'tab': 'general',
'name': 'advanced',
'label': 'Advanced',
'description': "For those who know what the're doing",
'advanced': True,
'default': '',
'type': 'string',
'label': 'Url Base',
'description': 'When using mod_proxy use this to prepend the url with this.',
'options': [
{
'name': 'api_key',
'default': uuid4().hex,
'type': 'string',
'readonly': True,
'label': 'Api Key',
'description': "This is top-secret! Don't share this!",
},
{
'name': 'debug',
'default': False,
'type': 'bool',
'label': 'Debug',
'description': 'Enable debugging.',
},
{
'name': 'url_base',
'default': '',
'type': 'string',
'label': 'Url Base',
'description': 'When using mod_proxy use this to prepend the url with this.',
},
],
},
'api_key': {
'default': uuid4().hex,
'type': 'string',
'readonly': True,
'label': 'Api Key',
'description': 'This is top-secret! Don\'t share this!',
}
}
],
}]

5
couchpotato/core/loader.py

@ -53,8 +53,9 @@ class Loader:
for section in module.config:
fireEventAsync('settings.options', section['name'], section)
options = {}
for key, option in section['options'].iteritems():
options[key] = option['default']
for group in section['groups']:
for option in group['options']:
options[option['name']] = option['default']
fireEventAsync('settings.register', section_name = section['name'], options = options, save = save)
return True
except Exception, e:

60
couchpotato/core/plugins/renamer/__init__.py

@ -1,33 +1,33 @@
def start():
pass
config = [{
'name': 'Renamer',
'tab': 'renaming',
'options': {
'enabled': {
'default': False,
'type': 'bool',
'description': 'Enable renaming',
},
'from': {
'default': '',
'type': 'directory',
'label': 'From',
'description': 'Folder where the movies are downloaded to.',
},
'to': {
'default': '',
'type': 'directory',
'label': 'To',
'description': 'Folder where the movies will be moved to.',
},
'run_every': {
'default': 1,
'type': 'int',
'unit': 'min(s)',
'description': 'Search for new movies inside the folder every X minutes.',
}
}
}]
#config = [{
# 'name': 'Renamer',
# 'tab': 'renaming',
# 'options': {
# 'enabled': {
# 'default': False,
# 'type': 'bool',
# 'description': 'Enable renaming',
# },
# 'from': {
# 'default': '',
# 'type': 'directory',
# 'label': 'From',
# 'description': 'Folder where the movies are downloaded to.',
# },
# 'to': {
# 'default': '',
# 'type': 'directory',
# 'label': 'To',
# 'description': 'Folder where the movies will be moved to.',
# },
# 'run_every': {
# 'default': 1,
# 'type': 'int',
# 'unit': 'min(s)',
# 'description': 'Search for new movies inside the folder every X minutes.',
# }
# }
#}]
config = []

26
couchpotato/core/providers/tmdb/__init__.py

@ -4,14 +4,22 @@ def start():
return TMDB()
config = [{
'name': 'TheMovieDB',
'tab': 'providers',
'options': {
'api_key': {
'name': 'themoviedb',
'groups': [
{
'tab': 'providers',
'name': 'tmdb',
'label': 'TheMovieDB',
'advanced': True,
'default': '9b939aee0aaafc12a65bf448e4af9543',
'type': 'string',
'description': 'Api key to use for calls to TheMovieDB.',
}
}
'options': [
{
'name': 'api_key',
'default': '9b939aee0aaafc12a65bf448e4af9543',
'type': 'string',
'label': 'Api Key',
'description': 'Used for all calls to TheMovieDB.',
},
],
},
],
}]

14
couchpotato/core/providers/tmdb/main.py

@ -5,8 +5,9 @@ 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 copy
import simplejson as json
import urllib2
log = CPLog(__name__)
@ -21,7 +22,7 @@ class TMDB(Provider):
addEvent('provider.movie.search', self.search)
def conf(self, attr):
return Env.setting(attr, 'TheMovieDB')
return Env.setting(attr, 'themoviedb')
def search(self, q, limit = 12, alternative = True):
''' Find movie by name '''
@ -48,7 +49,7 @@ class TMDB(Provider):
nr = 0
for movie in data:
year = movie['released'][:4]
year = str(movie['released'])[:4]
# Poster url
poster = ''
@ -59,7 +60,7 @@ class TMDB(Provider):
break
# 1900 is the same as None
if year == '1900':
if year == '1900' or year.lower() == 'none':
year = None
movie_data = {
@ -70,14 +71,13 @@ class TMDB(Provider):
'year': year,
'tagline': 'This is the tagline of the movie',
}
results.append(movie_data)
results.append(copy.deepcopy(movie_data))
alternativeName = movie['alternative_name']
if alternativeName and alternative:
if alternativeName.lower() != movie['name'].lower() and alternativeName.lower() != 'none' and alternativeName != None:
movie_data['name'] = toUnicode(alternativeName)
results.append(movie_data)
results.append(copy.deepcopy(movie_data))
nr += 1
if nr == limit:
break

2
couchpotato/core/settings/__init__.py

@ -52,7 +52,7 @@ class Settings():
def set(self, section, option, value):
return self.p.set(section, option, self.cleanValue(value))
def get(self, option = '', section = 'global', default = ''):
def get(self, option = '', section = 'core', default = ''):
try:
value = self.p.get(section, option)
return self.cleanValue(value)

9
couchpotato/core/settings/model.py

@ -121,3 +121,12 @@ class RenameHistory(Entity):
new = Field(String(255))
file = ManyToOne('File')
def setup():
""" Setup the database and create the tables that don't exists yet """
from elixir import setup_all, create_all
from couchpotato import get_engine
setup_all()
create_all(get_engine())

2
couchpotato/environment.py

@ -31,7 +31,7 @@ class Env:
return setattr(Env, '_' + attr, value)
@staticmethod
def setting(attr, section = 'global', value = None, default = ''):
def setting(attr, section = 'core', value = None, default = ''):
# Return setting
if value == None:

BIN
couchpotato/static/images/close_button.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

4
couchpotato/static/scripts/block.js

@ -17,10 +17,6 @@ var BlockBase = new Class({
this.el = new Element('div.block');
},
api: function(){
return this.getParent().getApi()
},
getParent: function(){
return this.page
},

165
couchpotato/static/scripts/block/search.js

@ -8,17 +8,57 @@ Block.Search = new Class({
var self = this;
self.el = new Element('div.search_form').adopt(
self.input = new Element('input', {
'events': {
'keyup': self.keyup.bind(self)
new Element('div.input').adopt(
self.input = new Element('input', {
'events': {
'keyup': self.keyup.bind(self),
'focus': self.hideResults.bind(self, false)
}
}),
new Element('a', {
'events': {
'click': self.clear.bind(self)
}
})
),
self.result_container = new Element('div.results_container', {
'tween': {
'duration': 200
}
}),
self.results = new Element('div.results')
}).adopt(
new Element('div.pointer'),
self.results = new Element('div.results')
).fade('hide')
);
// Debug
self.input.set('value', 'iron man');
self.autocomplete(0)
self.spinner = new Spinner(self.result_container);
self.OuterClickStack = new EventStack.OuterClick();
},
clear: function(e){
var self = this;
(e).stop();
self.input.set('value', '');
self.input.focus()
self.movies = []
self.results.empty()
},
hideResults: function(bool){
var self = this;
if(self.hidden == bool) return;
self.result_container.fade(bool ? 0 : 1)
if(!bool && self.OuterClickStack.stack.length == 0)
self.OuterClickStack.push(self.hideResults.bind(self, true), self.el);
self.hidden = bool;
},
keyup: function(e){
@ -36,6 +76,13 @@ Block.Search = new Class({
autocomplete: function(delay){
var self = this;
if(!self.q()){
self.hideResults(true)
return
}
self.spinner.show()
if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer)
self.autocomplete_timer = self.list.delay((delay || 300), self)
},
@ -48,13 +95,16 @@ Block.Search = new Class({
var q = self.q();
var cache = self.cache[q];
if(!cache)
self.api_request = self.api().request('movie.add.search', {
self.hideResults(false)
if(!cache){
self.api_request = Api.request('movie.add.search', {
'data': {
'q': q
},
'onComplete': self.fill.bind(self, q)
})
}
else
self.fill(q, cache)
@ -65,20 +115,28 @@ Block.Search = new Class({
fill: function(q, json){
var self = this;
self.spinner.hide();
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)
if(!movie.imdb || (movie.imdb && !self.results.getElement('#'+movie.imdb))){
var m = new Block.Search.Item(movie);
$(m).inject(self.results)
}
self.movies.include(m)
});
},
loading: function(bool){
this.el[bool ? 'addClass' : 'removeClass']('loading')
},
q: function(){
return this.input.get('value').trim();
}
@ -93,31 +151,58 @@ Block.Search.Item = new Class({
self.info = info;
self.create();
self.OuterClickStack = new EventStack.OuterClick();
},
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
var info = self.info
self.el = new Element('div.movie', {
'id': info.imdb
}).adopt(
new Element('div.add').adopt(
new Element('span', {
'text': 'test'
})
),
self.data_container = new Element('div.data', {
'tween': {
duration: 400,
transition: 'quint:in:out'
},
'events': {
'click': self.showOptions.bind(self)
}
}).adopt(
self.thumbnail = info.poster ? new Element('img.thumbnail', {
'src': info.poster
}) : null,
new Element('div.info').adopt(
self.name = new Element('h2', {
'text': info.name
}).adopt(
self.year = info.year ? new Element('span', {
'text': info.year
}) : null
),
self.tagline = new Element('span', {
'text': info.tagline
}),
self.director = self.info.director ? new Element('span', {
'text': 'Director:' + info.director
}) : null,
self.starring = info.actors ? new Element('span', {
'text': 'Starring:'
}) : null
)
)
)
if(self.info.actors){
Object.each(self.info.actors, function(actor){
if(info.actors){
Object.each(info.actors, function(actor){
new Element('span', {
'text': actor.name
}).inject(self.starring)
@ -125,6 +210,24 @@ Block.Search.Item = new Class({
}
},
showOptions: function(){
var self = this;
if(!self.width)
self.width = self.data_container.getCoordinates().width
self.data_container.tween('margin-left', 0, self.width);
self.OuterClickStack.push(self.closeOptions.bind(self), self.el);
},
closeOptions: function(){
var self = this;
self.data_container.tween('margin-left', self.width, 0);
},
toElement: function(){
return this.el
}

67
couchpotato/static/scripts/couchpotato.js

@ -18,7 +18,6 @@ var CouchPotato = new Class({
self.c = $(document.body)
self.route = new Route(self.defaults);
self.api = new Api(self.options.api)
self.createLayout();
self.createPages();
@ -43,8 +42,10 @@ var CouchPotato = new Class({
self.c.adopt(
self.header = new Element('div.header').adopt(
self.block.navigation = new Block.Navigation(self, {}),
self.block.search = new Block.Search(self, {})
new Element('div').adopt(
self.block.navigation = new Block.Navigation(self, {}),
self.block.search = new Block.Search(self, {})
)
),
self.content = new Element('div.content'),
self.block.footer = new Block.Footer(self, {})
@ -71,36 +72,29 @@ var CouchPotato = new Class({
var action = self.route.getAction();
var params = self.route.getParams();
if(self.current_page)
self.current_page.hide()
var page = self.pages[page_name];
page.open(action, params);
page.show();
if(self.current_page)
self.current_page.hide()
self.current_page = page;
},
getBlock: function(block_name){
return this.block[block_name]
},
getApi: function(){
return this.api
}
});
var Api = new Class({
url: '',
var ApiClass = new Class({
initialize: function(options){
setup: function(options){
var self = this
self.options = options;
},
request: function(type, options){
@ -123,6 +117,7 @@ var Api = new Class({
}
});
window.Api = new ApiClass()
var Route = new Class({
@ -183,4 +178,44 @@ var Route = new Class({
var p = function(){
if(typeof(console) !== 'undefined' && console != null)
console.log(arguments)
}
};
(function(){
var keyPaths = [];
var saveKeyPath = function(path) {
keyPaths.push({
sign: (path[0] === '+' || path[0] === '-')? parseInt(path.shift()+1) : 1,
path: path
});
};
var valueOf = function(object, path) {
var ptr = object;
path.each(function(key) { ptr = ptr[key] });
return ptr;
};
var comparer = function(a, b) {
for (var i = 0, l = keyPaths.length; i < l; i++) {
aVal = valueOf(a, keyPaths[i].path);
bVal = valueOf(b, keyPaths[i].path);
if (aVal > bVal) return keyPaths[i].sign;
if (aVal < bVal) return -keyPaths[i].sign;
}
return 0;
};
Array.implement('sortBy', function(){
keyPaths.empty();
Array.each(arguments, function(argument) {
switch (typeOf(argument)) {
case "array": saveKeyPath(argument); break;
case "string": saveKeyPath(argument.match(/[+-]|[^.]+/g)); break;
}
});
return this.sort(comparer);
});
})();

71
couchpotato/static/scripts/eventstack.js

@ -0,0 +1,71 @@
/*
---
name: EventStack
description: Helps you Escape.
authors: Christoph Pojer (@cpojer)
license: MIT-style license.
requires: [Core/Class.Extras, Core/Element.Event, Class-Extras/Class.Binds]
provides: EventStack
...
*/
(function(){
this.EventStack = new Class({
Implements: [Options, Class.Binds],
options: {
event: 'keyup',
condition: function(event){
return (event.key == 'esc');
}
},
initialize: function(options){
this.setOptions(options);
this.stack = [];
this.data = [];
document.addEvent(this.options.event, this.bound('condition'));
},
condition: function(event){
if (this.options.condition.call(this, event, this.data.getLast()))
this.pop(event);
},
erase: function(fn){
this.data.erase(this.data[this.stack.indexOf(fn)]);
this.stack.erase(fn);
return this;
},
push: function(fn, data){
this.erase(fn);
this.data.push(data || null);
this.stack.push(fn);
return this;
},
pop: function(event){
var fn = this.stack.pop(),
data = this.data.pop();
if (fn) fn.call(this, event, data);
return this;
}
});
}).call(this);

30
couchpotato/static/scripts/eventstack_outerclick.js

@ -0,0 +1,30 @@
/*
---
name: EventStack.OuterClick
description: Helps you escape from clicks outside of a certain area.
authors: Christoph Pojer (@cpojer)
license: MIT-style license.
requires: [EventStack]
provides: EventStack.OuterClick
...
*/
EventStack.OuterClick = new Class({
Extends: EventStack,
options: {
event: 'click',
condition: function(event, element){
return element && !element.contains(event.target);
}
}
});

5478
couchpotato/static/scripts/mootools.js

File diff suppressed because it is too large

2062
couchpotato/static/scripts/mootools_more.js

File diff suppressed because it is too large

140
couchpotato/static/scripts/mootools_tween_css3.js

@ -0,0 +1,140 @@
/*
---
name: Fx.Tween.CSS3.Replacement
script: Fx.Tween.CSS3.Replacement.js
license: MIT-style license.
description: Same behavior like Fx.Tween but tries to use native CSS3 transition if possible (Overwrites Fx.Tween).
copyright: Copyright (c) 2010, Dipl.-Ing. (FH) André Fiedler <kontakt at visualdrugs dot net>, based on code by eskimoblood (mootools users group)
authors: [André Fiedler, eskimoblood]
requires: [Core/Class.Extras, Core/Element.Event, Core/Element.Style, Core/Fx.Tween]
provides: [Fx.Tween]
...
*/
Element.NativeEvents.transitionend = 2;
Element.NativeEvents.webkitTransitionEnd = 2;
Element.NativeEvents.oTransitionEnd = 2;
Element.Events.transitionend = {
base: Browser.safari || Browser.chrome ? 'webkitTransitionEnd' : (Browser.opera ? 'oTransitionEnd' : 'transitionend')
};
Event.implement({
getPropertyName: function(){
return this.event.propertyName;
},
getElapsedTime: function(nativeTime){
return nativeTime ? this.event.elapsedTime : (this.event.elapsedTime * 1000).toInt();
}
});
Element.implement({
supportStyle: function(style){
var value = this.style[style];
return !!(value || value == '');
},
supportVendorStyle: function(style){
var prefixedStyle = null;
return this.supportStyle(style) ? style : ['webkit', 'Moz', 'o'].some(function(prefix){
prefixedStyle = prefix + style.capitalize();
return this.supportStyle(prefixedStyle);
}, this) ? prefixedStyle : null;
}
});
Fx.TweenCSS2 = Fx.Tween;
Fx.Tween = new Class({
Extends: Fx.TweenCSS2,
transitionTimings: {
'linear' : '0,0,1,1',
'expo:in' : '0.71,0.01,0.83,0',
'expo:out' : '0.14,1,0.32,0.99',
'expo:in:out' : '0.85,0,0.15,1',
'circ:in' : '0.34,0,0.96,0.23',
'circ:out' : '0,0.5,0.37,0.98',
'circ:in:out' : '0.88,0.1,0.12,0.9',
'sine:in' : '0.22,0.04,0.36,0',
'sine:out' : '0.04,0,0.5,1',
'sine:in:out' : '0.37,0.01,0.63,1',
'quad:in' : '0.14,0.01,0.49,0',
'quad:out' : '0.01,0,0.43,1',
'quad:in:out' : '0.47,0.04,0.53,0.96',
'cubic:in' : '0.35,0,0.65,0',
'cubic:out' : '0.09,0.25,0.24,1',
'cubic:in:out' : '0.66,0,0.34,1',
'quart:in' : '0.69,0,0.76,0.17',
'quart:out' : '0.26,0.96,0.44,1',
'quart:in:out' : '0.76,0,0.24,1',
'quint:in' : '0.64,0,0.78,0',
'quint:out' : '0.22,1,0.35,1',
'quint:in:out' : '0.9,0,0.1,1'
},
initialize: function(element, options){
options.transition = options.transition || 'sine:in:out';
this.parent(element, options);
if (typeof this.options.transition != 'string') alert('Only short notated transitions (like \'sine:in\') are supported by Fx.Tween.CSS3');
this.options.transition = this.options.transition.toLowerCase();
this.transition = this.element.supportVendorStyle('transition');
this.css3Supported = !!this.transition && !!this.transitionTimings[this.options.transition];
},
check: function(){
if (!this.boundComplete) return true;
return this.parent();
},
start: function(property, from, to){
if (this.css3Supported){
if (!this.check(property, from, to)) return this;
var args = Array.flatten(arguments);
this.property = this.options.property || args.shift();
var parsed = this.prepare(this.element, this.property, args);
this.from = parsed.from;
this.to = parsed.to;
this.boundComplete = function(event){
if (event.getPropertyName() == this.property /* && event.getElapsedTime() == this.options.duration */ ){
this.element.removeEvent('transitionend', this.boundComplete);
this.boundComplete = null;
this.fireEvent('complete', this.subject);
}
}.bind(this);
this.element.addEvent('transitionend', this.boundComplete);
var trans = function(){
this.element.setStyle(this.transition, this.property + ' ' + this.options.duration + 'ms cubic-bezier(' + this.transitionTimings[this.options.transition] + ')');
this.element.setStyle(this.property, this.to[0].value + + this.options.unit);
}.bind(this);
if (args[1]){
this.element.setStyle(this.transition, 'none');
this.element.setStyle(this.property, this.from[0].value + + this.options.unit);
trans.delay(0.1);
} else
trans();
this.fireEvent('start', this.subject);
return this;
}
return this.parent(property, from, to);
},
cancel: function(){
if (this.css3Supported){
this.element.setStyle(this.transition, 'none');
this.element.removeEvent('transitionend', this.boundComplete);
this.boundComplete = null;
}
this.parent();
return this;
}
});

4
couchpotato/static/scripts/page.js

@ -57,10 +57,6 @@ var PageBase = new Class({
getParent: function(){
return this.app
},
api: function(){
return this.getParent().getApi()
},
show: function(){
this.el.addClass('active');

133
couchpotato/static/scripts/page/settings.js

@ -5,11 +5,19 @@ Page.Settings = new Class({
name: 'settings',
title: 'Change settings.',
groups: {},
tabs: {
'general': {
'label': 'General'
},
'providers': {
'label': 'Providers'
}
},
open: function(action, params){
var self = this
//p('open config', action, params)
self.action = action;
self.params = params;
if(!self.data)
self.getData(self.create.bind(self))
@ -19,6 +27,7 @@ Page.Settings = new Class({
openTab: function(action){
var self = this;
action = action || self.action
if(self.current)
self.toggleTab(self.current, true);
@ -27,16 +36,16 @@ Page.Settings = new Class({
self.current = action;
},
toggleTab: function(tab, hide){
toggleTab: function(tab_name, hide){
var self = this;
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 t = self.tabs[tab_name] || self.tabs[self.action] || self.tabs.general;
t.tab[a](c);
t.content[a](c);
},
@ -44,7 +53,7 @@ Page.Settings = new Class({
var self = this;
if(onComplete)
self.api().request('settings', {
Api.request('settings', {
'useSpinner': true,
'spinnerOptions': {
'target': self.el
@ -68,38 +77,79 @@ Page.Settings = new Class({
}
},
showAdvanced: function(){
var self = this;
var c = self.advanced_toggle.checked ? 'addClass' : 'removeClass';
self.el[c]('show_advanced')
},
create: function(json){
var self = this
self.el.adopt(
self.tabs = new Element('ul.tabs'),
self.containers = new Element('form.uniForm.containers')
self.tabs_container = new Element('ul.tabs'),
self.containers = new Element('form.uniForm.containers').adopt(
new Element('label.advanced_toggle').adopt(
new Element('span', {
'text': 'Show advanced settings'
}),
self.advanced_toggle = new Element('input[type=checkbox]', {
'events': {
'change': self.showAdvanced.bind(self)
}
})
)
)
);
Object.each(json.options, function(section, section_name){
// Create tab
var tab = new Element('li').adopt(
new Element('a', {
'href': '/'+self.name+'/'+section.tab+'/',
'text': section.tab.capitalize()
// Create tabs
Object.each(self.tabs, function(tab, tab_name){
if(!self.tabs[tab_name].tab){
var tab_el = new Element('li').adopt(
new Element('a', {
'href': '/'+self.name+'/'+tab_name+'/',
'text': tab.label.capitalize()
})
).inject(self.tabs_container);
self.tabs[tab_name] = Object.merge(self.tabs[tab_name], {
'tab': tab_el,
'content': new Element('div.tab_content').inject(self.containers),
'groups': {}
})
).inject(self.tabs);
var group = new Element('div.group').inject(self.containers);
self.groups[section.tab] = {
'tab': tab,
'group': group
}
});
// Add section
var fieldset = new Element('fieldset.inlineLabels').inject(group)
Object.each(section.options, function(option, option_name){
var class_name = (option.type || 'input').capitalize();
var input = new Option[class_name](self, section_name, option_name, option);
input.inject(fieldset);
});
// Add content to tabs
Object.each(json.options, function(section, section_name){
// Add groups to content
section.groups.sortBy('order').each(function(group){
// Create the group
var group_el = new Element('fieldset', {
'class': group.advanced ? 'inlineLabels advanced' : 'inlineLabels'
}).adopt(
new Element('h2', {
'text': group.label
}).adopt(
new Element('span.hint', {
'text': group.description
})
)
).inject(self.tabs[group.tab].content);
self.tabs[group.tab].groups[group.name] = group_el
// Add options to group
group.options.sortBy('order').each(function(option){
var class_name = (option.type || 'input').capitalize();
var input = new Option[class_name](self, section_name, option.name, option);
input.inject(group_el);
});
});
});
self.openTab();
@ -251,6 +301,7 @@ Option.String = new Class({
'text': self.options.label
}),
self.input = new Element('input', {
'type': 'text',
'name': self.postName(),
'value': self.getSettingValue()
})
@ -291,15 +342,19 @@ Option.Checkbox = new Class({
create: function(){
var self = this;
var randomId = 'option-'+Math.floor(Math.random()*1000000)
new Element('label', {
'text': self.options.label
}).adopt(
self.input = new Element('input', {
'type': 'checkbox',
'value': self.getSettingValue(),
'checked': self.getSettingValue() !== undefined
})
).inject(self.el);
'text': self.options.label,
'for': randomId
}).inject(self.el);
self.input = new Element('input', {
'type': 'checkbox',
'value': self.getSettingValue(),
'checked': self.getSettingValue() !== undefined,
'id': randomId
}).inject(self.el);
}
});

2
couchpotato/static/scripts/page/wanted.js

@ -30,7 +30,7 @@ Page.Wanted = new Class({
var self = this
if(self.movies.length == 0)
self.api().request('movie', {
Api.request('movie', {
'data': {},
'onComplete': function(json){
self.store(json.movies);

24
couchpotato/static/style/main.css

@ -1,8 +1,11 @@
/* @override http://localhost:5000/static/style/main.css */
html {
color: #343434;
font-size: 12px;
line-height: 1.5;
font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif;
font-family: Helvetica, Arial, Geneva, sans-serif;
height: 100%;
}
body {
@ -10,6 +13,7 @@ body {
padding: 0;
background: #fff;
overflow-y: scroll;
height: 100%;
}
body.noscroll { overflow: hidden; }
@ -42,11 +46,13 @@ a {
a:hover { color: #4d66c4; }
.page {
display: none;
width: 960px;
margin: 0 auto;
line-height: 24px;
padding: 0 0 20px;
}
.page.active { display: block; }
.page .noticeMe {
background-color: lightgoldenrodyellow;
@ -59,7 +65,7 @@ a:hover { color: #4d66c4; }
.content {
clear:both;
padding: 130px 10px 10px;
padding: 80px 10px 10px;
}
.footer {
@ -99,7 +105,6 @@ form {
.header {
background: #f7f7f7;
padding:10px;
margin-bottom: 20px;
border-bottom: 1px solid #f1f1f1;
height: 60px;
-moz-box-shadow: 0 0 30px rgba(0,0,0,0.1);
@ -109,20 +114,24 @@ form {
z-index: 9999;
}
.header .navigation {
.header > div {
width: 960px;
margin: 0 auto;
overflow: hidden;
}
.header .navigation {
display: inline-block;
width: 75%;
margin: 0;
padding: 0;
}
.header .navigation li {
color: #8b8b8b;
display: block;
display: inline-block;
font-size:20px;
font-weight: bold;
margin: 0;
text-align: center;
float: left;
}
.header .navigation li a {
@ -153,3 +162,4 @@ form {
.header .navigation li a:hover, .header .navigation li a:active {
color: #8b8b8b;
}

119
couchpotato/static/style/movie_add.css

@ -0,0 +1,119 @@
/* @override http://localhost:5000/static/style/movie_add.css */
.search_form {
display: inline-block;
width: 25%;
}
.search_form input {
padding-right: 25px;
border: 1px solid #aaa;
padding: 4px;
margin: 0;
font-size: 14px;
width: 90%;
border-radius: 3px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
}
.search_form .input a {
width: 12px;
height: 20px;
display: inline-block;
margin: 0 0 -5px -20px;
top: 4px;
right: 5px;
background: url('../images/close_button.png') 0 center no-repeat;
cursor: pointer;
}
.search_form .input a:hover { background-position: -12px center; }
.search_form .results_container {
padding: 10px 0;
position: absolute;
background: #fff;
margin: 11px 0 0 -243px;
width: 470px;
min-height: 140px;
box-shadow: 0 0 30px rgba(0,0,0,0.2);
-moz-box-shadow: 0 0 30px rgba(0,0,0,0.2);
-webkit-box-shadow: 0 0 30px rgba(0,0,0,0.2);
border-radius: 3px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
}
.search_form .spinner {
background: #fff url('../images/spinner.gif') no-repeat center 70px;
}
.search_form .pointer {
border-right: 10px solid transparent;
border-left: 10px solid transparent;
border-bottom: 10px solid #fff;
display: block;
position: absolute;
width: 0px;
left: 50%;
margin: -19px 0 0 110px;
}
.search_form .results .movie {
overflow: hidden;
background: #666;
min-height: 140px;
}
.search_form .results .movie .add {
min-height: 140px;
}
.search_form .results .movie .data {
padding: 0 15px;
width: 440px;
position: relative;
min-height: 100px;
top: 0;
margin: -140px 0 0 0;
background: #fff;
min-height: 140px;
}
.search_form .results .movie .thumbnail {
width: 17%;
display: inline-block;
margin: 15px 3% 15px 0;
vertical-align: top;
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
box-shadow: 0 0 3px rgba(0,0,0,0.35);
-moz-box-shadow: 0 0 3px rgba(0,0,0,0.35);
-webkit-box-shadow: 0 0 3px rgba(0,0,0,0.35);
}
.search_form .results .movie .info {
width: 74%;
display: inline-block;
vertical-align: top;
padding: 15px 0;
background: #fff;
}
.search_form .results .movie .add +.info {
margin-left: 20%;
}
.search_form .results .movie .info h2 {
margin: 0;
}
.search_form .results .movie .info h2 span {
padding: 0 5px;
content: ")";
}
.search_form .results .movie .info h2 span:before { content: "("; }
.search_form .results .movie .info h2 span:after { content: ")"; }

126
couchpotato/static/style/page/settings.css

@ -0,0 +1,126 @@
/* @override http://localhost:5000/static/style/page/settings.css */
.page.settings {
overflow: hidden;
}
.page.settings .tabs {
float: left;
width: 20%;
font-size: 25px;
text-align: right;
list-style: none;
padding: 40px 0;
margin: 0;
min-height: 300px;
}
.page.settings .tabs a {
display: block;
padding: 10px 15px;
border: 1px solid transparent;
position: relative;
z-index: 200;
margin-right: -1px;
}
.page.settings .tabs .active a {
color: black;
background: #fbfbfb;
border-color: #f1f1f1;
border-right-color: transparent;
}
.page.settings .containers {
width: 75.8%;
float: left;
padding: 20px 2%;
min-height: 300px;
background: #fbfbfb;
border-left: 1px solid #f1f1f1;s
}
.page.settings .advanced {
display: none;
color: #ce3b19;
}
.page.settings.show_advanced .advanced { display: block; }
.page.settings .tab_content {
display: none;
}
.page.settings .tab_content.active { display: block; }
.page.settings fieldset {
padding: 10px 0;
}
.page.settings fieldset h2 {
font-weight: normal;
font-size: 25px;
padding: 0 9px 10px;
margin: 0;
border-bottom: 1px solid #f3f3f3;
}
.page.settings fieldset h2 .hint {
color: #888;
font-size: 12px;
margin-left: 10px;
}
.page.settings .ctrlHolder {
line-height: 25px;
padding: 10px;
border-bottom: 1px solid #f3f3f3;
font-size: 14px;
}
.page.settings .ctrlHolder:last-child { border: none; }
.page.settings .ctrlHolder:hover { background: rgba(211,234,254,0.1); }
.page.settings .ctrlHolder.focused:hover { background: rgba(251,246,48,0.29); }
.page.settings .ctrlHolder .formHint {
float: right;
width: 47%;
margin: -18px 0;
padding: 0;
}
.page.settings .ctrlHolder input[type=checkbox] + .formHint {
float: none;
width: auto;
display: inline-block;
margin-left: 1% !important;
color: #222;
}
.page.settings .ctrlHolder label {
font-weight: bold;
width: 20%;
margin: 0;
padding: 6px 0 0;
}
.page.settings input[type=text] {
border: 1px solid #aaa;
padding: 3px;
margin: 0;
width: 30%;
border-radius: 3px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
}
.page.settings .input.xsmall { width: 5% }
.page.settings .input.small { width: 10% }
.page.settings .input.medium { width: 15% }
.page.settings .input.large { width: 25% }
.page.settings .input.xlarge { width: 30% }
.page.settings .advanced_toggle {
clear: both;
display: block;
text-align: right;
height: 20px;
margin: 0;
}
.page.settings .advanced_toggle span { padding: 0 5px; }
.page.settings.show_advanced .advanced_toggle {
color: #ce3b19;
}

19
couchpotato/templates/_desktop.html

@ -5,12 +5,17 @@
<link rel="stylesheet" href="{{ url_for('.static', filename='style/uniform.generic.css') }}">
<link rel="stylesheet" href="{{ url_for('.static', filename='style/uniform.css') }}">
<link rel="stylesheet" href="{{ url_for('.static', filename='style/page/settings.css') }}">
<link rel="stylesheet" href="{{ url_for('.static', filename='style/movie_add.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>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/eventstack.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/eventstack_outerclick.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/block.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/block/navigation.js') }}"></script>
@ -29,13 +34,15 @@
<script type="text/javascript">
window.addEvent('domready', function() {
new Uniform();
Api.setup({
'url': '{{ url_for('api.index') }}',
'path_sep': '{{ sep }}',
'is_remote': false
})
var cp = new CouchPotato({
'base_url': '{{ request.path }}',
'api': {
'url': '{{ url_for('api.index') }}',
'path_sep': '{{ sep }}',
'is_remote': false
}
'base_url': '{{ request.path }}'
});
})
</script>

Loading…
Cancel
Save