diff --git a/couchpotato/api/__init__.py b/couchpotato/api/__init__.py index 33546af..d028119 100644 --- a/couchpotato/api/__init__.py +++ b/couchpotato/api/__init__.py @@ -3,8 +3,8 @@ from flask import Module api = Module(__name__) -def addApiView(route, func): - api.add_url_rule(route + '/', endpoint = route if route else 'index', view_func = func) +def addApiView(route, func, static = False): + api.add_url_rule(route + ('' if static else '/'), endpoint = route if route else 'index', view_func = func) """ Api view """ def index(): diff --git a/couchpotato/core/loader.py b/couchpotato/core/loader.py index aea0508..4fa31e2 100644 --- a/couchpotato/core/loader.py +++ b/couchpotato/core/loader.py @@ -60,7 +60,7 @@ class Loader: options = {} for group in section['groups']: for option in group['options']: - options[option['name']] = option['default'] + options[option['name']] = option.get('default', '') fireEvent('settings.register', section_name = section['name'], options = options, save = save) return True except Exception, e: diff --git a/couchpotato/core/plugins/base.py b/couchpotato/core/plugins/base.py index 0a6ea92..8b4484a 100644 --- a/couchpotato/core/plugins/base.py +++ b/couchpotato/core/plugins/base.py @@ -1,7 +1,27 @@ +from couchpotato.api import addApiView from couchpotato.environment import Env +from flask.helpers import send_from_directory +import os.path class Plugin(): def conf(self, attr): return Env.setting(attr, self.__class__.__name__.lower()) + + def registerStatic(self, file_path): + + class_name = self.__class__.__name__.lower() + self.plugin_file = file_path + path = class_name + '.static/' + + addApiView(path + '', self.showStatic, static = True) + + return path + + def showStatic(self, file = ''): + + plugin_dir = os.path.dirname(self.plugin_file) + dir = os.path.join(plugin_dir, 'static') + + return send_from_directory(dir, file) diff --git a/couchpotato/core/plugins/clientscript/__init__.py b/couchpotato/core/plugins/clientscript/__init__.py new file mode 100644 index 0000000..8490eae --- /dev/null +++ b/couchpotato/core/plugins/clientscript/__init__.py @@ -0,0 +1,6 @@ +from .main import ClientScript + +def start(): + return ClientScript() + +config = [] diff --git a/couchpotato/core/plugins/clientscript/main.py b/couchpotato/core/plugins/clientscript/main.py new file mode 100644 index 0000000..8d9f28d --- /dev/null +++ b/couchpotato/core/plugins/clientscript/main.py @@ -0,0 +1,56 @@ +from couchpotato.core.event import addEvent +from couchpotato.core.logger import CPLog +from couchpotato.core.plugins.base import Plugin + +log = CPLog(__name__) + + +class ClientScript(Plugin): + + urls = { + 'style': {}, + 'script': {}, + } + + html = { + 'style': '', + 'script': '', + } + + def __init__(self): + addEvent('register_style', self.registerStyle) + addEvent('register_script', self.registerScript) + + addEvent('clientscript.get_styles', self.getStyles) + addEvent('clientscript.get_scripts', self.getScripts) + + def getStyles(self, *args, **kwargs): + return self.get('style', *args, **kwargs) + + def getScripts(self, *args, **kwargs): + return self.get('script', *args, **kwargs) + + def get(self, type, as_html = False, location = 'head'): + + data = '' if as_html else [] + + try: + return self.urls[type][location] + except Exception, e: + log.error(e) + + return data + + def registerStyle(self, path, position = 'head'): + self.register(path, 'style', position) + + def registerScript(self, path, position = 'head'): + self.register(path, 'script', position) + + def register(self, file, type, location): + + if not self.urls[type].get(location): + self.urls[type][location] = [] + + filePath = file + self.urls[type][location].append(filePath) diff --git a/couchpotato/core/plugins/extension/__init__.py b/couchpotato/core/plugins/extension/__init__.py new file mode 100644 index 0000000..962254e --- /dev/null +++ b/couchpotato/core/plugins/extension/__init__.py @@ -0,0 +1,6 @@ +from .main import Extension + +def start(): + return Extension() + +config = [] diff --git a/couchpotato/core/plugins/extension/main.py b/couchpotato/core/plugins/extension/main.py new file mode 100644 index 0000000..f961ee8 --- /dev/null +++ b/couchpotato/core/plugins/extension/main.py @@ -0,0 +1,7 @@ +from couchpotato.core.plugins.base import Plugin + + +class Extension(Plugin): + + def __init__(self): + self.registerStatic(__file__) diff --git a/couchpotato/core/plugins/extension/static/couchpotato.user.js b/couchpotato/core/plugins/extension/static/couchpotato.user.js new file mode 100644 index 0000000..305d1f5 --- /dev/null +++ b/couchpotato/core/plugins/extension/static/couchpotato.user.js @@ -0,0 +1,389 @@ +// ==UserScript== +// @name CouchPotato UserScript +// @description Add movies to your CouchPotato via external sites like IMDB +// @include http*://*.imdb.com/title/tt* +// @include http*://imdb.com/title/tt* +// @include ${host}* +// @include http://*.sharethe.tv/movies/* +// @include http://sharethe.tv/movies/* +// @include http://*.moviemeter.nl/film/* +// @include http://moviemeter.nl/film/* +// @include http://whiwa.net/stats/movie/* +// @include http://trailers.apple.com/trailers/* +// @include http://www.themoviedb.org/movie/* +// @include http://www.allocine.fr/film/* +// @include http://trakt.tv/movie/* +// @include http://*.trak.tv/movie/* +// @exclude http://trak.tv/movie/*/* +// @exclude http://*.trak.tv/movie/*/* +// ==/UserScript== + +var version = 7; + +function create() { + switch (arguments.length) { + case 1: + var A = document.createTextNode(arguments[0]); + break; + default: + var A = document.createElement(arguments[0]), B = arguments[1]; + for ( var b in B) { + if (b.indexOf("on") == 0){ + A.addEventListener(b.substring(2), B[b], false); + } + else if (",style,accesskey,id,name,src,href,which".indexOf("," + + b.toLowerCase()) != -1){ + A.setAttribute(b, B[b]); + } + else{ + A[b] = B[b]; + } + } + for ( var i = 2, len = arguments.length; i < len; ++i){ + A.appendChild(arguments[i]); + } + } + return A; +} + +if (typeof GM_addStyle == 'undefined'){ + GM_addStyle = function(css) { + var head = document.getElementsByTagName('head')[0], style = document + .createElement('style'); + if (!head) { + return + } + style.type = 'text/css'; + style.textContent = css; + head.appendChild(style); + } +} + +// Styles +GM_addStyle('\ + #mmPopup { opacity: 0.5; width:200px; font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif; -moz-border-radius-topleft: 6px; -moz-border-radius-topright: 6px; -webkit-border-top-left-radius: 6px; -webkit-border-top-right-radius: 6px; -moz-box-shadow: 0 0 20px rgba(0,0,0,0.5); -webkit-box-shadow: 0 0 20px rgba(0,0,0,0.5); position:fixed; z-index:9999; bottom:0; right:0; font-size:15px; margin: 0 20px; display: block; background:#f5f5f5; } \ + #mmPopup:hover { opacity: 1; } \ + #mmPopup a#addTo { cursor:pointer; text-align:center; text-decoration:none; color: #000; display:block; padding:15px 0 10px; } \ + #mmPopup a#closeBtn { cursor:pointer; float: right; padding:10px; } \ + #mmPopup a img { vertical-align: middle; } \ + #mmPopup a:hover { color:#000; } \ + #mmPopup iframe{ background:#f5f5f5; margin:6px; height:70px; width:188px; overflow:hidden; border:none; } \ +'); + +var cpLocation = '${host}'; +var movieImg = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAZCAYAAABQDyyRAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA+9JREFUeNrMVklIXFkUPVWWY5cDccIpMQ444YCi7UJ3KrpUxAkURRAFW6GdMCI0ooKuxIWCIkrc6FYMcYogrgxoEHFeRFRE42w5D/X73dv1i4pUOiGmkly4/u9779c979x7z3sKSZLwK02JX2y/BYCXwmeESybyGV0Mo6YQNTBzf38f09/fj7GxMRwcHPyQnTk5OSEpKQm5ublQqVTvxdCfXwIg9fT0YGBgAO7u7qipqUFAQACurq7Q29uLoaEhXhgdHY3q6mqo1WocHx+jpaUF8/PzPJeamor8/HwKhKWlJbS2tmJ/f5/nsrKyUFhYSK8vhG8+BmD2j7Dm5mZotVqcnp5ibW0N4eHhcHFxQUREBM7OznhsZ2cHu7u7iI2Nhb29PQOi8b29PaysrECpVCIqKgpubm4IDAzE7OwsLi8vsbW1hYyMDIrVK/yTUQDd3d2oqKjgjygFc3NzCAsLg7OzMyIjI3F+fo7V1VVsbm5ie3sbMTExsLW15acMYmFhAbe3twza1dUVwcHB0Gg0WF9fR15eHsXqNAZA3wUJCQkoKipiGilIQ0MDf2xmZsYUJicn87rp6Wmm+OLigpmglIWEhPDc4OAg+vr6cH19zSwUFBR8tVa4BhITE03aauPj4/QIE75gFMBPanmjAFT05ycxYNRU8svo6CiGh4fR2dkJoQvw8PBAXV0dfHx8cHNzw+MjIyO8Ni4uDpWVlbCxseGibWpqwuLiIs9lZ2cjJycHlpaW3DlTU1N6afhfABMTE+jq6uLgnp6eqK+v5+BU2aQTcvD4+HhUVVXB2toaJycnrAdy8MzMTNYDasnl5WUeIzA6eyWc0GiNdkFbWxvvlIKKzvxs57IYGQYnMWpsbNSLEQWibqHgBIiA2dnZIS0tDbW1taxlwm0o3YYp1zNwd3fHSlheXs4MUO+TElJaZCUsKyuDubk5q9xjJaTd02/ISkgAqR1JCw4PD+XNSiZvQysrKygUClhYWDCrpAX+/v7o6OjQiOkA4RpdGi4/Y+Cp5uDggJKSEj5HiAkCQSmU2T06OlILuadikURqbgXAt+K9khlIT0/nc+ApRqceSe63/FZQUBDa29vp9W9mICUlhU/DJ10slP/Vs6+vLx9gZNRRGxsb3JJeXl76td7e3vrPiIEPYmEEtdrk5CRR9V0AHB0dUVpaitDQUD0gOmGJEV0NUAEeGVxU3gn/CwLAS7qUSCYwUf2SOOSk4uJi+vdYuJtwtfA/6AQgpxR81N1WnIU//4EKbP7w8PBGPJ9REersTHTchaE8G3bBvs6fZHJLiwBW4vakJfr9/Py4JIx+IFNhAqf6em2QkT7hysfr/hVgAIhbr+v/xmSzAAAAAElFTkSuQmCC' +var closeImg = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAA5ElEQVR42tRTQYoEIQwsl/2Bl3gQoY9eBKEf5kvyG8G7h4Z+S38gIu5lp5lZ2R7YPm1BDhZJSFWiGmPgDj5wE7cbfD4/mBkAHprUj9yTTyn9OsGIMSLG+Fxwxc8SiAi9d4QQHskjhIDeO4jorQcq5wwiQmsN3nt479FaAxEh5zxJmyZIKalSClprL1FKQUpJXZr4DBH52xqZeRhjICKw1sJaCxGBMQbMPN41GFpriAicc6i1otYK5xxEBFrraQuThGVZAADbtp2amXms6woAOI7j0gO17/t5MN+HNfEvBf//M30NAKe7aRqUOIlfAAAAAElFTkSuQmCC' + +lib = (function(){ + var _public = {} + _public.osd = function(id, year){ + var navbar, newElement; + + var iFrame = create('iframe', { + src : cpLocation + "movie/imdbAdd/?id=" + id + '&year=' + year, + frameborder : 0, + scrolling : 'no' + }) + + var addToText = '' + var popupId = 'mmPopup' + + var popup = create('div', { + id : popupId, + innerHTML : addToText + }); + var addButton = create('a', { + innerHTML: 'Add to CouchPotato', + id: 'addTo', + onclick: function(){ + popup.innerHTML = ''; + popup.appendChild(create('a', { + innerHTML: '', + id: 'closeBtn', + onclick: function(){ + popup.innerHTML = ''; + popup.appendChild(addButton); + } + })); + popup.appendChild(iFrame); + } + }) + popup.appendChild(addButton); + + document.body.parentNode.insertBefore(popup, document.body); + } + + _public.search = function(name, year){ + + var imdb_id = 'tt1234567'; + + lib.osd(imdb_id, year); + + return imdb_id; + } + + return _public +})(); + +var CouchPotato = {} + +CouchPotato['imdb.com'] = (function(){ + + function isMovie(){ + var series = document.getElementsByTagName('h5') + for (var i = 0; i < series.length; i++) { + if (series[i].innerHTML == 'Seasons:') { + return false; + } + } + return true; + } + + function getId(){ + return 'tt' + location.href.replace(/[^\d+]+/g, ''); + } + + function getYear(){ + try { + return document.getElementsByTagName('h1')[0].getElementsByTagName('a')[0].text; + } catch (e) { + var spans = document.getElementsByTagName('h1')[0].getElementsByTagName('span'); + var pattern = /^\((TV|Video) ([0-9]+)\)$/; + for (var i = 0; i < spans.length; i++) { + if (spans[i].innerHTML.search(pattern)) { + return spans[i].innerHTML.match(pattern)[1]; + } + } + } + } + + var constructor = function(){ + if(isMovie()){ + lib.osd(getId(), getYear()); + } + } + + return constructor; + +})(); + +CouchPotato['sharethe.tv'] = (function(){ + + function isMovie(){ + var pattern = /movies\/[^/]+\/?$/; + matched = location.href.match(pattern); + return null != matched; + } + + function getId(){ + var pattern = /imdb\.com\/title\/tt(\d+)/; + var html = document.getElementsByTagName('html')[0].innerHTML; + var imdb_id = html.match(pattern)[1]; + return imdb_id; + + } + + function getYear(){ + var pattern = /(\d+)[^\d]*$/; + var html = document.getElementsByTagName('html')[0].innerHTML; + var year = html.match(pattern)[1]; + return year; + + } + + function constructor(){ + if(isMovie()){ + lib.osd(getId(), getYear()); + } + } + + return constructor; + +})(); + +CouchPotato['moviemeter.nl'] = (function(){ + + function isMovie(){ + var pattern = /[^/]+\/?$/; + var html = document.getElementsByTagName('h1')[0].innerHTML + matched = location.href.match(pattern); + return null != matched; + } + + function getId(){ + var pattern = /imdb\.com\/title\/tt(\d+)/; + var html = document.getElementsByTagName('html')[0].innerHTML; + var imdb_id = html.match(pattern)[1]; + return imdb_id; + + } + + function getYear(){ + var pattern = /(\d+)[^\d]*$/; + var html = document.getElementsByTagName('h1')[0].innerHTML; + var year = html.match(pattern)[1]; + return year; + + } + + function constructor(){ + if(isMovie()){ + lib.osd(getId(), getYear()); + } + } + + return constructor; + +})(); + +CouchPotato['whiwa.net'] = (function(){ + + function isMovie(){ + var pattern = /[^/]+\/?$/; + var html = document.getElementsByTagName('h3')[0].innerHTML + var matched = location.href.match(pattern); + return null != matched; + } + + function getId(){ + var pattern = /imdb\.com\/title\/tt(\d+)/; + var html = document.getElementsByTagName('html')[0].innerHTML; + var imdb_id = html.match(pattern)[1]; + return imdb_id; + + } + + function getYear(){ + var pattern = /(\d+)[^\d]*$/; + var html = document.getElementsByTagName('h3')[0].innerHTML; + var year = html.match(pattern)[1]; + return year; + + } + + function constructor(){ + if(isMovie()){ + lib.osd(getId(), getYear()); + } + } + + return constructor; + +})(); + +CouchPotato['trakt.tv'] = (function(){ + + var imdb_input = null; + var year_input = null; + + function isMovie(){ + imdb_input = document.getElementById("meta-imdb-id"); + year_input = document.getElementById("meta-year"); + return (null != imdb_input) && (null != year_input); + } + + function getId(){ + return imdb_input.value.substr(2); + } + + function getYear(){ + return year_input.value; + + } + + function constructor(){ + if(isMovie()){ + lib.osd(getId(), getYear()); + } + } + + return constructor; + +})(); + +CouchPotato['trailers.apple.com'] = (function(){ + + function getId() { + var name = document.title.substr(0, document.title.indexOf(" -")).replace(/ /g, "+"); + return lib.search(name, getYear()) + + } + + function getYear(){ + var release_date = document.getElementById("view-showtimes").parentNode.innerHTML; + var year = new RegExp("\\d{4}", release_date) + + return year; + } + + function constructor(){ + getId(); + } + + return constructor; + +})(); + +CouchPotato['themoviedb.org'] = (function(){ + + var obj = this; + + function getId() { + + name = document.title.substr(0, document.title.indexOf("TMDb")-3).replace(/ /g, "+"); + lib.search(name, getYear()) + + } + + function getYear(){ + var year = document.getElementById("year").innerHTML; + year = year.substr(1, year.length-2); + return year; + } + + function constructor(){ + getId(); + } + + return constructor; + +})(); + +CouchPotato['allocine.fr'] = (function(){ + + function isMovie(){ + var pattern = /fichefilm_gen_cfilm=\d+?\.html$/; + matched = location.href.match(pattern); + return null != matched; + } + + function getId() { + var name = document.title.substr(0, document.title.indexOf(" -")).replace(/ /g, "+"); + lib.search(name, getYear()) + } + + function getYear(){ + var year = new RegExp("\\d{4}", document.title) + return year; + } + + function constructor(){ + if(isMovie()){ + lib.osd(getId(), getYear()); + } + } + + return constructor; + +})(); + +// Start +(function(){ + for (var i in CouchPotato){ + GM_log(i); + if(location.href.indexOf(i) != -1){ + new CouchPotato[i](); + break; + } + } +})(); diff --git a/couchpotato/core/plugins/file/main.py b/couchpotato/core/plugins/file/main.py index ccf89d5..4f5c3a1 100644 --- a/couchpotato/core/plugins/file/main.py +++ b/couchpotato/core/plugins/file/main.py @@ -1,6 +1,6 @@ from couchpotato import get_session from couchpotato.api import addApiView -from couchpotato.core.event import addEvent +from couchpotato.core.event import addEvent, fireEvent from couchpotato.core.helpers.variable import md5, getExt from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin @@ -22,6 +22,9 @@ class FileManager(Plugin): addApiView('file.cache/', self.showImage) + path = self.registerStatic(__file__) + fireEvent('register_script', path + 'file.js') + def showImage(self, file = ''): cache_dir = Env.get('cache_dir') diff --git a/couchpotato/static/scripts/file.js b/couchpotato/core/plugins/file/static/file.js similarity index 100% rename from couchpotato/static/scripts/file.js rename to couchpotato/core/plugins/file/static/file.js diff --git a/couchpotato/core/plugins/movie/main.py b/couchpotato/core/plugins/movie/main.py index 59716f9..20ccf16 100644 --- a/couchpotato/core/plugins/movie/main.py +++ b/couchpotato/core/plugins/movie/main.py @@ -19,6 +19,10 @@ class MoviePlugin(Plugin): addApiView('movie.edit', self.edit) addApiView('movie.delete', self.delete) + path = self.registerStatic(__file__) + fireEvent('register_script', path + 'search.js') + fireEvent('register_style', path + 'search.css') + def list(self): params = getParams() diff --git a/couchpotato/static/style/plugin/movie_add.css b/couchpotato/core/plugins/movie/static/search.css similarity index 100% rename from couchpotato/static/style/plugin/movie_add.css rename to couchpotato/core/plugins/movie/static/search.css diff --git a/couchpotato/static/scripts/block/search.js b/couchpotato/core/plugins/movie/static/search.js similarity index 100% rename from couchpotato/static/scripts/block/search.js rename to couchpotato/core/plugins/movie/static/search.js diff --git a/couchpotato/core/plugins/quality/main.py b/couchpotato/core/plugins/quality/main.py index 493353e..df5a2ff 100644 --- a/couchpotato/core/plugins/quality/main.py +++ b/couchpotato/core/plugins/quality/main.py @@ -1,13 +1,14 @@ from couchpotato import get_session -from couchpotato.core.event import addEvent +from couchpotato.core.event import addEvent, fireEvent from couchpotato.core.helpers.encoding import toUnicode from couchpotato.core.logger import CPLog +from couchpotato.core.plugins.base import Plugin from couchpotato.core.settings.model import Quality, Profile, ProfileType - log = CPLog(__name__) -class QualityPlugin: + +class QualityPlugin(Plugin): qualities = [ {'identifier': 'bd50', 'size': (15000, 60000), 'label': 'BR-Disk', 'alternative': ['1080p', 'bd25'], 'allow': [], 'ext':[], 'tags': ['x264', 'h264', 'blu ray']}, @@ -28,6 +29,10 @@ class QualityPlugin: addEvent('quality.all', self.all) addEvent('app.load', self.fill) + path = self.registerStatic(__file__) + fireEvent('register_script', path + 'quality.js') + fireEvent('register_style', path + 'quality.css') + def all(self): db = get_session() diff --git a/couchpotato/static/style/plugin/quality.css b/couchpotato/core/plugins/quality/static/quality.css similarity index 100% rename from couchpotato/static/style/plugin/quality.css rename to couchpotato/core/plugins/quality/static/quality.css diff --git a/couchpotato/static/scripts/quality.js b/couchpotato/core/plugins/quality/static/quality.js similarity index 100% rename from couchpotato/static/scripts/quality.js rename to couchpotato/core/plugins/quality/static/quality.js diff --git a/couchpotato/core/plugins/status/main.py b/couchpotato/core/plugins/status/main.py index 747cecf..c221a1b 100644 --- a/couchpotato/core/plugins/status/main.py +++ b/couchpotato/core/plugins/status/main.py @@ -1,12 +1,14 @@ from couchpotato import get_session -from couchpotato.core.event import addEvent +from couchpotato.core.event import addEvent, fireEvent from couchpotato.core.helpers.encoding import toUnicode from couchpotato.core.logger import CPLog +from couchpotato.core.plugins.base import Plugin from couchpotato.core.settings.model import Status log = CPLog(__name__) -class StatusPlugin: + +class StatusPlugin(Plugin): statuses = { 'active': 'Active', @@ -21,6 +23,9 @@ class StatusPlugin: addEvent('status.all', self.all) addEvent('app.load', self.fill) + path = self.registerStatic(__file__) + fireEvent('register_script', path + 'status.js') + def all(self): db = get_session() diff --git a/couchpotato/static/scripts/status.js b/couchpotato/core/plugins/status/static/status.js similarity index 100% rename from couchpotato/static/scripts/status.js rename to couchpotato/core/plugins/status/static/status.js diff --git a/couchpotato/templates/_desktop.html b/couchpotato/templates/_desktop.html index c9ee8b3..1f9e802 100644 --- a/couchpotato/templates/_desktop.html +++ b/couchpotato/templates/_desktop.html @@ -7,8 +7,6 @@ - - @@ -20,7 +18,6 @@ - @@ -29,9 +26,10 @@ - - - + {% for url in fireEvent('clientscript.get_scripts', as_html = True, single = True) %} + {% endfor %} + {% for url in fireEvent('clientscript.get_styles', as_html = True, single = True) %} + {% endfor %}