Merge branch 'refs/heads/develop' into tv

Conflicts:
	couchpotato/core/media/movie/_base/static/search.js
This commit is contained in:
Ruud
2013-10-07 23:12:59 +02:00
17 changed files with 340 additions and 257 deletions
@@ -0,0 +1,6 @@
from .main import Search
def start():
return Search()
config = []
@@ -0,0 +1,59 @@
from couchpotato.api import addApiView
from couchpotato.core.event import fireEvent, addEvent
from couchpotato.core.helpers.variable import mergeDicts
from couchpotato.core.logger import CPLog
from couchpotato.core.plugins.base import Plugin
log = CPLog(__name__)
class Search(Plugin):
def __init__(self):
addApiView('search', self.search, docs = {
'desc': 'Search the info in providers for a movie',
'params': {
'q': {'desc': 'The (partial) movie name you want to search for'},
'type': {'desc': 'Search for a specific media type. Leave empty to search all.'},
},
'return': {'type': 'object', 'example': """{
'success': True,
'movies': array,
'show': array,
etc
}"""}
})
addEvent('app.load', self.addSingleSearches)
def search(self, q = '', types = None, **kwargs):
# Make sure types is the correct instance
if isinstance(types, (str, unicode)):
types = [types]
elif isinstance(types, (list, tuple, set)):
types = list(types)
if not types:
result = fireEvent('info.search', q = q, merge = True)
else:
result = {}
for media_type in types:
result[media_type] = fireEvent('%s.search' % media_type)
return mergeDicts({
'success': True,
}, result)
def createSingleSearch(self, media_type):
def singleSearch(q, **kwargs):
return self.search(q, type = media_type, **kwargs)
return singleSearch
def addSingleSearches(self):
for media_type in fireEvent('media.types', merge = True):
addApiView('%s.search' % media_type, self.createSingleSearch(media_type))
@@ -129,13 +129,13 @@
overflow-x: hidden;
}
.movie_result {
.media_result {
overflow: hidden;
height: 50px;
position: relative;
}
.movie_result .options {
.media_result .options {
position: absolute;
height: 100%;
top: 0;
@@ -147,48 +147,48 @@
border-radius: 0;
box-shadow: inset 0 1px 8px rgba(0,0,0,0.25);
}
.movie_result .options > .in_library_wanted {
.media_result .options > .in_library_wanted {
margin-top: -7px;
}
.movie_result .options > div {
.media_result .options > div {
border: 0;
}
.movie_result .options .thumbnail {
.media_result .options .thumbnail {
vertical-align: middle;
}
.movie_result .options select {
.media_result .options select {
vertical-align: middle;
display: inline-block;
margin-right: 10px;
}
.movie_result .options select[name=title] { width: 170px; }
.movie_result .options select[name=profile] { width: 90px; }
.movie_result .options select[name=category] { width: 80px; }
.media_result .options select[name=title] { width: 170px; }
.media_result .options select[name=profile] { width: 90px; }
.media_result .options select[name=category] { width: 80px; }
@media all and (max-width: 480px) {
.movie_result .options select[name=title] { width: 90px; }
.movie_result .options select[name=profile] { width: 50px; }
.movie_result .options select[name=category] { width: 50px; }
.media_result .options select[name=title] { width: 90px; }
.media_result .options select[name=profile] { width: 50px; }
.media_result .options select[name=category] { width: 50px; }
}
.movie_result .options .button {
.media_result .options .button {
vertical-align: middle;
display: inline-block;
}
.movie_result .options .message {
.media_result .options .message {
height: 100%;
font-size: 20px;
color: #fff;
line-height: 20px;
}
.movie_result .data {
.media_result .data {
position: absolute;
height: 100%;
top: 0;
@@ -199,20 +199,20 @@
border-top: 1px solid rgba(255,255,255, 0.08);
transition: all .4s cubic-bezier(0.9,0,0.1,1);
}
.movie_result .data.open {
.media_result .data.open {
left: 100% !important;
}
.movie_result:last-child .data { border-bottom: 0; }
.media_result:last-child .data { border-bottom: 0; }
.movie_result .in_wanted, .movie_result .in_library {
.media_result .in_wanted, .media_result .in_library {
position: absolute;
bottom: 2px;
left: 14px;
font-size: 11px;
}
.movie_result .thumbnail {
.media_result .thumbnail {
width: 34px;
min-height: 100%;
display: block;
@@ -220,7 +220,7 @@
vertical-align: top;
}
.movie_result .info {
.media_result .info {
position: absolute;
top: 20%;
left: 15px;
@@ -228,7 +228,7 @@
vertical-align: middle;
}
.movie_result .info h2 {
.media_result .info h2 {
margin: 0;
font-weight: normal;
font-size: 20px;
@@ -240,7 +240,7 @@
width: 100%;
}
.movie_result .info h2 .title {
.media_result .info h2 .title {
display: block;
margin: 0;
text-overflow: ellipsis;
@@ -253,7 +253,7 @@
width: 88%;
}
.movie_result .info h2 .year {
.media_result .info h2 .year {
padding: 0 5px;
text-align: center;
position: absolute;
@@ -271,7 +271,7 @@
}
.search_form .mask,
.movie_result .mask {
.media_result .mask {
position: absolute;
height: 100%;
width: 100%;
@@ -0,0 +1,188 @@
Block.Search = new Class({
Extends: BlockBase,
cache: {},
create: function(){
var self = this;
var focus_timer = 0;
self.el = new Element('div.search_form').adopt(
new Element('div.input').adopt(
self.input = new Element('input', {
'placeholder': 'Search & add a new media',
'events': {
'keyup': self.keyup.bind(self),
'focus': function(){
if(focus_timer) clearTimeout(focus_timer);
self.el.addClass('focused')
if(this.get('value'))
self.hideResults(false)
},
'blur': function(){
focus_timer = (function(){
self.el.removeClass('focused')
}).delay(100);
}
}
}),
new Element('a.icon2', {
'events': {
'click': self.clear.bind(self),
'touchend': self.clear.bind(self)
}
})
),
self.result_container = new Element('div.results_container', {
'tween': {
'duration': 200
},
'events': {
'mousewheel': function(e){
(e).stopPropagation();
}
}
}).adopt(
self.results = new Element('div.results')
)
);
self.mask = new Element('div.mask').inject(self.result_container).fade('hide');
},
clear: function(e){
var self = this;
(e).preventDefault();
if(self.last_q === ''){
self.input.blur()
self.last_q = null;
}
else {
self.last_q = '';
self.input.set('value', '');
self.input.focus()
self.media = {}
self.results.empty()
self.el.removeClass('filled')
}
},
hideResults: function(bool){
var self = this;
if(self.hidden == bool) return;
self.el[bool ? 'removeClass' : 'addClass']('shown');
if(bool){
History.removeEvent('change', self.hideResults.bind(self, !bool));
self.el.removeEvent('outerClick', self.hideResults.bind(self, !bool));
}
else {
History.addEvent('change', self.hideResults.bind(self, !bool));
self.el.addEvent('outerClick', self.hideResults.bind(self, !bool));
}
self.hidden = bool;
},
keyup: function(e){
var self = this;
self.el[self.q() ? 'addClass' : 'removeClass']('filled')
if(self.q() != self.last_q){
if(self.api_request && self.api_request.isRunning())
self.api_request.cancel();
if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer)
self.autocomplete_timer = self.autocomplete.delay(300, self)
}
},
autocomplete: function(){
var self = this;
if(!self.q()){
self.hideResults(true)
return
}
self.list()
},
list: function(){
var self = this,
q = self.q(),
cache = self.cache[q];
self.hideResults(false);
if(!cache){
self.mask.fade('in');
if(!self.spinner)
self.spinner = createSpinner(self.mask);
self.api_request = Api.request('search', {
'data': {
'q': q
},
'onComplete': self.fill.bind(self, q)
})
}
else
self.fill(q, cache)
self.last_q = q;
},
fill: function(q, json){
var self = this;
self.cache[q] = json
self.media = {}
self.results.empty()
Object.each(json, function(media, type){
if(typeOf(media) == 'array'){
Object.each(media, function(m){
var m = new Block.Search[m.type.capitalize() + 'Item'](m);
$(m).inject(self.results)
self.media[m.imdb || 'r-'+Math.floor(Math.random()*10000)] = m
if(q == m.imdb)
m.showOptions()
});
}
})
// Calculate result heights
var w = window.getSize(),
rc = self.result_container.getCoordinates();
self.results.setStyle('max-height', (w.y - rc.top - 50) + 'px')
self.mask.fade('out')
},
loading: function(bool){
this.el[bool ? 'addClass' : 'removeClass']('loading')
},
q: function(){
return this.input.get('value').trim();
}
});
@@ -34,17 +34,6 @@ class MovieBase(MovieTypeBase):
super(MovieBase, self).__init__()
self.initType()
addApiView('movie.search', self.search, docs = {
'desc': 'Search the movie providers for a movie',
'params': {
'q': {'desc': 'The (partial) movie name you want to search for'},
},
'return': {'type': 'object', 'example': """{
'success': True,
'empty': bool, any movies returned or not,
'movies': array, movies found,
}"""}
})
addApiView('movie.list', self.listView, docs = {
'desc': 'List movies in wanted list',
'params': {
@@ -436,7 +436,7 @@
}
.movies .data .quality .seeding { background-color: #0a6819; }
.movies .data .quality .finish {
background-image: url('../images/sprite.png');
background-image: url('../../images/sprite.png');
background-repeat: no-repeat;
background-position: 0 2px;
padding-left: 14px;
@@ -992,7 +992,7 @@
}
.movies .empty_wanted {
background-image: url('../images/emptylist.png');
background-image: url('../../images/emptylist.png');
background-position: 80% 0;
height: 750px;
width: 100%;
@@ -1,189 +1,4 @@
Block.Search = new Class({
Extends: BlockBase,
cache: {},
create: function(){
var self = this;
var focus_timer = 0;
self.el = new Element('div.search_form').adopt(
new Element('div.input').adopt(
self.input = new Element('input', {
'placeholder': 'Search & add a new movie',
'events': {
'keyup': self.keyup.bind(self),
'focus': function(){
if(focus_timer) clearTimeout(focus_timer);
self.el.addClass('focused')
if(this.get('value'))
self.hideResults(false)
},
'blur': function(){
focus_timer = (function(){
self.el.removeClass('focused')
}).delay(100);
}
}
}),
new Element('a.icon2', {
'events': {
'click': self.clear.bind(self),
'touchend': self.clear.bind(self)
}
})
),
self.result_container = new Element('div.results_container', {
'tween': {
'duration': 200
},
'events': {
'mousewheel': function(e){
(e).stopPropagation();
}
}
}).adopt(
self.results = new Element('div.results')
)
);
self.mask = new Element('div.mask').inject(self.result_container).fade('hide');
},
clear: function(e){
var self = this;
(e).preventDefault();
if(self.last_q === ''){
self.input.blur()
self.last_q = null;
}
else {
self.last_q = '';
self.input.set('value', '');
self.input.focus()
self.movies = []
self.results.empty()
self.el.removeClass('filled')
}
},
hideResults: function(bool){
var self = this;
if(self.hidden == bool) return;
self.el[bool ? 'removeClass' : 'addClass']('shown');
if(bool){
History.removeEvent('change', self.hideResults.bind(self, !bool));
self.el.removeEvent('outerClick', self.hideResults.bind(self, !bool));
}
else {
History.addEvent('change', self.hideResults.bind(self, !bool));
self.el.addEvent('outerClick', self.hideResults.bind(self, !bool));
}
self.hidden = bool;
},
keyup: function(e){
var self = this;
self.el[self.q() ? 'addClass' : 'removeClass']('filled')
if(self.q() != self.last_q){
if(self.api_request && self.api_request.isRunning())
self.api_request.cancel();
if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer)
self.autocomplete_timer = self.autocomplete.delay(300, self)
}
},
autocomplete: function(){
var self = this;
if(!self.q()){
self.hideResults(true)
return
}
self.list()
},
list: function(){
var self = this,
q = self.q(),
cache = self.cache[q];
self.hideResults(false);
if(!cache){
self.mask.fade('in');
if(!self.spinner)
self.spinner = createSpinner(self.mask);
self.api_request = Api.request('movie.search', {
'data': {
'q': q
},
'onComplete': self.fill.bind(self, q)
})
}
else
self.fill(q, cache)
self.last_q = q;
},
fill: function(q, json){
var self = this;
self.cache[q] = json
self.movies = {}
self.results.empty()
Object.each(json.movies, function(movie){
var m = new Block.Search.Item(movie);
$(m).inject(self.results)
self.movies[movie.imdb || 'r-'+Math.floor(Math.random()*10000)] = m
if(q == movie.imdb)
m.movieOptions()
});
// Calculate result heights
var w = window.getSize(),
rc = self.result_container.getCoordinates();
self.results.setStyle('max-height', (w.y - rc.top - 50) + 'px')
self.mask.fade('out')
},
loading: function(bool){
this.el[bool ? 'addClass' : 'removeClass']('loading')
},
q: function(){
return this.input.get('value').trim();
}
});
Block.Search.Item = new Class({
Block.Search.MovieItem = new Class({
Implements: [Options, Events],
@@ -201,7 +16,7 @@ Block.Search.Item = new Class({
var self = this,
info = self.info;
self.el = new Element('div.movie_result', {
self.el = new Element('div.media_result', {
'id': info.imdb
}).adopt(
self.thumbnail = info.images && info.images.poster.length > 0 ? new Element('img.thumbnail', {
@@ -5,31 +5,31 @@
height: 40px;
}
.suggestions .movie_result {
.suggestions .media_result {
display: inline-block;
width: 33.333%;
height: 150px;
}
@media all and (max-width: 960px) {
.suggestions .movie_result {
.suggestions .media_result {
width: 50%;
}
}
@media all and (max-width: 600px) {
.suggestions .movie_result {
.suggestions .media_result {
width: 100%;
}
}
.suggestions .movie_result .data {
.suggestions .media_result .data {
left: 100px;
background: #4e5969;
border: none;
}
.suggestions .movie_result .data .info {
.suggestions .media_result .data .info {
top: 15px;
left: 15px;
right: 15px;
@@ -37,32 +37,32 @@
overflow: hidden;
}
.suggestions .movie_result .data .info h2 {
.suggestions .media_result .data .info h2 {
white-space: normal;
max-height: 120px;
font-size: 18px;
line-height: 18px;
}
.suggestions .movie_result .data .info .rating,
.suggestions .movie_result .data .info .genres,
.suggestions .movie_result .data .info .year {
.suggestions .media_result .data .info .rating,
.suggestions .media_result .data .info .genres,
.suggestions .media_result .data .info .year {
position: static;
display: block;
padding: 0;
opacity: .6;
}
.suggestions .movie_result .data .info .year {
.suggestions .media_result .data .info .year {
margin: 10px 0 0;
}
.suggestions .movie_result .data .info .rating {
.suggestions .media_result .data .info .rating {
font-size: 20px;
float: right;
margin-top: -20px;
}
.suggestions .movie_result .data .info .rating:before {
.suggestions .media_result .data .info .rating:before {
content: "\e031";
font-family: 'Elusive-Icons';
font-size: 14px;
@@ -70,25 +70,25 @@
vertical-align: bottom;
}
.suggestions .movie_result .data .info .genres {
.suggestions .media_result .data .info .genres {
font-size: 11px;
font-style: italic;
text-align: right;
}
.suggestions .movie_result .data {
.suggestions .media_result .data {
cursor: default;
}
.suggestions .movie_result .options {
.suggestions .media_result .options {
left: 100px;
}
.suggestions .movie_result .options select[name=title] { width: 100%; }
.suggestions .movie_result .options select[name=profile] { width: 100%; }
.suggestions .movie_result .options select[name=category] { width: 100%; }
.suggestions .media_result .options select[name=title] { width: 100%; }
.suggestions .media_result .options select[name=profile] { width: 100%; }
.suggestions .media_result .options select[name=category] { width: 100%; }
.suggestions .movie_result .button {
.suggestions .media_result .button {
position: absolute;
margin: 2px 0 0 0;
right: 15px;
@@ -96,25 +96,25 @@
}
.suggestions .movie_result .thumbnail {
.suggestions .media_result .thumbnail {
width: 100px;
}
.suggestions .movie_result .actions {
.suggestions .media_result .actions {
position: absolute;
bottom: 10px;
right: 10px;
display: none;
width: 140px;
}
.suggestions .movie_result:hover .actions {
.suggestions .media_result:hover .actions {
display: block;
}
.suggestions .movie_result .data.open .actions {
.suggestions .media_result .data.open .actions {
display: none;
}
.suggestions .movie_result .actions a {
.suggestions .media_result .actions a {
margin-left: 10px;
vertical-align: middle;
}
@@ -17,7 +17,7 @@ var SuggestList = new Class({
'click:relay(a.delete)': function(e, el){
(e).stop();
$(el).getParent('.movie_result').destroy();
$(el).getParent('.media_result').destroy();
Api.request('suggestion.ignore', {
'data': {
@@ -30,7 +30,7 @@ var SuggestList = new Class({
'click:relay(a.eye-open)': function(e, el){
(e).stop();
$(el).getParent('.movie_result').destroy();
$(el).getParent('.media_result').destroy();
Api.request('suggestion.ignore', {
'data': {
@@ -65,7 +65,7 @@ var SuggestList = new Class({
Object.each(json.suggestions, function(movie){
var m = new Block.Search.Item(movie, {
var m = new Block.Search.MovieItem(movie, {
'onAdded': function(){
self.afterAdded(m, movie)
}
+3 -3
View File
@@ -661,9 +661,9 @@ Remove it if you want it to be renamed (again, or at least let it try again)
fireEvent('status.get', ['snatched', 'ignored', 'failed', 'done', 'seeding', 'downloaded', 'missing'], single = True)
db = get_session()
rels = db.query(Release).filter_by(status_id = snatched_status.get('id')).all()
rels.extend(db.query(Release).filter_by(status_id = seeding_status.get('id')).all())
rels.extend(db.query(Release).filter_by(status_id = missing_status.get('id')).all())
rels = db.query(Release).filter(
Release.status_id.in_([snatched_status.get('id'), seeding_status.get('id'), missing_status.get('id')])
).all()
scan_items = []
scan_required = False
@@ -43,9 +43,32 @@ class Movie(ModifierBase):
}
def __init__(self):
addEvent('result.modify.info.search', self.returnByType)
addEvent('result.modify.movie.search', self.combineOnIMDB)
addEvent('result.modify.movie.info', self.checkLibrary)
def returnByType(self, results):
new_results = {'unknown':[]}
for r in results:
if r.get('type'):
type_name = r.get('type') + 's'
if not new_results.has_key(type_name):
new_results[type_name] = []
new_results[type_name].append(r)
else:
new_results['unknown'].append(r)
if len(new_results['unknown']) == 0:
del new_results['unknown']
# Combine movies, needs a cleaner way..
if new_results.has_key('movies'):
new_results['movies'] = self.combineOnIMDB(new_results['movies'])
return new_results
def combineOnIMDB(self, results):
temp = {}
@@ -107,4 +130,4 @@ class Movie(ModifierBase):
class Show(ModifierBase):
pass
pass
@@ -84,6 +84,7 @@ class OMDBAPI(MovieProvider):
year = tryInt(movie.get('Year', ''))
movie_data = {
'type': 'movie',
'via_imdb': True,
'titles': [movie.get('Title')] if movie.get('Title') else [],
'original_title': movie.get('Title'),
@@ -11,6 +11,7 @@ log = CPLog(__name__)
class TheMovieDb(MovieProvider):
def __init__(self):
addEvent('info.search', self.search, priority = 2)
addEvent('movie.search', self.search, priority = 2)
addEvent('movie.info', self.getInfo, priority = 2)
addEvent('movie.info_by_tmdb', self.getInfo)
@@ -103,6 +104,7 @@ class TheMovieDb(MovieProvider):
year = None
movie_data = {
'type': 'movie',
'via_tmdb': True,
'tmdb_id': movie.id,
'titles': [toUnicode(movie.title)],
@@ -12,7 +12,7 @@ class TorrentBytes(TorrentProvider):
urls = {
'test' : 'https://www.torrentbytes.net/',
'login' : 'https://www.torrentbytes.net/takelogin.php',
'login' : 'https://www.torrentbytes.net/login.php',
'login_check' : 'https://www.torrentbytes.net/inbox.php',
'detail' : 'https://www.torrentbytes.net/details.php?id=%s',
'search' : 'https://www.torrentbytes.net/browse.php?search=%s&cat=%d',
@@ -25,7 +25,7 @@ class Yify(TorrentProvider):
def _searchOnTitle(self, title, movie, quality, results):
data = self.getJsonData(self.urls['search'] % (title, quality['identifier']))
data = self.getJsonData(self.urls['search'] % (movie['library']['identifier'], quality['identifier']))
if data and data.get('MovieList'):
try: