From 79cb716cedd7041e0547c0d693ae5a37ecc4d021 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 10:34:52 +0200 Subject: [PATCH 01/16] Update Mootools --- couchpotato/static/scripts/couchpotato.js | 6 +- .../static/scripts/library/mootools.js | 391 +++++++++++------- .../static/scripts/library/mootools_more.js | 141 +++++-- 3 files changed, 353 insertions(+), 185 deletions(-) diff --git a/couchpotato/static/scripts/couchpotato.js b/couchpotato/static/scripts/couchpotato.js index e08e2c3d..b71c1f0d 100644 --- a/couchpotato/static/scripts/couchpotato.js +++ b/couchpotato/static/scripts/couchpotato.js @@ -54,7 +54,7 @@ }, pushState: function(e){ - if((!e.meta && Browser.Platform.mac) || (!e.control && !Browser.Platform.mac)){ + if((!e.meta && Browser.platform.mac) || (!e.control && !Browser.platform.mac)){ (e).preventDefault(); var url = e.target.get('href'); if(History.getPath() != url) @@ -63,7 +63,7 @@ }, isMac: function(){ - return Browser.Platform.mac + return Browser.platform.mac }, createLayout: function(){ @@ -322,7 +322,7 @@ var url = 'http://www.dereferer.org/?' + el.get('href'); - if(el.get('target') == '_blank' || (e.meta && Browser.Platform.mac) || (e.control && !Browser.Platform.mac)) + if(el.get('target') == '_blank' || (e.meta && Browser.platform.mac) || (e.control && !Browser.platform.mac)) window.open(url); else window.location = url; diff --git a/couchpotato/static/scripts/library/mootools.js b/couchpotato/static/scripts/library/mootools.js index 9917ad32..838edee6 100644 --- a/couchpotato/static/scripts/library/mootools.js +++ b/couchpotato/static/scripts/library/mootools.js @@ -20,7 +20,7 @@ description: The heart of MooTools. license: MIT-style license. -copyright: Copyright (c) 2006-2012 [Valerio Proietti](http://mad4milk.net/). +copyright: Copyright (c) 2006-2014 [Valerio Proietti](http://mad4milk.net/). authors: The MooTools production team (http://mootools.net/developers/) @@ -36,8 +36,8 @@ provides: [Core, MooTools, Type, typeOf, instanceOf, Native] (function(){ this.MooTools = { - version: '1.4.5', - build: 'ab8ea8824dc3b24b6666867a2c4ed58ebb762cf0' + version: '1.5.0', + build: '0f7b690afee9349b15909f33016a25d2e4d9f4e3' }; // typeOf, instanceOf @@ -50,7 +50,7 @@ var typeOf = this.typeOf = function(item){ if (item.nodeType == 1) return 'element'; if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace'; } else if (typeof item.length == 'number'){ - if (item.callee) return 'arguments'; + if ('callee' in item) return 'arguments'; if ('item' in item) return 'collection'; } @@ -267,7 +267,7 @@ var force = function(name, object, methods){ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){ fn.call(prototype, prototype[methods[i]], methods[i]); } - for (var key in prototype) fn.call(prototype, prototype[key], key) + for (var key in prototype) fn.call(prototype, prototype[key], key); }; } @@ -275,7 +275,7 @@ var force = function(name, object, methods){ }; force('String', String, [ - 'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search', + 'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search', 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase' ])('Array', Array, [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', @@ -325,11 +325,13 @@ Object.each = Object.forEach; Array.implement({ + /**/ forEach: function(fn, bind){ for (var i = 0, l = this.length; i < l; i++){ if (i in this) fn.call(bind, this[i], i, this); } }, + /**/ each: function(fn, bind){ Array.forEach(this, fn, bind); @@ -421,7 +423,7 @@ description: Contains Array Prototypes like each, contains, and erase. license: MIT-style license. -requires: Type +requires: [Type] provides: Array @@ -564,7 +566,7 @@ Array.implement({ if (this.length != 3) return null; var rgb = this.map(function(value){ if (value.length == 1) value += value; - return value.toInt(16); + return parseInt(value, 16); }); return (array) ? rgb : 'rgb(' + rgb + ')'; }, @@ -594,7 +596,7 @@ description: Contains String Prototypes like camelCase, capitalize, test, and to license: MIT-style license. -requires: Type +requires: [Type, Array] provides: String @@ -603,14 +605,16 @@ provides: String String.implement({ + // + contains: function(string, index){ + return (index ? String(this).slice(index) : String(this)).indexOf(string) > -1; + }, + // + test: function(regex, params){ return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this); }, - contains: function(string, separator){ - return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : String(this).indexOf(string) > -1; - }, - trim: function(){ return String(this).replace(/^\s+|\s+$/g, ''); }, @@ -669,6 +673,8 @@ String.implement({ }); + + /* --- @@ -1063,37 +1069,47 @@ provides: [Browser, Window, Document] var document = this.document; var window = document.window = this; -var ua = navigator.userAgent.toLowerCase(), - platform = navigator.platform.toLowerCase(), - UA = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0], - mode = UA[1] == 'ie' && document.documentMode; +var parse = function(ua, platform){ + ua = ua.toLowerCase(); + platform = (platform ? platform.toLowerCase() : ''); -var Browser = this.Browser = { + var UA = ua.match(/(opera|ie|firefox|chrome|trident|crios|version)[\s\/:]([\w\d\.]+)?.*?(safari|(?:rv[\s\/:]|version[\s\/:])([\w\d\.]+)|$)/) || [null, 'unknown', 0]; - extend: Function.prototype.extend, + if (UA[1] == 'trident'){ + UA[1] = 'ie'; + if (UA[4]) UA[2] = UA[4]; + } else if (UA[1] == 'crios') { + UA[1] = 'chrome'; + } - name: (UA[1] == 'version') ? UA[3] : UA[1], + var platform = ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0]; + if (platform == 'win') platform = 'windows'; - version: mode || parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]), + return { + extend: Function.prototype.extend, + name: (UA[1] == 'version') ? UA[3] : UA[1], + version: parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]), + platform: platform + }; +}; - Platform: { - name: ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0] - }, +var Browser = this.Browser = parse(navigator.userAgent, navigator.platform); +if (Browser.ie){ + Browser.version = document.documentMode; +} + +Browser.extend({ Features: { xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector), json: !!(window.JSON) }, + parseUA: parse +}); - Plugins: {} -}; - -Browser[Browser.name] = true; -Browser[Browser.name + parseInt(Browser.version, 10)] = true; -Browser.Platform[Browser.Platform.name] = true; // Request @@ -1126,18 +1142,7 @@ Browser.Request = (function(){ Browser.Features.xhr = !!(Browser.Request); -// Flash detection -var version = (Function.attempt(function(){ - return navigator.plugins['Shockwave Flash'].description; -}, function(){ - return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); -}) || '0 r0').match(/\d+/g); - -Browser.Plugins.Flash = { - version: Number(version[0] || '0.' + version[1]) || 0, - build: Number(version[2]) || 0 -}; // String scripts @@ -1756,7 +1761,7 @@ local.setDocument = function(document){ // native matchesSelector function - features.nativeMatchesSelector = root.matchesSelector || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector; + features.nativeMatchesSelector = root.matches || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector; if (features.nativeMatchesSelector) try { // if matchesSelector trows errors on incorrect sintaxes we can use it features.nativeMatchesSelector.call(root, ':slick'); @@ -2590,12 +2595,12 @@ license: MIT-style license. requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder] -provides: [Element, Elements, $, $$, Iframe, Selectors] +provides: [Element, Elements, $, $$, IFrame, Selectors] ... */ -var Element = function(tag, props){ +var Element = this.Element = function(tag, props){ var konstructor = Element.Constructors[tag]; if (konstructor) return konstructor(props); if (typeof tag != 'string') return document.id(tag).set(props); @@ -2779,7 +2784,7 @@ Array.mirror(Elements); /**/ var createElementAcceptsHTML; try { - createElementAcceptsHTML = (document.createElement('').name == 'x'); + createElementAcceptsHTML = (document.createElement('').name == 'x'); } catch (e){} var escapeQuotes = function(html){ @@ -3112,7 +3117,28 @@ var pollutesGetAttribute = (function(div){ return (div.getAttribute('random') == 'attribute'); })(document.createElement('div')); -/* */ +var hasCloneBug = (function(test){ + test.innerHTML = ''; + return test.cloneNode(true).firstChild.childNodes.length != 1; +})(document.createElement('div')); +/* */ + +var hasClassList = !!document.createElement('div').classList; + +var classes = function(className){ + var classNames = (className || '').clean().split(" "), uniques = {}; + return classNames.filter(function(className){ + if (className !== "" && !uniques[className]) return uniques[className] = className; + }); +}; + +var addToClassList = function(name){ + this.classList.add(name); +}; + +var removeFromClassList = function(name){ + this.classList.remove(name); +}; Element.implement({ @@ -3122,7 +3148,8 @@ Element.implement({ setter(this, value); } else { /* */ - if (pollutesGetAttribute) var attributeWhiteList = this.retrieve('$attributeWhiteList', {}); + var attributeWhiteList; + if (pollutesGetAttribute) attributeWhiteList = this.retrieve('$attributeWhiteList', {}); /* */ if (value == null){ @@ -3194,17 +3221,27 @@ Element.implement({ return this; }, - hasClass: function(className){ + hasClass: hasClassList ? function(className){ + return this.classList.contains(className); + } : function(className){ return this.className.clean().contains(className, ' '); }, - addClass: function(className){ - if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean(); + addClass: hasClassList ? function(className){ + classes(className).forEach(addToClassList, this); + return this; + } : function(className){ + this.className = classes(className + ' ' + this.className).join(' '); return this; }, - removeClass: function(className){ - this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1'); + removeClass: hasClassList ? function(className){ + classes(className).forEach(removeFromClassList, this); + return this; + } : function(className){ + var classNames = classes(this.className); + classes(className).forEach(classNames.erase, classNames); + this.className = classNames.join(' '); return this; }, @@ -3279,6 +3316,37 @@ Element.implement({ }); + +// appendHTML + +var appendInserters = { + before: 'beforeBegin', + after: 'afterEnd', + bottom: 'beforeEnd', + top: 'afterBegin', + inside: 'beforeEnd' +}; + +Element.implement('appendHTML', ('insertAdjacentHTML' in document.createElement('div')) ? function(html, where){ + this.insertAdjacentHTML(appendInserters[where || 'bottom'], html); + return this; +} : function(html, where){ + var temp = new Element('div', {html: html}), + children = temp.childNodes, + fragment = temp.firstChild; + + if (!fragment) return this; + if (children.length > 1){ + fragment = document.createDocumentFragment(); + for (var i = 0, l = children.length; i < l; i++){ + fragment.appendChild(children[i]); + } + } + + inserters[where || 'bottom'](fragment, this); + return this; +}); + var collected = {}, storage = {}; var get = function(uid){ @@ -3344,7 +3412,7 @@ Element.implement({ } /**/ - if (Browser.ie){ + if (hasCloneBug){ var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object'); for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML; } @@ -3357,13 +3425,7 @@ Element.implement({ [Element, Window, Document].invoke('implement', { addListener: function(type, fn){ - if (type == 'unload'){ - var old = fn, self = this; - fn = function(){ - self.removeListener('unload', fn); - old(); - }; - } else { + if (window.attachEvent && !window.addEventListener){ collected[Slick.uidOf(this)] = this; } if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]); @@ -3398,10 +3460,14 @@ Element.implement({ }); /**/ -if (window.attachEvent && !window.addEventListener) window.addListener('unload', function(){ - Object.each(collected, clean); - if (window.CollectGarbage) CollectGarbage(); -}); +if (window.attachEvent && !window.addEventListener){ + var gc = function(){ + Object.each(collected, clean); + if (window.CollectGarbage) CollectGarbage(); + window.removeListener('unload', gc); + } + window.addListener('unload', gc); +} /**/ Element.Properties = {}; @@ -3446,11 +3512,13 @@ Element.Properties.html = { }; +var supportsHTML5Elements = true, supportsTableInnerHTML = true, supportsTRInnerHTML = true; + /**/ // technique by jdbarlett - http://jdbartlett.com/innershiv/ var div = document.createElement('div'); div.innerHTML = ''; -var supportsHTML5Elements = (div.childNodes.length == 1); +supportsHTML5Elements = (div.childNodes.length == 1); if (!supportsHTML5Elements){ var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' '), fragment = document.createDocumentFragment(), l = tags.length; @@ -3460,7 +3528,7 @@ div = null; /**/ /**/ -var supportsTableInnerHTML = Function.attempt(function(){ +supportsTableInnerHTML = Function.attempt(function(){ var table = document.createElement('table'); table.innerHTML = ''; return true; @@ -3469,7 +3537,7 @@ var supportsTableInnerHTML = Function.attempt(function(){ /**/ var tr = document.createElement('tr'), html = ''; tr.innerHTML = html; -var supportsTRInnerHTML = (tr.innerHTML == html); +supportsTRInnerHTML = (tr.innerHTML == html); tr = null; /**/ @@ -3514,11 +3582,12 @@ if (testForm.firstChild.value != 's') Element.Properties.value = { var tag = this.get('tag'); if (tag != 'select') return this.setProperty('value', value); var options = this.getElements('option'); + value = String(value); for (var i = 0; i < options.length; i++){ var option = options[i], attr = option.getAttributeNode('value'), optionValue = (attr && attr.specified) ? option.value : option.get('text'); - if (optionValue == value) return option.selected = true; + if (optionValue === value) return option.selected = true; } }, @@ -3572,17 +3641,24 @@ provides: Element.Style (function(){ -var html = document.html; +var html = document.html, el; // // Check for oldIE, which does not remove styles when they're set to null -var el = document.createElement('div'); +el = document.createElement('div'); el.style.color = 'red'; el.style.color = null; var doesNotRemoveStyles = el.style.color == 'red'; + +// check for oldIE, which returns border* shorthand styles in the wrong order (color-width-style instead of width-style-color) +var border = '1px solid #123abc'; +el.style.border = border; +var returnsBordersInWrongOrder = el.style.border != border; el = null; // +var hasGetComputedStyle = !!window.getComputedStyle; + Element.Properties.styles = {set: function(styles){ this.setStyles(styles); }}; @@ -3596,16 +3672,25 @@ var setVisibility = function(element, opacity){ element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden'; }; +// +var setFilter = function(element, regexp, value){ + var style = element.style, + filter = style.filter || element.getComputedStyle('filter') || ''; + style.filter = (regexp.test(filter) ? filter.replace(regexp, value) : filter + ' ' + value).trim(); + if (!style.filter) style.removeAttribute('filter'); +}; +// + var setOpacity = (hasOpacity ? function(element, opacity){ element.style.opacity = opacity; } : (hasFilter ? function(element, opacity){ - var style = element.style; - if (!element.currentStyle || !element.currentStyle.hasLayout) style.zoom = 1; - if (opacity == null || opacity == 1) opacity = ''; - else opacity = 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')'; - var filter = style.filter || element.getComputedStyle('filter') || ''; - style.filter = reAlpha.test(filter) ? filter.replace(reAlpha, opacity) : filter + opacity; - if (!style.filter) style.removeAttribute('filter'); + if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1; + if (opacity == null || opacity == 1){ + setFilter(element, reAlpha, ''); + if (opacity == 1 && getOpacity(element) != 1) setFilter(element, reAlpha, 'alpha(opacity=100)'); + } else { + setFilter(element, reAlpha, 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')'); + } } : setVisibility)); var getOpacity = (hasOpacity ? function(element){ @@ -3622,15 +3707,27 @@ var getOpacity = (hasOpacity ? function(element){ return opacity; })); -var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat'; +var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat', + namedPositions = {left: '0%', top: '0%', center: '50%', right: '100%', bottom: '100%'}, + hasBackgroundPositionXY = (html.style.backgroundPositionX != null); + +// +var removeStyle = function(style, property){ + if (property == 'backgroundPosition'){ + style.removeAttribute(property + 'X'); + property += 'Y'; + } + style.removeAttribute(property); +}; +// Element.implement({ getComputedStyle: function(property){ - if (this.currentStyle) return this.currentStyle[property.camelCase()]; + if (!hasGetComputedStyle && this.currentStyle) return this.currentStyle[property.camelCase()]; var defaultView = Element.getDocument(this).defaultView, computed = defaultView ? defaultView.getComputedStyle(this, null) : null; - return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : null; + return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : ''; }, setStyle: function(property, value){ @@ -3652,7 +3749,7 @@ Element.implement({ this.style[property] = value; // if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){ - this.style.removeAttribute(property); + removeStyle(this.style, property); } // return this; @@ -3663,20 +3760,25 @@ Element.implement({ property = (property == 'float' ? floatName : property).camelCase(); var result = this.style[property]; if (!result || property == 'zIndex'){ - result = []; - for (var style in Element.ShortStyles){ - if (property != style) continue; - for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s)); + if (Element.ShortStyles.hasOwnProperty(property)){ + result = []; + for (var s in Element.ShortStyles[property]) result.push(this.getStyle(s)); return result.join(' '); } result = this.getComputedStyle(property); } + if (hasBackgroundPositionXY && /^backgroundPosition[XY]?$/.test(property)){ + return result.replace(/(top|right|bottom|left)/g, function(position){ + return namedPositions[position]; + }) || '0px'; + } + if (!result && property == 'backgroundPosition') return '0px 0px'; if (result){ result = String(result); var color = result.match(/rgba?\([\d\s,]+\)/); if (color) result = result.replace(color[0], color[0].rgbToHex()); } - if (Browser.opera || Browser.ie){ + if (!hasGetComputedStyle && !this.style[property]){ if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){ var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0; values.each(function(value){ @@ -3684,10 +3786,15 @@ Element.implement({ }, this); return this['offset' + property.capitalize()] - size + 'px'; } - if (Browser.ie && (/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){ + if ((/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){ return '0px'; } } + // + if (returnsBordersInWrongOrder && /^border(Top|Right|Bottom|Left)?$/.test(property) && /^#/.test(result)){ + return result.replace(/^(.+)\s(.+)\s(.+)$/, '$2 $3 $1'); + } + // return result; }, @@ -3709,7 +3816,7 @@ Element.implement({ Element.Styles = { left: '@px', top: '@px', bottom: '@px', right: '@px', width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px', - backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)', + backgroundColor: 'rgb(@, @, @)', backgroundSize: '@px', backgroundPosition: '@px @px', color: 'rgb(@, @, @)', fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)', margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)', borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)', @@ -3738,6 +3845,7 @@ Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, bor Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)'; }); +if (hasBackgroundPositionXY) Element.ShortStyles.backgroundPosition = {backgroundPositionX: '@', backgroundPositionY: '@'}; })(); @@ -3779,7 +3887,7 @@ var DOMEvent = this.DOMEvent = new Type('DOMEvent', function(event, win){ if (type.indexOf('key') == 0){ var code = this.code = (event.which || event.keyCode); this.key = _keys[code]; - if (type == 'keydown'){ + if (type == 'keydown' || type == 'keyup'){ if (code > 111 && code < 124) this.key = 'f' + (code - 111); else if (code > 95 && code < 106) this.key = code - 96; } @@ -4001,23 +4109,27 @@ Element.NativeEvents = { gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window + hashchange: 1, popstate: 2, // history error: 1, abort: 1, scroll: 1 //misc }; -Element.Events = {mousewheel: { - base: (Browser.firefox) ? 'DOMMouseScroll' : 'mousewheel' -}}; +Element.Events = { + mousewheel: { + base: 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll' + } +}; + +var check = function(event){ + var related = event.relatedTarget; + if (related == null) return true; + if (!related) return false; + return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related)); +}; if ('onmouseenter' in document.documentElement){ Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2; + Element.MouseenterCheck = check; } else { - var check = function(event){ - var related = event.relatedTarget; - if (related == null) return true; - if (!related) return false; - return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related)); - }; - Element.Events.mouseenter = { base: 'mouseover', condition: check @@ -4035,12 +4147,12 @@ if (!window.addEventListener){ Element.Events.change = { base: function(){ var type = this.type; - return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change' + return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change'; }, condition: function(event){ - return this.type != 'radio' || (event.event.propertyName == 'checked' && this.checked); + return event.type != 'propertychange' || event.event.propertyName == 'checked'; } - } + }; } /**/ @@ -4080,10 +4192,12 @@ var bubbleUp = function(self, match, fn, event, target){ var map = { mouseenter: { - base: 'mouseover' + base: 'mouseover', + condition: Element.MouseenterCheck }, mouseleave: { - base: 'mouseout' + base: 'mouseout', + condition: Element.MouseenterCheck }, focus: { base: 'focus' + (eventListenerSupport ? '' : 'in'), @@ -4190,8 +4304,8 @@ var delegation = { }; var elementEvent = Element.Events[_type]; - if (elementEvent && elementEvent.condition){ - var __match = match, condition = elementEvent.condition; + if (_map.condition || elementEvent && elementEvent.condition){ + var __match = match, condition = _map.condition || elementEvent.condition; match = function(target, event){ return __match(target, event) && condition.call(target, event, type); }; @@ -4226,7 +4340,7 @@ var delegation = { if (_map.remove) _map.remove(this, _uid); delete stored[_uid]; storage[_type] = stored; - return removeEvent.call(this, type, delegator); + return removeEvent.call(this, type, delegator, _map.capture); } var __uid, s; @@ -4344,7 +4458,9 @@ Element.implement({ }, getOffsets: function(){ - if (this.getBoundingClientRect && !Browser.Platform.ios){ + var hasGetBoundingClientRect = this.getBoundingClientRect; + + if (hasGetBoundingClientRect){ var bound = this.getBoundingClientRect(), html = document.id(this.getDocument().documentElement), htmlScroll = html.getScroll(), @@ -4364,27 +4480,9 @@ Element.implement({ position.x += element.offsetLeft; position.y += element.offsetTop; - if (Browser.firefox){ - if (!borderBox(element)){ - position.x += leftBorder(element); - position.y += topBorder(element); - } - var parent = element.parentNode; - if (parent && styleString(parent, 'overflow') != 'visible'){ - position.x += leftBorder(parent); - position.y += topBorder(parent); - } - } else if (element != this && Browser.safari){ - position.x += leftBorder(element); - position.y += topBorder(element); - } - element = element.offsetParent; } - if (Browser.firefox && !borderBox(this)){ - position.x -= leftBorder(this); - position.y -= topBorder(this); - } + return position; }, @@ -4666,13 +4764,17 @@ var Fx = this.Fx = new Class({ }, resume: function(){ - if ((this.frame < this.frames) && !this.isRunning()) pushInstance.call(this, this.options.fps); + if (this.isPaused()) pushInstance.call(this, this.options.fps); return this; }, isRunning: function(){ var list = instances[this.options.fps]; return list && list.contains(this); + }, + + isPaused: function(){ + return (this.frame < this.frames) && !this.isRunning(); } }); @@ -4745,7 +4847,7 @@ Fx.CSS = new Class({ from = element.getStyle(property); var unit = this.options.unit; // adapted from: https://github.com/ryanmorr/fx/blob/master/fx.js#L299 - if (unit && from.slice(-unit.length) != unit && parseFloat(from) != 0){ + if (unit && from && typeof from == 'string' && from.slice(-unit.length) != unit && parseFloat(from) != 0){ element.setStyle(property, to + unit); var value = element.getComputedStyle(property); // IE and Opera support pixelLeft or pixelWidth @@ -4817,11 +4919,13 @@ Fx.CSS = new Class({ search: function(selector){ if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector]; var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$'); - Array.each(document.styleSheets, function(sheet, j){ - var href = sheet.href; - if (href && href.contains('://') && !href.contains(document.domain)) return; - var rules = sheet.rules || sheet.cssRules; + + var searchStyles = function(rules){ Array.each(rules, function(rule, i){ + if (rule.media){ + searchStyles(rule.rules || rule.cssRules); + return; + } if (!rule.style) return; var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){ return m.toLowerCase(); @@ -4833,6 +4937,13 @@ Fx.CSS = new Class({ to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value; }); }); + }; + + Array.each(document.styleSheets, function(sheet, j){ + var href = sheet.href; + if (href && href.indexOf('://') > -1 && href.indexOf(document.domain) == -1) return; + var rules = sheet.rules || sheet.cssRules; + searchStyles(rules); }); return Fx.CSS.Cache[selector] = to; } @@ -5369,10 +5480,10 @@ var Request = this.Request = new Class({ if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition); if (this.options.noCache) - url += (url.contains('?') ? '&' : '?') + String.uniqueID(); + url += (url.indexOf('?') > -1 ? '&' : '?') + String.uniqueID(); - if (data && method == 'get'){ - url += (url.contains('?') ? '&' : '?') + data; + if (data && (method == 'get' || method == 'delete')){ + url += (url.indexOf('?') > -1 ? '&' : '?') + data; data = null; } @@ -5526,10 +5637,14 @@ JSON.encode = JSON.stringify ? function(obj){ return null; }; +JSON.secure = true; + + JSON.decode = function(string, secure){ if (!string || typeOf(string) != 'string') return null; - - if (secure || JSON.secure){ + + if (secure == null) secure = JSON.secure; + if (secure){ if (JSON.parse) return JSON.parse(string); if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.'); } diff --git a/couchpotato/static/scripts/library/mootools_more.js b/couchpotato/static/scripts/library/mootools_more.js index 77b3a7a1..2ff49cb3 100644 --- a/couchpotato/static/scripts/library/mootools_more.js +++ b/couchpotato/static/scripts/library/mootools_more.js @@ -1,6 +1,16 @@ -// MooTools: the javascript framework. -// Load this file's selection again by visiting: http://mootools.net/more/0f75cfbac1aabbedaba7630beef8d10c -// Or build this file again with packager using: packager build More/Events.Pseudos More/Date More/Date.Extras More/Element.Forms More/Element.Position More/Element.Shortcuts More/Fx.Scroll More/Fx.Slide More/Sortables More/Request.JSONP More/Request.Periodical +/* +--- +MooTools: the javascript framework + +web build: + - http://mootools.net/more/0f75cfbac1aabbedaba7630beef8d10c + +packager build: + - packager build More/Events.Pseudos More/Date More/Date.Extras More/Element.Forms More/Element.Position More/Element.Shortcuts More/Fx.Scroll More/Fx.Slide More/Sortables More/Request.JSONP More/Request.Periodical + +... +*/ + /* --- @@ -31,8 +41,8 @@ provides: [MooTools.More] */ MooTools.More = { - 'version': '1.4.0.1', - 'build': 'a4244edf2aa97ac8a196fc96082dd35af1abab87' + version: '1.5.0', + build: '73db5e24e6e9c5c87b3a27aebef2248053f7db37' }; @@ -48,7 +58,7 @@ license: MIT-style license authors: - Arian Stolwijk -requires: [Core/Class.Extras, Core/Slick.Parser, More/MooTools.More] +requires: [Core/Class.Extras, Core/Slick.Parser, MooTools.More] provides: [Events.Pseudos] @@ -211,7 +221,7 @@ authors: requires: - Core/Object - - /MooTools.More + - MooTools.More provides: [Object.Extras] @@ -280,8 +290,8 @@ authors: requires: - Core/Events - - /Object.Extras - - /MooTools.More + - Object.Extras + - MooTools.More provides: [Locale, Lang] @@ -444,7 +454,7 @@ authors: - Aaron Newton requires: - - /Locale + - Locale provides: [Locale.en-US.Date] @@ -1079,7 +1089,7 @@ authors: - Scott Kyle requires: - - /Date + - Date provides: [Date.Extras] @@ -1235,10 +1245,8 @@ var special = { 'S': /[ŠŞŚ]/g, 't': /[ťţ]/g, 'T': /[ŤŢ]/g, - 'ue': /[ü]/g, - 'UE': /[Ü]/g, - 'u': /[ùúûůµ]/g, - 'U': /[ÙÚÛŮ]/g, + 'u': /[ùúûůüµ]/g, + 'U': /[ÙÚÛŮÜ]/g, 'y': /[ÿý]/g, 'Y': /[ŸÝ]/g, 'z': /[žźż]/g, @@ -1263,7 +1271,16 @@ tidy = { '-': /[\u2013]/g, // '--': /[\u2014]/g, '»': /[\uFFFD]/g -}; +}, + +conversions = { + ms: 1, + s: 1000, + m: 6e4, + h: 36e5 +}, + +findUnits = /(\d*.?\d+)([msh]+)/; var walk = function(string, replacements){ var result = string, key; @@ -1325,6 +1342,13 @@ String.implement({ if (trail) string += trail; } return string; + }, + + ms: function(){ + // "Borrowed" from https://gist.github.com/1503944 + var units = findUnits.exec(this); + if (units == null) return Number(this); + return Number(units[1]) * conversions[units[2]]; } }); @@ -1348,8 +1372,8 @@ authors: requires: - Core/Element - - /String.Extras - - /MooTools.More + - String.Extras + - MooTools.More provides: [Element.Forms] @@ -1493,7 +1517,7 @@ authors: requires: - Core/Element.Style - Core/Element.Dimensions - - /MooTools.More + - MooTools.More provides: [Element.Measure] @@ -1710,13 +1734,15 @@ var local = Element.Position = { }, setOffsetOption: function(element, options){ - var parentOffset = {x: 0, y: 0}, - offsetParent = element.measure(function(){ - return document.id(this.getOffsetParent()); - }), - parentScroll = offsetParent.getScroll(); + var parentOffset = {x: 0, y: 0}; + var parentScroll = {x: 0, y: 0}; + var offsetParent = element.measure(function(){ + return document.id(this.getOffsetParent()); + }); if (!offsetParent || offsetParent == element.getDocument().body) return; + + parentScroll = offsetParent.getScroll(); parentOffset = offsetParent.measure(function(){ var position = this.getPosition(); if (this.getStyle('position') == 'fixed'){ @@ -1896,7 +1922,7 @@ authors: requires: - Core/Element.Style - - /MooTools.More + - MooTools.More provides: [Element.Shortcuts] @@ -1976,7 +2002,7 @@ requires: - Core/Fx - Core/Element.Event - Core/Element.Dimensions - - /MooTools.More + - MooTools.More provides: [Fx.Scroll] @@ -2014,7 +2040,6 @@ Fx.Scroll = new Class({ set: function(){ var now = Array.flatten(arguments); - if (Browser.firefox) now = [Math.round(now[0]), Math.round(now[1])]; // not needed anymore in newer firefox versions this.element.scrollTo(now[0], now[1]); return this; }, @@ -2148,7 +2173,7 @@ authors: requires: - Core/Fx - Core/Element.Style - - /MooTools.More + - MooTools.More provides: [Fx.Slide] @@ -2325,7 +2350,7 @@ requires: - Core/Element.Event - Core/Element.Style - Core/Element.Dimensions - - /MooTools.More + - MooTools.More provides: [Drag] ... @@ -2371,10 +2396,10 @@ var Drag = new Class({ this.mouse = {'now': {}, 'pos': {}}; this.value = {'start': {}, 'now': {}}; - this.selection = (Browser.ie) ? 'selectstart' : 'mousedown'; + this.selection = 'selectstart' in document ? 'selectstart' : 'mousedown'; - if (Browser.ie && !Drag.ondragstartFixed){ + if ('ondragstart' in document && !('FileReader' in window) && !Drag.ondragstartFixed){ document.ondragstart = Function.from(false); Drag.ondragstartFixed = true; } @@ -2559,7 +2584,7 @@ authors: requires: - Core/Element.Dimensions - - /Drag + - Drag provides: [Drag.Move] @@ -2586,10 +2611,7 @@ Drag.Move = new Class({ element = this.element; this.droppables = $$(this.options.droppables); - this.container = document.id(this.options.container); - - if (this.container && typeOf(this.container) != 'element') - this.container = document.id(this.container.getDocument().body); + this.setContainer(this.options.container); if (this.options.style){ if (this.options.modifiers.x == 'left' && this.options.modifiers.y == 'top'){ @@ -2606,6 +2628,13 @@ Drag.Move = new Class({ this.addEvent('start', this.checkDroppables, true); this.overed = null; }, + + setContainer: function(container) { + this.container = document.id(container); + if (this.container && typeOf(this.container) != 'element'){ + this.container = document.id(this.container.getDocument().body); + } + }, start: function(event){ if (this.container) this.options.limit = this.calculateLimit(); @@ -2670,7 +2699,9 @@ Drag.Move = new Class({ if (container != offsetParent){ left += containerMargin.left + offsetParentPadding.left; - top += ((Browser.ie6 || Browser.ie7) ? 0 : containerMargin.top) + offsetParentPadding.top; + if (!offsetParentPadding.left && left < 0) left = 0; + top += offsetParent == document.body ? 0 : containerMargin.top + offsetParentPadding.top; + if (!offsetParentPadding.top && top < 0) top = 0; } } else { left -= elementMargin.left; @@ -2754,7 +2785,7 @@ authors: requires: - Core/Fx.Morph - - /Drag.Move + - Drag.Move provides: [Sortables] @@ -2773,7 +2804,8 @@ var Sortables = new Class({ clone: false, revert: false, handle: false, - dragOptions: {} + dragOptions: {}, + unDraggableTags: ['button', 'input', 'a', 'textarea', 'select', 'option'] }, initialize: function(lists, options){ @@ -2839,6 +2871,24 @@ var Sortables = new Class({ return list; }, this)); }, + + getDroppableCoordinates: function (element){ + var offsetParent = element.getOffsetParent(); + var position = element.getPosition(offsetParent); + var scroll = { + w: window.getScroll(), + offsetParent: offsetParent.getScroll() + }; + position.x += scroll.offsetParent.x; + position.y += scroll.offsetParent.y; + + if (offsetParent.getStyle('position') == 'fixed'){ + position.x -= scroll.w.x; + position.y -= scroll.w.y; + } + + return position; + }, getClone: function(event, element){ if (!this.options.clone) return new Element(element.tagName).inject(document.body); @@ -2859,7 +2909,7 @@ var Sortables = new Class({ }); } - return clone.inject(this.list).setPosition(element.getPosition(element.getOffsetParent())); + return clone.inject(this.list).setPosition(this.getDroppableCoordinates(this.element)); }, getDroppables: function(){ @@ -2884,7 +2934,7 @@ var Sortables = new Class({ if ( !this.idle || event.rightClick || - ['button', 'input', 'a', 'textarea'].contains(event.target.get('tag')) + (!this.options.handle && this.options.unDraggableTags.contains(event.target.get('tag'))) ) return; this.idle = false; @@ -2915,14 +2965,16 @@ var Sortables = new Class({ end: function(){ this.drag.detach(); this.element.setStyle('opacity', this.opacity); + var self = this; if (this.effect){ var dim = this.element.getStyles('width', 'height'), clone = this.clone, - pos = clone.computePosition(this.element.getPosition(this.clone.getOffsetParent())); + pos = clone.computePosition(this.getDroppableCoordinates(clone)); var destroy = function(){ this.removeEvent('cancel', destroy); clone.destroy(); + self.reset(); }; this.effect.element = clone; @@ -2935,8 +2987,9 @@ var Sortables = new Class({ }).addEvent('cancel', destroy).chain(destroy); } else { this.clone.destroy(); + self.reset(); } - this.reset(); + }, reset: function(){ @@ -3125,7 +3178,7 @@ authors: requires: - Core/Request - - /MooTools.More + - MooTools.More provides: [Request.Periodical] From 876c60271082c2f701882db6108934a66ab1e104 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 12:29:31 +0200 Subject: [PATCH 02/16] Code cleanup --- couchpotato/__init__.py | 2 +- couchpotato/core/_base/downloader/main.py | 3 +++ .../_base/downloader/static/downloaders.js | 5 +++-- couchpotato/core/_base/updater/main.py | 2 +- couchpotato/core/downloaders/nzbvortex.py | 4 ++-- couchpotato/core/downloaders/sabnzbd.py | 2 +- couchpotato/core/downloaders/synology.py | 1 + couchpotato/core/downloaders/utorrent.py | 2 +- couchpotato/core/media/__init__.py | 2 +- couchpotato/core/media/_base/media/main.py | 1 - .../core/media/_base/providers/base.py | 3 +++ .../core/media/_base/providers/nzb/newznab.py | 2 +- .../_base/providers/torrent/ilovetorrents.py | 1 + .../_base/providers/torrent/torrentday.py | 1 - .../core/media/_base/search/static/search.css | 1 - couchpotato/core/media/movie/charts/main.py | 1 + .../core/media/movie/charts/static/charts.css | 1 - .../media/movie/providers/info/fanarttv.py | 17 +++++--------- .../media/movie/providers/info/themoviedb.py | 2 +- .../movie/providers/torrent/sceneaccess.py | 1 - .../media/movie/providers/trailer/base.py | 3 +++ .../core/media/movie/suggestion/main.py | 1 - couchpotato/core/plugins/base.py | 2 +- couchpotato/core/plugins/file.py | 4 ++-- couchpotato/core/plugins/log/main.py | 4 ++-- couchpotato/core/plugins/log/static/log.css | 2 +- couchpotato/core/plugins/renamer.py | 2 +- .../core/plugins/wizard/static/wizard.js | 4 ++-- couchpotato/core/settings.py | 1 - couchpotato/static/scripts/api.js | 4 ++-- .../static/scripts/library/question.js | 22 +++++++++---------- couchpotato/static/scripts/page/settings.js | 4 ++-- couchpotato/static/style/main.css | 2 ++ 33 files changed, 56 insertions(+), 53 deletions(-) diff --git a/couchpotato/__init__.py b/couchpotato/__init__.py index 092bd406..fb6b4dc3 100644 --- a/couchpotato/__init__.py +++ b/couchpotato/__init__.py @@ -45,7 +45,7 @@ class WebHandler(BaseHandler): self.write({'success': False, 'error': 'Failed returning results'}) -def addView(route, func, static = False): +def addView(route, func): views[route] = func diff --git a/couchpotato/core/_base/downloader/main.py b/couchpotato/core/_base/downloader/main.py index 80890e4a..70e5cc9c 100644 --- a/couchpotato/core/_base/downloader/main.py +++ b/couchpotato/core/_base/downloader/main.py @@ -72,6 +72,9 @@ class DownloaderBase(Provider): return return self.download(data = data, media = media, filedata = filedata) + def download(self, *args, **kwargs): + return False + def _getAllDownloadStatus(self, download_ids): if self.isDisabled(manual = True, data = {}): return diff --git a/couchpotato/core/_base/downloader/static/downloaders.js b/couchpotato/core/_base/downloader/static/downloaders.js index ec85ce3d..45215158 100644 --- a/couchpotato/core/_base/downloader/static/downloaders.js +++ b/couchpotato/core/_base/downloader/static/downloaders.js @@ -40,15 +40,16 @@ var DownloadersBase = new Class({ button.set('text', button_name); + var message; if(json.success){ - var message = new Element('span.success', { + message = new Element('span.success', { 'text': 'Connection successful' }).inject(button, 'after') } else { var msg_text = 'Connection failed. Check logs for details.'; if(json.hasOwnProperty('msg')) msg_text = json.msg; - var message = new Element('span.failed', { + message = new Element('span.failed', { 'text': msg_text }).inject(button, 'after') } diff --git a/couchpotato/core/_base/updater/main.py b/couchpotato/core/_base/updater/main.py index 5a47fc06..27f9917a 100644 --- a/couchpotato/core/_base/updater/main.py +++ b/couchpotato/core/_base/updater/main.py @@ -10,7 +10,7 @@ from threading import RLock from couchpotato.api import addApiView from couchpotato.core.event import addEvent, fireEvent, fireEventAsync -from couchpotato.core.helpers.encoding import ss, sp +from couchpotato.core.helpers.encoding import sp from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin from couchpotato.environment import Env diff --git a/couchpotato/core/downloaders/nzbvortex.py b/couchpotato/core/downloaders/nzbvortex.py index 92ece6d6..9094055f 100644 --- a/couchpotato/core/downloaders/nzbvortex.py +++ b/couchpotato/core/downloaders/nzbvortex.py @@ -130,7 +130,7 @@ class NZBVortex(DownloaderBase): url = cleanHost(self.conf('host'), ssl = self.conf('ssl')) + 'api/' + call try: - data = self.urlopen('%s?%s' % (url, params), *args, verify_ssl = False, **kwargs) + data = self.urlopen('%s?%s' % (url, params), *args, **kwargs) if data: return json.loads(data) @@ -154,7 +154,7 @@ class NZBVortex(DownloaderBase): url = cleanHost(self.conf('host')) + 'api/app/apilevel' try: - data = self.urlopen(url, show_error = False, verify_ssl = False) + data = self.urlopen(url, show_error = False) self.api_level = float(json.loads(data).get('apilevel')) except URLError as e: if hasattr(e, 'code') and e.code == 403: diff --git a/couchpotato/core/downloaders/sabnzbd.py b/couchpotato/core/downloaders/sabnzbd.py index 57e1dca5..cd51cb87 100644 --- a/couchpotato/core/downloaders/sabnzbd.py +++ b/couchpotato/core/downloaders/sabnzbd.py @@ -194,7 +194,7 @@ class Sabnzbd(DownloaderBase): 'output': 'json' })) - data = self.urlopen(url, timeout = 60, show_error = False, verify_ssl = False, headers = {'User-Agent': Env.getIdentifier()}, **kwargs) + data = self.urlopen(url, timeout = 60, show_error = False, headers = {'User-Agent': Env.getIdentifier()}, **kwargs) if use_json: d = json.loads(data) if d.get('error'): diff --git a/couchpotato/core/downloaders/synology.py b/couchpotato/core/downloaders/synology.py index 125d750a..2c12536f 100644 --- a/couchpotato/core/downloaders/synology.py +++ b/couchpotato/core/downloaders/synology.py @@ -90,6 +90,7 @@ class SynologyRPC(object): self.download_url = 'http://%s:%s/webapi/DownloadStation/task.cgi' % (host, port) self.auth_url = 'http://%s:%s/webapi/auth.cgi' % (host, port) + self.sid = None self.username = username self.password = password self.destination = destination diff --git a/couchpotato/core/downloaders/utorrent.py b/couchpotato/core/downloaders/utorrent.py index 7ff5930d..3164681c 100644 --- a/couchpotato/core/downloaders/utorrent.py +++ b/couchpotato/core/downloaders/utorrent.py @@ -168,7 +168,7 @@ class uTorrent(DownloaderBase): status = 'busy' if (torrent[1] & self.status_flags['STARTED'] or torrent[1] & self.status_flags['QUEUED']) and torrent[4] == 1000: status = 'seeding' - elif (torrent[1] & self.status_flags['ERROR']): + elif torrent[1] & self.status_flags['ERROR']: status = 'failed' elif torrent[4] == 1000: status = 'completed' diff --git a/couchpotato/core/media/__init__.py b/couchpotato/core/media/__init__.py index f0abd28c..6c17e6db 100644 --- a/couchpotato/core/media/__init__.py +++ b/couchpotato/core/media/__init__.py @@ -1,7 +1,7 @@ import os import traceback -from couchpotato import get_db, CPLog +from couchpotato import CPLog from couchpotato.core.event import addEvent, fireEvent, fireEventAsync from couchpotato.core.helpers.encoding import toUnicode from couchpotato.core.plugins.base import Plugin diff --git a/couchpotato/core/media/_base/media/main.py b/couchpotato/core/media/_base/media/main.py index c1fdd92b..3c1bf694 100644 --- a/couchpotato/core/media/_base/media/main.py +++ b/couchpotato/core/media/_base/media/main.py @@ -125,7 +125,6 @@ class MediaPlugin(MediaBase): imdb_id = getImdb(str(media_id)) - media = None if imdb_id: media = db.get('media', 'imdb-%s' % imdb_id, with_doc = True)['doc'] else: diff --git a/couchpotato/core/media/_base/providers/base.py b/couchpotato/core/media/_base/providers/base.py index 6bcf3332..587545c8 100644 --- a/couchpotato/core/media/_base/providers/base.py +++ b/couchpotato/core/media/_base/providers/base.py @@ -129,6 +129,9 @@ class YarrProvider(Provider): else: return [] + def buildUrl(self, *args, **kwargs): + pass + def login(self): # Check if we are still logged in every hour diff --git a/couchpotato/core/media/_base/providers/nzb/newznab.py b/couchpotato/core/media/_base/providers/nzb/newznab.py index 13655eed..628a55f2 100644 --- a/couchpotato/core/media/_base/providers/nzb/newznab.py +++ b/couchpotato/core/media/_base/providers/nzb/newznab.py @@ -91,7 +91,7 @@ class Base(NZBProvider, RSS): # Extract a password from the description password = re.search('(?:' + self.passwords_regex + ')(?: *)(?:\:|\=)(?: *)(.*?)\|\n|$', description, flags = re.I).group(1) if password: - name = name + ' {{%s}}' % password.strip() + name += ' {{%s}}' % password.strip() except: log.debug('Error getting details of "%s": %s', (name, traceback.format_exc())) diff --git a/couchpotato/core/media/_base/providers/torrent/ilovetorrents.py b/couchpotato/core/media/_base/providers/torrent/ilovetorrents.py index eac2c476..33edff7f 100644 --- a/couchpotato/core/media/_base/providers/torrent/ilovetorrents.py +++ b/couchpotato/core/media/_base/providers/torrent/ilovetorrents.py @@ -51,6 +51,7 @@ class Base(TorrentProvider): results_table = None data_split = splitString(data, ' .spinner, .mask{ text-transform: none; line-height: 1; -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; font-size: 15px; color: #FFF; } From a2da42877754775c79d1a16179009ef1aa3ea815 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 14:40:22 +0200 Subject: [PATCH 03/16] Chart css cleanup Tiny webkit scroll --- .../core/media/movie/charts/static/charts.css | 93 +++++++++++-------- 1 file changed, 55 insertions(+), 38 deletions(-) diff --git a/couchpotato/core/media/movie/charts/static/charts.css b/couchpotato/core/media/movie/charts/static/charts.css index 7ac372e2..e3f11fa4 100644 --- a/couchpotato/core/media/movie/charts/static/charts.css +++ b/couchpotato/core/media/movie/charts/static/charts.css @@ -3,26 +3,43 @@ margin-bottom: 30px; } -.charts > h2 { - height: 40px; -} + .charts ::-webkit-scrollbar { + width: 5px; + position: absolute; + } -.charts .chart { - display: inline-block; - width: 50%; - vertical-align: top; - max-height: 510px; - overflow: hidden; - scrollbar-base-color: #4e5969; -} + .charts ::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.4); + -webkit-border-radius: 5px; + border-radius: 5px; + } -.charts .chart:hover { - overflow-y: auto; -} + .charts ::-webkit-scrollbar-thumb { + -webkit-border-radius: 5px; + border-radius: 5px; + background: rgba(255,255,255,0.3); + } -.charts .chart .media_result.hidden { - display: none; -} + .charts > h2 { + height: 40px; + } + + .charts .chart { + display: inline-block; + width: 50%; + vertical-align: top; + max-height: 510px; + overflow: hidden; + scrollbar-base-color: #4e5969; + } + + .charts .chart:hover { + overflow-y: auto; + } + + .charts .chart .media_result.hidden { + display: none; + } .charts .refresh { clear:both; @@ -36,30 +53,30 @@ text-align:center; } - .charts .refresh a { - text-align: center; - padding: 0; - display: none; - width: 30px; - height: 30px; - position: absolute; - right: 10px; - top: -40px; - opacity: .7; - } + .charts .refresh a { + text-align: center; + padding: 0; + display: none; + width: 30px; + height: 30px; + position: absolute; + right: 10px; + top: -40px; + opacity: .7; + } - .charts .refresh a:hover { - opacity: 1; - } + .charts .refresh a:hover { + opacity: 1; + } -.charts p.no_charts_enabled { - padding: 0.7em 1em; - display: none; -} + .charts p.no_charts_enabled { + padding: 0.7em 1em; + display: none; + } -.charts .chart h3 a { - color: #fff; -} + .charts .chart h3 a { + color: #fff; + } .charts .chart .media_result { From dcd0364ecc01831c58968867a43d83167a4f0cdc Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 14:59:06 +0200 Subject: [PATCH 04/16] Re-use tiny scroller for webkit --- .../core/media/movie/_base/static/movie.css | 4 ---- .../core/media/movie/_base/static/movie.js | 2 +- .../core/media/movie/charts/static/charts.css | 22 ----------------- .../core/media/movie/charts/static/charts.js | 6 +++-- couchpotato/static/style/main.css | 24 +++++++++++++++++++ 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/couchpotato/core/media/movie/_base/static/movie.css b/couchpotato/core/media/movie/_base/static/movie.css index 05fcddf4..52f503c3 100644 --- a/couchpotato/core/media/movie/_base/static/movie.css +++ b/couchpotato/core/media/movie/_base/static/movie.css @@ -357,12 +357,8 @@ top: 30px; clear: both; bottom: 30px; - overflow: hidden; position: absolute; } - .movies .data:hover .description { - overflow: auto; - } .movies.list_list .movie:not(.details_view) .info .description, .movies.mass_edit_list .info .description, .movies.thumbs_list .info .description { diff --git a/couchpotato/core/media/movie/_base/static/movie.js b/couchpotato/core/media/movie/_base/static/movie.js index 1a547f2f..679bbc2c 100644 --- a/couchpotato/core/media/movie/_base/static/movie.js +++ b/couchpotato/core/media/movie/_base/static/movie.js @@ -158,7 +158,7 @@ var Movie = new Class({ 'text': self.data.info.year || 'n/a' }) ), - self.description = new Element('div.description', { + self.description = new Element('div.description.tiny_scroll', { 'text': self.data.info.plot }), self.quality = new Element('div.quality', { diff --git a/couchpotato/core/media/movie/charts/static/charts.css b/couchpotato/core/media/movie/charts/static/charts.css index e3f11fa4..261169f6 100644 --- a/couchpotato/core/media/movie/charts/static/charts.css +++ b/couchpotato/core/media/movie/charts/static/charts.css @@ -3,23 +3,6 @@ margin-bottom: 30px; } - .charts ::-webkit-scrollbar { - width: 5px; - position: absolute; - } - - .charts ::-webkit-scrollbar-track { - -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.4); - -webkit-border-radius: 5px; - border-radius: 5px; - } - - .charts ::-webkit-scrollbar-thumb { - -webkit-border-radius: 5px; - border-radius: 5px; - background: rgba(255,255,255,0.3); - } - .charts > h2 { height: 40px; } @@ -29,14 +12,9 @@ width: 50%; vertical-align: top; max-height: 510px; - overflow: hidden; scrollbar-base-color: #4e5969; } - .charts .chart:hover { - overflow-y: auto; - } - .charts .chart .media_result.hidden { display: none; } diff --git a/couchpotato/core/media/movie/charts/static/charts.js b/couchpotato/core/media/movie/charts/static/charts.js index d04f1c2c..a04e248f 100644 --- a/couchpotato/core/media/movie/charts/static/charts.js +++ b/couchpotato/core/media/movie/charts/static/charts.js @@ -22,9 +22,11 @@ var Charts = new Class({ 'events': { 'click': function(e) { e.preventDefault(); - self.el.getChildren('div.chart').destroy(); + + self.el.getElements('.chart').destroy(); self.el_refreshing_text.show(); self.el_refresh_link.hide(); + self.api_request = Api.request('charts.view', { 'data': { 'force_update': 1 }, 'onComplete': self.fill.bind(self) @@ -72,7 +74,7 @@ var Charts = new Class({ Object.each(json.charts, function(chart){ - var c = new Element('div.chart').grab( + var c = new Element('div.chart.tiny_scroll').grab( new Element('h3').grab( new Element('a', { 'text': chart.name, 'href': chart.url diff --git a/couchpotato/static/style/main.css b/couchpotato/static/style/main.css index 84f933b5..c7850b22 100644 --- a/couchpotato/static/style/main.css +++ b/couchpotato/static/style/main.css @@ -53,6 +53,30 @@ input:-moz-placeholder { font-style: italic; } +.tiny_scroll { + overflow: hidden; +} + + .tiny_scroll:hover { + overflow-y: auto; + } + + .tiny_scroll::-webkit-scrollbar { + width: 5px; + } + + .tiny_scroll::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.4); + -webkit-border-radius: 5px; + border-radius: 5px; + } + + .tiny_scroll::-webkit-scrollbar-thumb { + -webkit-border-radius: 5px; + border-radius: 5px; + background: rgba(255,255,255,0.3); + } + a img { border:none; } From 963ce356fb03f04007ac684572f3a1d68d578b11 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 15:19:25 +0200 Subject: [PATCH 05/16] MediaBrowser metadata --- .../movie/providers/metadata/mediabrowser.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 couchpotato/core/media/movie/providers/metadata/mediabrowser.py diff --git a/couchpotato/core/media/movie/providers/metadata/mediabrowser.py b/couchpotato/core/media/movie/providers/metadata/mediabrowser.py new file mode 100644 index 00000000..02a14c35 --- /dev/null +++ b/couchpotato/core/media/movie/providers/metadata/mediabrowser.py @@ -0,0 +1,36 @@ +import os + +from couchpotato.core.media.movie.providers.metadata.base import MovieMetaData + + +autoload = 'WindowsMediaCenter' + + +class MediaBrowser(MovieMetaData): + + def getThumbnailName(self, name, root, i): + return os.path.join(root, 'folder.jpg') + + def getFanartName(self, name, root, i): + return os.path.join(root, 'backdrop.jpg') + + +config = [{ + 'name': 'mediabrowser', + 'groups': [ + { + 'tab': 'renamer', + 'subtab': 'metadata', + 'name': 'mediabrowser_metadata', + 'label': 'MediaBrowser', + 'description': 'Generate folder.jpg and backdrop.jpg', + 'options': [ + { + 'name': 'meta_enabled', + 'default': False, + 'type': 'enabler', + }, + ], + }, + ], +}] From 9b7c1db509b37a9d626af42123daeba9751d4ebf Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 15:20:21 +0200 Subject: [PATCH 06/16] Sony PS3 metadata --- .../media/movie/providers/metadata/ps3.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 couchpotato/core/media/movie/providers/metadata/ps3.py diff --git a/couchpotato/core/media/movie/providers/metadata/ps3.py b/couchpotato/core/media/movie/providers/metadata/ps3.py new file mode 100644 index 00000000..0ff0d854 --- /dev/null +++ b/couchpotato/core/media/movie/providers/metadata/ps3.py @@ -0,0 +1,33 @@ +import os + +from couchpotato.core.media.movie.providers.metadata.base import MovieMetaData + + +autoload = 'WindowsMediaCenter' + + +class SonyPS3(MovieMetaData): + + def getThumbnailName(self, name, root, i): + return os.path.join(root, 'cover.jpg') + + +config = [{ + 'name': 'sonyps3', + 'groups': [ + { + 'tab': 'renamer', + 'subtab': 'metadata', + 'name': 'sonyps3_metadata', + 'label': 'Sony PS3', + 'description': 'Generate cover.jpg', + 'options': [ + { + 'name': 'meta_enabled', + 'default': False, + 'type': 'enabler', + }, + ], + }, + ], +}] From 60fb3e33aed4d79d89bc14d8b25e791f888b8aea Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 15:22:07 +0200 Subject: [PATCH 07/16] Sony PS3 metadata --- couchpotato/core/media/movie/providers/metadata/mediabrowser.py | 2 +- couchpotato/core/media/movie/providers/metadata/ps3.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/media/movie/providers/metadata/mediabrowser.py b/couchpotato/core/media/movie/providers/metadata/mediabrowser.py index 02a14c35..6e40e4c1 100644 --- a/couchpotato/core/media/movie/providers/metadata/mediabrowser.py +++ b/couchpotato/core/media/movie/providers/metadata/mediabrowser.py @@ -3,7 +3,7 @@ import os from couchpotato.core.media.movie.providers.metadata.base import MovieMetaData -autoload = 'WindowsMediaCenter' +autoload = 'MediaBrowser' class MediaBrowser(MovieMetaData): diff --git a/couchpotato/core/media/movie/providers/metadata/ps3.py b/couchpotato/core/media/movie/providers/metadata/ps3.py index 0ff0d854..05df0a53 100644 --- a/couchpotato/core/media/movie/providers/metadata/ps3.py +++ b/couchpotato/core/media/movie/providers/metadata/ps3.py @@ -3,7 +3,7 @@ import os from couchpotato.core.media.movie.providers.metadata.base import MovieMetaData -autoload = 'WindowsMediaCenter' +autoload = 'SonyPS3' class SonyPS3(MovieMetaData): From 553f8d6ccdf341b80737a339dc5e46a57fa3e4ef Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 17:05:08 +0200 Subject: [PATCH 08/16] Ignore ETA every 7 days on search --- couchpotato/core/media/movie/searcher.py | 37 ++++++++++++++++++--- couchpotato/core/notifications/base.py | 1 + couchpotato/core/notifications/core/main.py | 1 + 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/couchpotato/core/media/movie/searcher.py b/couchpotato/core/media/movie/searcher.py index a5b1f477..56a0ff9b 100644 --- a/couchpotato/core/media/movie/searcher.py +++ b/couchpotato/core/media/movie/searcher.py @@ -128,6 +128,9 @@ class MovieSearcher(SearcherBase, MovieTypeBase): found_releases = [] previous_releases = movie.get('releases', []) too_early_to_search = [] + outside_eta_results = 0 + alway_search = self.conf('always_search') + ignore_eta = False default_title = getTitle(movie) if not default_title: @@ -135,6 +138,13 @@ class MovieSearcher(SearcherBase, MovieTypeBase): fireEvent('media.delete', movie['_id'], single = True) return + # Ignore eta once every 7 days + if not alway_search: + prop_name = 'last_ignored_eta.%s' % movie['_id'] + last_ignored_eta = float(Env.prop(prop_name, default = 0)) + if last_ignored_eta > time.time() - 604800: + ignore_eta = True + fireEvent('notify.frontend', type = 'movie.searcher.started', data = {'_id': movie['_id']}, message = 'Searching for "%s"' % default_title) db = get_db() @@ -154,9 +164,13 @@ class MovieSearcher(SearcherBase, MovieTypeBase): index += 1 - if not self.conf('always_search') and not self.couldBeReleased(q_identifier in pre_releases, release_dates, movie['info']['year']): + could_not_be_released = not self.couldBeReleased(q_identifier in pre_releases, release_dates, movie['info']['year']) + if not alway_search and could_not_be_released: too_early_to_search.append(q_identifier) - continue + + # Skip release, if ETA isn't ignored + if not ignore_eta: + continue has_better_quality = 0 @@ -177,15 +191,19 @@ class MovieSearcher(SearcherBase, MovieTypeBase): break quality = fireEvent('quality.single', identifier = q_identifier, single = True) - log.info('Search for %s in %s', (default_title, quality['label'])) + log.info('Search for %s in %s%s', (default_title, quality['label'], ' ignoring ETA' if alway_search or ignore_eta else '')) # Extend quality with profile customs quality['custom'] = quality_custom results = fireEvent('searcher.search', search_protocols, movie, quality, single = True) or [] - if len(results) == 0: + results_count = len(results) + if results_count == 0: log.debug('Nothing found for %s in %s', (default_title, quality['label'])) + # Keep track of releases found outside ETA window + outside_eta_results += results_count if could_not_be_released else 0 + # Check if movie isn't deleted while searching if not fireEvent('media.get', movie.get('_id'), single = True): break @@ -193,8 +211,12 @@ class MovieSearcher(SearcherBase, MovieTypeBase): # Add them to this movie releases list found_releases += fireEvent('release.create_from_search', results, movie, quality, single = True) + # Don't trigger download, but notify user of available releases + if could_not_be_released: + if results_count > 0: + log.debug('Found %s releases for "%s", but ETA isn\'t correct yet.', (results_count, default_title)) # Try find a valid result and download it - if fireEvent('release.try_download_result', results, movie, quality_custom, manual, single = True): + elif fireEvent('release.try_download_result', results, movie, quality_custom, manual, single = True): ret = True # Remove releases that aren't found anymore @@ -214,6 +236,11 @@ class MovieSearcher(SearcherBase, MovieTypeBase): if len(too_early_to_search) > 0: log.info2('Too early to search for %s, %s', (too_early_to_search, default_title)) + if outside_eta_results > 0: + log.info('Found %s releases, but before ETA. Use dashboard to download manually', outside_eta_results) + message = 'Found %s releases for "%s" before ETA. Check them out on the dashboard.' % (outside_eta_results, default_title) + fireEvent('media.available', message = message, data = {}) + fireEvent('notify.frontend', type = 'movie.searcher.ended', data = {'_id': movie['_id']}) return ret diff --git a/couchpotato/core/notifications/base.py b/couchpotato/core/notifications/base.py index 0bfb337c..725704e0 100644 --- a/couchpotato/core/notifications/base.py +++ b/couchpotato/core/notifications/base.py @@ -15,6 +15,7 @@ class Notification(Provider): test_message = 'ZOMG Lazors Pewpewpew!' listen_to = [ + 'media.available', 'renamer.after', 'movie.snatched', 'updater.available', 'updater.updated', 'core.message.important', diff --git a/couchpotato/core/notifications/core/main.py b/couchpotato/core/notifications/core/main.py index 526099fd..5190218e 100644 --- a/couchpotato/core/notifications/core/main.py +++ b/couchpotato/core/notifications/core/main.py @@ -28,6 +28,7 @@ class CoreNotifier(Notification): m_lock = None listen_to = [ + 'media.available', 'renamer.after', 'movie.snatched', 'updater.available', 'updater.updated', 'core.message', 'core.message.important', From f68c35694457230b200ec2d9e8c61d57a26f410b Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 21:07:02 +0200 Subject: [PATCH 09/16] Update title index --- couchpotato/core/media/_base/media/index.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/media/_base/media/index.py b/couchpotato/core/media/_base/media/index.py index fd1affca..1b77b706 100644 --- a/couchpotato/core/media/_base/media/index.py +++ b/couchpotato/core/media/_base/media/index.py @@ -99,7 +99,7 @@ from couchpotato.core.helpers.encoding import simplifyString""" class TitleIndex(TreeBasedIndex): - _version = 3 + _version = 4 custom_header = """from CodernityDB.tree_index import TreeBasedIndex from string import ascii_letters @@ -128,7 +128,7 @@ from couchpotato.core.helpers.encoding import toUnicode, simplifyString""" title = title[len(prefix):] break - return str(nr_prefix + title).ljust(32, '_')[:32] + return str(nr_prefix + title).ljust(32, ' ')[:32] class StartsWithIndex(TreeBasedIndex): From 5d3b0deb4d18c76b0d55bf0f5fd763bf03747e57 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 21:10:20 +0200 Subject: [PATCH 10/16] Simpler progress update --- couchpotato/core/plugins/manage.py | 10 +++++----- couchpotato/core/plugins/scanner.py | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/couchpotato/core/plugins/manage.py b/couchpotato/core/plugins/manage.py index 5b1cf0ef..ff69727c 100644 --- a/couchpotato/core/plugins/manage.py +++ b/couchpotato/core/plugins/manage.py @@ -210,14 +210,14 @@ class Manage(Plugin): 'to_go': total_found, }) + self.updateProgress(folder, to_go) + if group['media'] and group['identifier']: added_identifiers.append(group['identifier']) # Add it to release and update the info fireEvent('release.add', group = group, update_info = False) fireEvent('movie.update_info', identifier = group['identifier'], on_complete = self.createAfterUpdate(folder, group['identifier'])) - else: - self.updateProgress(folder) return addToLibrary @@ -228,7 +228,6 @@ class Manage(Plugin): if not self.in_progress or self.shuttingDown(): return - self.updateProgress(folder) total = self.in_progress[folder]['total'] movie_dict = fireEvent('media.get', identifier, single = True) @@ -236,10 +235,11 @@ class Manage(Plugin): return afterUpdate - def updateProgress(self, folder): + def updateProgress(self, folder, to_go): pr = self.in_progress[folder] - pr['to_go'] -= 1 + if to_go < pr['to_go']: + pr['to_go'] = to_go avg = (time.time() - pr['started']) / (pr['total'] - pr['to_go']) pr['eta'] = tryInt(avg * pr['to_go']) diff --git a/couchpotato/core/plugins/scanner.py b/couchpotato/core/plugins/scanner.py index b75f0bcc..0b461862 100644 --- a/couchpotato/core/plugins/scanner.py +++ b/couchpotato/core/plugins/scanner.py @@ -364,6 +364,7 @@ class Scanner(Plugin): if return_ignored is False and identifier in ignored_identifiers: log.debug('Ignore file found, ignoring release: %s', identifier) + total_found -= 1 continue # Group extra (and easy) files first @@ -384,6 +385,7 @@ class Scanner(Plugin): if len(group['files']['movie']) == 0: log.error('Couldn\'t find any movie files for %s', identifier) + total_found -= 1 continue log.debug('Getting metadata for %s', identifier) @@ -429,7 +431,7 @@ class Scanner(Plugin): # Notify parent & progress on something found if on_found: - on_found(group, total_found, total_found - len(processed_movies)) + on_found(group, total_found, len(valid_files)) # Wait for all the async events calm down a bit while threading.activeCount() > 100 and not self.shuttingDown(): From fdec80f6769a0e589c74905be53089536f46b856 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 21:21:32 +0200 Subject: [PATCH 11/16] Set last_force_eta time --- couchpotato/core/media/movie/searcher.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/couchpotato/core/media/movie/searcher.py b/couchpotato/core/media/movie/searcher.py index 56a0ff9b..f8e27244 100644 --- a/couchpotato/core/media/movie/searcher.py +++ b/couchpotato/core/media/movie/searcher.py @@ -138,14 +138,15 @@ class MovieSearcher(SearcherBase, MovieTypeBase): fireEvent('media.delete', movie['_id'], single = True) return + fireEvent('notify.frontend', type = 'movie.searcher.started', data = {'_id': movie['_id']}, message = 'Searching for "%s"' % default_title) + # Ignore eta once every 7 days if not alway_search: prop_name = 'last_ignored_eta.%s' % movie['_id'] last_ignored_eta = float(Env.prop(prop_name, default = 0)) if last_ignored_eta > time.time() - 604800: ignore_eta = True - - fireEvent('notify.frontend', type = 'movie.searcher.started', data = {'_id': movie['_id']}, message = 'Searching for "%s"' % default_title) + Env.prop(prop_name, value = time.time()) db = get_db() @@ -238,7 +239,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): if outside_eta_results > 0: log.info('Found %s releases, but before ETA. Use dashboard to download manually', outside_eta_results) - message = 'Found %s releases for "%s" before ETA. Check them out on the dashboard.' % (outside_eta_results, default_title) + message = 'Found %s releases for "%s" before ETA. You can check them out on the dashboard.' % (outside_eta_results, default_title) fireEvent('media.available', message = message, data = {}) fireEvent('notify.frontend', type = 'movie.searcher.ended', data = {'_id': movie['_id']}) From 2d243d51e4bacbf74e0b819ad8ec59461c24b473 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 22:03:38 +0200 Subject: [PATCH 12/16] Ignore ETA on manual refresh --- couchpotato/core/media/__init__.py | 2 +- couchpotato/core/media/movie/searcher.py | 16 +++++++++------- couchpotato/core/plugins/release/main.py | 4 ++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/couchpotato/core/media/__init__.py b/couchpotato/core/media/__init__.py index 6c17e6db..4a3eb684 100644 --- a/couchpotato/core/media/__init__.py +++ b/couchpotato/core/media/__init__.py @@ -28,7 +28,7 @@ class MediaBase(Plugin): media = fireEvent('media.get', media_id, single = True) event_name = '%s.searcher.single' % media.get('type') - fireEventAsync(event_name, media, on_complete = self.createNotifyFront(media_id)) + fireEventAsync(event_name, media, on_complete = self.createNotifyFront(media_id), manual = True) except: log.error('Failed creating onComplete: %s', traceback.format_exc()) diff --git a/couchpotato/core/media/movie/searcher.py b/couchpotato/core/media/movie/searcher.py index f8e27244..4b2d2e4b 100644 --- a/couchpotato/core/media/movie/searcher.py +++ b/couchpotato/core/media/movie/searcher.py @@ -58,13 +58,13 @@ class MovieSearcher(SearcherBase, MovieTypeBase): def searchAllView(self, **kwargs): - fireEventAsync('movie.searcher.all') + fireEventAsync('movie.searcher.all', manual = True) return { 'success': not self.in_progress } - def searchAll(self): + def searchAll(self, manual = False): if self.in_progress: log.info('Search already in progress') @@ -91,7 +91,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): media = fireEvent('media.get', media_id, single = True) try: - self.single(media, search_protocols) + self.single(media, search_protocols, manual = manual) except IndexError: log.error('Forcing library update for %s, if you see this often, please report: %s', (getIdentifier(media), traceback.format_exc())) fireEvent('movie.update_info', media_id) @@ -130,7 +130,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): too_early_to_search = [] outside_eta_results = 0 alway_search = self.conf('always_search') - ignore_eta = False + ignore_eta = manual default_title = getTitle(movie) if not default_title: @@ -217,7 +217,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): if results_count > 0: log.debug('Found %s releases for "%s", but ETA isn\'t correct yet.', (results_count, default_title)) # Try find a valid result and download it - elif fireEvent('release.try_download_result', results, movie, quality_custom, manual, single = True): + elif fireEvent('release.try_download_result', results, movie, quality_custom, single = True): ret = True # Remove releases that aren't found anymore @@ -239,8 +239,10 @@ class MovieSearcher(SearcherBase, MovieTypeBase): if outside_eta_results > 0: log.info('Found %s releases, but before ETA. Use dashboard to download manually', outside_eta_results) - message = 'Found %s releases for "%s" before ETA. You can check them out on the dashboard.' % (outside_eta_results, default_title) - fireEvent('media.available', message = message, data = {}) + + if not manual: + message = 'Found %s releases for "%s" before ETA. You can check them out on the dashboard.' % (outside_eta_results, default_title) + fireEvent('media.available', message = message, data = {}) fireEvent('notify.frontend', type = 'movie.searcher.ended', data = {'_id': movie['_id']}) diff --git a/couchpotato/core/plugins/release/main.py b/couchpotato/core/plugins/release/main.py index 0d22c165..970e90df 100644 --- a/couchpotato/core/plugins/release/main.py +++ b/couchpotato/core/plugins/release/main.py @@ -341,7 +341,7 @@ class Release(Plugin): return True - def tryDownloadResult(self, results, media, quality_custom, manual = False): + def tryDownloadResult(self, results, media, quality_custom): wait_for = False let_through = False @@ -375,7 +375,7 @@ class Release(Plugin): wait_for = True continue - downloaded = fireEvent('release.download', data = rel, media = media, manual = manual, single = True) + downloaded = fireEvent('release.download', data = rel, media = media, single = True) if downloaded is True: return True elif downloaded != 'try_next': From 41052ae50826a4d29b817f65cc9053fcad204084 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 22:14:13 +0200 Subject: [PATCH 13/16] Use same before ETA message --- couchpotato/core/media/movie/searcher.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/media/movie/searcher.py b/couchpotato/core/media/movie/searcher.py index 4b2d2e4b..24aa73e2 100644 --- a/couchpotato/core/media/movie/searcher.py +++ b/couchpotato/core/media/movie/searcher.py @@ -238,10 +238,10 @@ class MovieSearcher(SearcherBase, MovieTypeBase): log.info2('Too early to search for %s, %s', (too_early_to_search, default_title)) if outside_eta_results > 0: - log.info('Found %s releases, but before ETA. Use dashboard to download manually', outside_eta_results) + message = 'Found %s releases for "%s" before ETA. Select and download via the dashboard.' % (outside_eta_results, default_title) + log.info(message) if not manual: - message = 'Found %s releases for "%s" before ETA. You can check them out on the dashboard.' % (outside_eta_results, default_title) fireEvent('media.available', message = message, data = {}) fireEvent('notify.frontend', type = 'movie.searcher.ended', data = {'_id': movie['_id']}) From c0297f10cb188b12bbaf97a6f4329ea7d7da8024 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 22:24:11 +0200 Subject: [PATCH 14/16] Force download on "best release" selection --- couchpotato/core/media/movie/searcher.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/couchpotato/core/media/movie/searcher.py b/couchpotato/core/media/movie/searcher.py index 24aa73e2..81d62004 100644 --- a/couchpotato/core/media/movie/searcher.py +++ b/couchpotato/core/media/movie/searcher.py @@ -109,7 +109,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): self.in_progress = False - def single(self, movie, search_protocols = None, manual = False): + def single(self, movie, search_protocols = None, manual = False, force_download = False): # Find out search type try: @@ -216,8 +216,9 @@ class MovieSearcher(SearcherBase, MovieTypeBase): if could_not_be_released: if results_count > 0: log.debug('Found %s releases for "%s", but ETA isn\'t correct yet.', (results_count, default_title)) + # Try find a valid result and download it - elif fireEvent('release.try_download_result', results, movie, quality_custom, single = True): + if (force_download or not could_not_be_released) and fireEvent('release.try_download_result', results, movie, quality_custom, single = True): ret = True # Remove releases that aren't found anymore @@ -364,13 +365,13 @@ class MovieSearcher(SearcherBase, MovieTypeBase): def tryNextReleaseView(self, media_id = None, **kwargs): - trynext = self.tryNextRelease(media_id, manual = True) + trynext = self.tryNextRelease(media_id, manual = True, force_download = True) return { 'success': trynext } - def tryNextRelease(self, media_id, manual = False): + def tryNextRelease(self, media_id, manual = False, force_download = False): try: db = get_db() @@ -382,7 +383,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase): movie_dict = fireEvent('media.get', media_id, single = True) log.info('Trying next release for: %s', getTitle(movie_dict)) - self.single(movie_dict, manual = manual) + self.single(movie_dict, manual = manual, force_download = force_download) return True From 83e8ae392d76f6c364dea9b575bc4e1ff7ea27e2 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 23:17:59 +0200 Subject: [PATCH 15/16] Don't create a new "done" release on rename. fix #3250 --- couchpotato/core/plugins/manage.py | 9 ++-- couchpotato/core/plugins/release/main.py | 58 +++++++++++++++--------- couchpotato/core/plugins/renamer.py | 7 ++- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/couchpotato/core/plugins/manage.py b/couchpotato/core/plugins/manage.py index ff69727c..97f66a78 100644 --- a/couchpotato/core/plugins/manage.py +++ b/couchpotato/core/plugins/manage.py @@ -32,7 +32,7 @@ class Manage(Plugin): # Add files after renaming def after_rename(message = None, group = None): if not group: group = {} - return self.scanFilesToLibrary(folder = group['destination_dir'], files = group['renamed_files']) + return self.scanFilesToLibrary(folder = group['destination_dir'], files = group['renamed_files'], release_download = group['release_download']) addEvent('renamer.after', after_rename, priority = 110) addApiView('manage.update', self.updateLibraryView, docs = { @@ -254,7 +254,7 @@ class Manage(Plugin): return [] - def scanFilesToLibrary(self, folder = None, files = None): + def scanFilesToLibrary(self, folder = None, files = None, release_download = None): folder = os.path.normpath(folder) @@ -263,7 +263,10 @@ class Manage(Plugin): if groups: for group in groups.values(): if group.get('media'): - fireEvent('release.add', group = group) + if release_download and release_download.get('release_id'): + fireEvent('release.add', group = group, update_id = release_download.get('release_id')) + else: + fireEvent('release.add', group = group) def getDiskSpace(self): diff --git a/couchpotato/core/plugins/release/main.py b/couchpotato/core/plugins/release/main.py index 970e90df..08f02480 100644 --- a/couchpotato/core/plugins/release/main.py +++ b/couchpotato/core/plugins/release/main.py @@ -104,7 +104,7 @@ class Release(Plugin): elif rel['status'] in ['snatched', 'downloaded']: self.updateStatus(rel['_id'], status = 'ignore') - def add(self, group, update_info = True): + def add(self, group, update_info = True, update_id = None): try: db = get_db() @@ -120,27 +120,43 @@ class Release(Plugin): 'profile_id': None, }, search_after = False, update_after = update_info, notify_after = False, status = 'done', single = True) - # Add Release - release = { - '_t': 'release', - 'media_id': media['_id'], - 'identifier': release_identifier, - 'quality': group['meta_data']['quality'].get('identifier'), - 'is_3d': group['meta_data']['quality'].get('is_3d', 0), - 'last_edit': int(time.time()), - 'status': 'done' - } - try: - r = db.get('release_identifier', release_identifier, with_doc = True)['doc'] - r['media_id'] = media['_id'] - except: - r = db.insert(release) + release = None + if update_id: + try: + release = db.get('id', update_id) + release.update({ + 'identifier': release_identifier, + 'last_edit': int(time.time()), + 'status': 'done', + }) + except: + log.error('Failed updating existing release: %s', traceback.format_exc()) + else: - # Update with ref and _id - release.update({ - '_id': r['_id'], - '_rev': r['_rev'], - }) + # Add Release + if not release: + release = { + '_t': 'release', + 'media_id': media['_id'], + 'identifier': release_identifier, + 'quality': group['meta_data']['quality'].get('identifier'), + 'is_3d': group['meta_data']['quality'].get('is_3d', 0), + 'last_edit': int(time.time()), + 'status': 'done' + } + + try: + r = db.get('release_identifier', release_identifier, with_doc = True)['doc'] + r['media_id'] = media['_id'] + except: + log.error('Failed updating release by identifier: %s', traceback.format_exc()) + r = db.insert(release) + + # Update with ref and _id + release.update({ + '_id': r['_id'], + '_rev': r['_rev'], + }) # Empty out empty file groups release['files'] = dict((k, [toUnicode(x) for x in v]) for k, v in group['files'].items() if v) diff --git a/couchpotato/core/plugins/renamer.py b/couchpotato/core/plugins/renamer.py index 2da985fa..11585802 100644 --- a/couchpotato/core/plugins/renamer.py +++ b/couchpotato/core/plugins/renamer.py @@ -227,6 +227,7 @@ class Renamer(Plugin): for group_identifier in groups: group = groups[group_identifier] + group['release_download'] = None rename_files = {} remove_files = [] remove_releases = [] @@ -504,13 +505,15 @@ class Renamer(Plugin): if release_download['status'] == 'completed': # Set the release to downloaded fireEvent('release.update_status', release['_id'], status = 'downloaded', single = True) + group['release_download'] = release_download elif release_download['status'] == 'seeding': # Set the release to seeding fireEvent('release.update_status', release['_id'], status = 'seeding', single = True) elif release.get('identifier') == group['meta_data']['quality']['identifier']: - # Set the release to downloaded - fireEvent('release.update_status', release['_id'], status = 'downloaded', single = True) + # Set the release to downloaded + fireEvent('release.update_status', release['_id'], status = 'downloaded', single = True) + group['release_download'] = release_download # Remove leftover files if not remove_leftovers: # Don't remove anything From 8ae1e586148e74e5b355f29335da58d9a8ba36f7 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 11 Jun 2014 23:34:17 +0200 Subject: [PATCH 16/16] Don't call parent init for synoindex --- couchpotato/core/notifications/nmj.py | 3 ++- couchpotato/core/notifications/synoindex.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/notifications/nmj.py b/couchpotato/core/notifications/nmj.py index 7579cafa..665837f1 100644 --- a/couchpotato/core/notifications/nmj.py +++ b/couchpotato/core/notifications/nmj.py @@ -22,10 +22,11 @@ class NMJ(Notification): # noinspection PyMissingConstructor def __init__(self): - addEvent('renamer.after', self.addToLibrary) addApiView(self.testNotifyName(), self.test) addApiView('notify.nmj.auto_config', self.autoConfig) + addEvent('renamer.after', self.addToLibrary) + def autoConfig(self, host = 'localhost', **kwargs): mount = '' diff --git a/couchpotato/core/notifications/synoindex.py b/couchpotato/core/notifications/synoindex.py index cd4ff8f9..b14e1a03 100644 --- a/couchpotato/core/notifications/synoindex.py +++ b/couchpotato/core/notifications/synoindex.py @@ -1,6 +1,7 @@ import os import subprocess +from couchpotato.api import addApiView from couchpotato.core.event import addEvent from couchpotato.core.logger import CPLog from couchpotato.core.notifications.base import Notification @@ -16,7 +17,8 @@ class Synoindex(Notification): index_path = '/usr/syno/bin/synoindex' def __init__(self): - super(Synoindex, self).__init__() + addApiView(self.testNotifyName(), self.test) + addEvent('renamer.after', self.addToLibrary) def addToLibrary(self, message = None, group = None):