From 7b7488ece857f10f54a4e54f41c8f1b9f2d20903 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 16 Mar 2013 22:23:11 +0100 Subject: [PATCH] Dashboard split Do more with snatched and other statusses --- couchpotato/core/plugins/dashboard/main.py | 14 +- couchpotato/core/plugins/movie/main.py | 12 +- couchpotato/core/plugins/movie/static/list.js | 22 +- .../core/plugins/movie/static/movie.actions.js | 699 +++++++++++++++++++++ couchpotato/core/plugins/movie/static/movie.css | 18 + couchpotato/core/plugins/movie/static/movie.js | 393 +----------- couchpotato/core/plugins/searcher/main.py | 9 +- couchpotato/static/images/icon.readd.png | Bin 0 -> 778 bytes couchpotato/static/scripts/page/home.js | 60 +- couchpotato/static/scripts/page/manage.js | 2 +- couchpotato/static/scripts/page/wanted.js | 283 +-------- couchpotato/static/style/main.css | 1 + 12 files changed, 796 insertions(+), 717 deletions(-) create mode 100644 couchpotato/core/plugins/movie/static/movie.actions.js create mode 100644 couchpotato/static/images/icon.readd.png diff --git a/couchpotato/core/plugins/dashboard/main.py b/couchpotato/core/plugins/dashboard/main.py index d5f9ef0..814b9d6 100644 --- a/couchpotato/core/plugins/dashboard/main.py +++ b/couchpotato/core/plugins/dashboard/main.py @@ -8,6 +8,7 @@ from couchpotato.core.plugins.base import Plugin from couchpotato.core.settings.model import Movie from sqlalchemy.sql.expression import or_ import random +import time log = CPLog(__name__) @@ -41,7 +42,6 @@ class Dashboard(Plugin): identifiers = [m.library.identifier for m in movies] suggestions = fireEvent('movie.suggest', movies = identifiers, single = True) - print suggestions return jsonified({ 'result': True, @@ -52,6 +52,7 @@ class Dashboard(Plugin): params = getParams() db = get_session() + now = time.time() # Get profiles first, determine pre or post theater profiles = fireEvent('profile.all', single = True) @@ -92,7 +93,7 @@ class Dashboard(Plugin): movies = [] for movie in all_movies: pp = profile_pre.get(movie.profile.id) - eta = movie.library.info.get('release_date', {}) + eta = movie.library.info.get('release_date', {}) or {} coming_soon = False # Theater quality @@ -101,6 +102,7 @@ class Dashboard(Plugin): if pp.get('dvd') and fireEvent('searcher.could_be_released', False, eta, single = True): coming_soon = True + if coming_soon: temp = movie.to_dict({ 'profile': {'types': {}}, @@ -108,7 +110,11 @@ class Dashboard(Plugin): 'library': {'titles': {}, 'files':{}}, 'files': {}, }) - movies.append(temp) + + # Don't list older movies + if ((not params.get('late') and (not eta.get('dvd') or (eta.get('dvd') and eta.get('dvd') > (now - 2419200)))) or \ + (params.get('late') and eta.get('dvd') and eta.get('dvd') < (now - 2419200))): + movies.append(temp) if len(movies) >= limit: break @@ -118,3 +124,5 @@ class Dashboard(Plugin): 'empty': len(movies) == 0, 'movies': movies, }) + + getLateView = getSoonView diff --git a/couchpotato/core/plugins/movie/main.py b/couchpotato/core/plugins/movie/main.py index d8e7499..5ec968c 100644 --- a/couchpotato/core/plugins/movie/main.py +++ b/couchpotato/core/plugins/movie/main.py @@ -368,7 +368,9 @@ class MoviePlugin(Plugin): # Status status_active = fireEvent('status.add', 'active', single = True) - status_snatched = fireEvent('status.add', 'snatched', single = True) + snatched_status = fireEvent('status.add', 'snatched', single = True) + ignored_status = fireEvent('status.add', 'ignored', single = True) + downloaded_status = fireEvent('status.add', 'downloaded', single = True) default_profile = fireEvent('profile.default', single = True) @@ -392,10 +394,14 @@ class MoviePlugin(Plugin): fireEventAsync('library.update', params.get('identifier'), default_title = params.get('title', ''), on_complete = onComplete) search_after = False elif force_readd: + # Clean snatched history for release in m.releases: - if release.status_id == status_snatched.get('id'): - release.delete() + if release.status_id in [downloaded_status.get('id'), snatched_status.get('id')]: + if params.get('ignore_previous', False): + release.status_id = ignored_status.get('id') + else: + fireEvent('release.delete', release.id, single = True) m.profile_id = params.get('profile_id', default_profile.get('id')) else: diff --git a/couchpotato/core/plugins/movie/static/list.js b/couchpotato/core/plugins/movie/static/list.js index 9e76fad..6b3b8dd 100644 --- a/couchpotato/core/plugins/movie/static/list.js +++ b/couchpotato/core/plugins/movie/static/list.js @@ -29,6 +29,9 @@ var MovieList = new Class({ self.title = self.options.title ? new Element('h2', { 'text': self.options.title }) : null, + self.description = self.options.description ? new Element('div.description', { + 'html': self.options.description + }) : null, self.movie_list = new Element('div'), self.load_more = self.options.load_more ? new Element('a.load_more', { 'events': { @@ -121,18 +124,14 @@ var MovieList = new Class({ createMovie: function(movie, inject_at){ var self = this; - - // Attach proper actions - var a = self.options.actions, - status = Status.get(movie.status_id), - actions = a ? a[status.identifier.capitalize()] || a.Wanted : {}; - var m = new Movie(self, { - 'actions': actions, + 'actions': self.options.actions, 'view': self.current_view, 'onSelect': self.calculateSelected.bind(self) }, movie); + $(m).inject(self.movie_list, inject_at || 'bottom'); + m.fireEvent('injected'); self.movies.include(m) @@ -398,8 +397,11 @@ var MovieList = new Class({ var self = this; self.movies = [] - self.calculateSelected() - self.navigation_alpha.getElements('.active').removeClass('active') + if(self.mass_edit_select) + self.calculateSelected() + if(self.navigation_alpha) + self.navigation_alpha.getElements('.active').removeClass('active') + self.offset = 0; if(self.scrollspy){ self.load_more.show(); @@ -506,7 +508,7 @@ var MovieList = new Class({ var self = this; var is_empty = self.movies.length == 0 && self.total_movies == 0; - + if(self.title) self.title[is_empty ? 'hide' : 'show']() diff --git a/couchpotato/core/plugins/movie/static/movie.actions.js b/couchpotato/core/plugins/movie/static/movie.actions.js new file mode 100644 index 0000000..a56e9ab --- /dev/null +++ b/couchpotato/core/plugins/movie/static/movie.actions.js @@ -0,0 +1,699 @@ +var MovieAction = new Class({ + + class_name: 'action icon', + + initialize: function(movie){ + var self = this; + self.movie = movie; + + self.create(); + if(self.el) + self.el.addClass(self.class_name) + }, + + create: function(){}, + + disable: function(){ + this.el.addClass('disable') + }, + + enable: function(){ + this.el.removeClass('disable') + }, + + createMask: function(){ + var self = this; + self.mask = new Element('div.mask', { + 'styles': { + 'z-index': '1' + } + }).inject(self.movie, 'top').fade('hide'); + //self.positionMask(); + }, + + positionMask: function(){ + var self = this, + movie = $(self.movie), + s = movie.getSize() + + return; + + return self.mask.setStyles({ + 'width': s.x, + 'height': s.y + }).position({ + 'relativeTo': movie + }) + }, + + toElement: function(){ + return this.el || null + } + +}); + +var MA = {}; + +MA.IMDB = new Class({ + + Extends: MovieAction, + id: null, + + create: function(){ + var self = this; + + self.id = self.movie.get('identifier'); + + self.el = new Element('a.imdb', { + 'title': 'Go to the IMDB page of ' + self.movie.getTitle(), + 'href': 'http://www.imdb.com/title/'+self.id+'/', + 'target': '_blank' + }); + + if(!self.id) self.disable(); + } + +}); + +MA.Release = new Class({ + + Extends: MovieAction, + + create: function(){ + var self = this; + + self.el = new Element('a.releases.icon.download', { + 'title': 'Show the releases that are available for ' + self.movie.getTitle(), + 'events': { + 'click': self.show.bind(self) + } + }); + + if(self.movie.data.releases.length == 0){ + self.el.hide() + } + else { + + var buttons_done = false; + + self.movie.data.releases.sortBy('-info.score').each(function(release){ + if(buttons_done) return; + + var status = Status.get(release.status_id); + + if((self.next_release && (status.identifier == 'ignored' || status.identifier == 'failed')) || (!self.next_release && status.identifier == 'available')){ + self.hide_on_click = false; + self.show(); + buttons_done = true; + } + + }); + + } + + }, + + show: function(e){ + var self = this; + if(e) + (e).preventDefault(); + + if(!self.options_container){ + self.options_container = new Element('div.options').adopt( + self.release_container = new Element('div.releases.table').adopt( + self.trynext_container = new Element('div.buttons.try_container') + ) + ).inject(self.movie, 'top'); + + // Header + new Element('div.item.head').adopt( + new Element('span.name', {'text': 'Release name'}), + new Element('span.status', {'text': 'Status'}), + new Element('span.quality', {'text': 'Quality'}), + new Element('span.size', {'text': 'Size'}), + new Element('span.age', {'text': 'Age'}), + new Element('span.score', {'text': 'Score'}), + new Element('span.provider', {'text': 'Provider'}) + ).inject(self.release_container) + + self.movie.data.releases.sortBy('-info.score').each(function(release){ + + var status = Status.get(release.status_id), + quality = Quality.getProfile(release.quality_id) || {}, + info = release.info, + provider = self.get(release, 'provider') + (release.info['provider_extra'] ? self.get(release, 'provider_extra') : ''); + release.status = status; + + var release_name = self.get(release, 'name'); + if(release.files && release.files.length > 0){ + try { + var movie_file = release.files.filter(function(file){ + var type = File.Type.get(file.type_id); + return type && type.identifier == 'movie' + }).pick(); + release_name = movie_file.path.split(Api.getOption('path_sep')).getLast(); + } + catch(e){} + } + + // Create release + new Element('div', { + 'class': 'item '+status.identifier, + 'id': 'release_'+release.id + }).adopt( + new Element('span.name', {'text': release_name, 'title': release_name}), + new Element('span.status', {'text': status.identifier, 'class': 'release_status '+status.identifier}), + new Element('span.quality', {'text': quality.get('label') || 'n/a'}), + new Element('span.size', {'text': release.info['size'] ? Math.floor(self.get(release, 'size')) : 'n/a'}), + new Element('span.age', {'text': self.get(release, 'age')}), + new Element('span.score', {'text': self.get(release, 'score')}), + new Element('span.provider', { 'text': provider, 'title': provider }), + release.info['detail_url'] ? new Element('a.info.icon', { + 'href': release.info['detail_url'], + 'target': '_blank' + }) : null, + new Element('a.download.icon', { + 'events': { + 'click': function(e){ + (e).preventDefault(); + if(!this.hasClass('completed')) + self.download(release); + } + } + }), + new Element('a.delete.icon', { + 'events': { + 'click': function(e){ + (e).preventDefault(); + self.ignore(release); + this.getParent('.item').toggleClass('ignored') + } + } + }) + ).inject(self.release_container) + + if(status.identifier == 'ignored' || status.identifier == 'failed' || status.identifier == 'snatched'){ + if(!self.last_release || (self.last_release && self.last_release.status.identifier != 'snatched' && status.identifier == 'snatched')) + self.last_release = release; + } + else if(!self.next_release && status.identifier == 'available'){ + self.next_release = release; + } + }); + + if(self.last_release){ + self.release_container.getElement('#release_'+self.last_release.id).addClass('last_release'); + } + + if(self.next_release){ + self.release_container.getElement('#release_'+self.next_release.id).addClass('next_release'); + } + + if(self.next_release || self.last_release){ + + self.trynext_container.adopt( + new Element('span.or', { + 'text': 'This movie is snatched, if anything went wrong, download' + }), + self.last_release ? new Element('a.button.orange', { + 'text': 'the same release again', + 'events': { + 'click': self.trySameRelease.bind(self) + } + }) : null, + self.next_release && self.last_release ? new Element('span.or', { + 'text': ',' + }) : null, + self.next_release ? [new Element('a.button.green', { + 'text': self.last_release ? 'another release' : 'the best release', + 'events': { + 'click': self.tryNextRelease.bind(self) + } + }), + new Element('span.or', { + 'text': 'or pick one below' + })] : null + ) + } + + } + + self.movie.slide('in', self.options_container); + }, + + get: function(release, type){ + return release.info[type] || 'n/a' + }, + + download: function(release){ + var self = this; + + var release_el = self.release_container.getElement('#release_'+release.id), + icon = release_el.getElement('.download.icon'); + + icon.addClass('spinner'); + + Api.request('release.download', { + 'data': { + 'id': release.id + }, + 'onComplete': function(json){ + icon.removeClass('spinner') + if(json.success) + icon.addClass('completed'); + else + icon.addClass('attention').set('title', 'Something went wrong when downloading, please check logs.'); + } + }); + }, + + ignore: function(release){ + var self = this; + + Api.request('release.ignore', { + 'data': { + 'id': release.id + } + }) + + }, + + tryNextRelease: function(movie_id){ + var self = this; + + if(self.last_release) + self.ignore(self.last_release); + + if(self.next_release) + self.download(self.next_release); + + }, + + trySameRelease: function(movie_id){ + var self = this; + + if(self.last_release) + self.download(self.last_release); + + } + +}); + +MA.Trailer = new Class({ + + Extends: MovieAction, + id: null, + + create: function(){ + var self = this; + + self.el = new Element('a.trailer', { + 'title': 'Watch the trailer of ' + self.movie.getTitle(), + 'events': { + 'click': self.watch.bind(self) + } + }); + + }, + + watch: function(offset){ + var self = this; + + var data_url = 'http://gdata.youtube.com/feeds/videos?vq="{title}" {year} trailer&max-results=1&alt=json-in-script&orderby=relevance&sortorder=descending&format=5&fmt=18' + var url = data_url.substitute({ + 'title': encodeURI(self.movie.getTitle()), + 'year': self.movie.get('year'), + 'offset': offset || 1 + }), + size = $(self.movie).getSize(), + height = (size.x/16)*9, + id = 'trailer-'+randomString(); + + self.player_container = new Element('div[id='+id+']'); + self.container = new Element('div.hide.trailer_container') + .adopt(self.player_container) + .inject($(self.movie), 'top'); + + self.container.setStyle('height', 0); + self.container.removeClass('hide'); + + self.close_button = new Element('a.hide.hide_trailer', { + 'text': 'Hide trailer', + 'events': { + 'click': self.stop.bind(self) + } + }).inject(self.movie); + + self.container.setStyle('height', height); + $(self.movie).setStyle('height', height); + + new Request.JSONP({ + 'url': url, + 'onComplete': function(json){ + var video_url = json.feed.entry[0].id.$t.split('/'), + video_id = video_url[video_url.length-1]; + + self.player = new YT.Player(id, { + 'height': height, + 'width': size.x, + 'videoId': video_id, + 'playerVars': { + 'autoplay': 1, + 'showsearch': 0, + 'wmode': 'transparent', + 'iv_load_policy': 3 + } + }); + + self.close_button.removeClass('hide'); + + var quality_set = false; + var change_quality = function(state){ + if(!quality_set && (state.data == 1 || state.data || 2)){ + try { + self.player.setPlaybackQuality('hd720'); + quality_set = true; + } + catch(e){ + + } + } + } + self.player.addEventListener('onStateChange', change_quality); + + } + }).send() + + }, + + stop: function(){ + var self = this; + + self.player.stopVideo(); + self.container.addClass('hide'); + self.close_button.addClass('hide'); + $(self.movie).setStyle('height', null); + + setTimeout(function(){ + self.container.destroy() + self.close_button.destroy(); + }, 1800) + } + + +}); + +MA.Edit = new Class({ + + Extends: MovieAction, + + create: function(){ + var self = this; + + self.el = new Element('a.edit', { + 'title': 'Change movie information, like title and quality.', + 'events': { + 'click': self.editMovie.bind(self) + } + }); + + }, + + editMovie: function(e){ + var self = this; + (e).preventDefault(); + + if(!self.options_container){ + self.options_container = new Element('div.options').adopt( + new Element('div.form').adopt( + self.title_select = new Element('select', { + 'name': 'title' + }), + self.profile_select = new Element('select', { + 'name': 'profile' + }), + new Element('a.button.edit', { + 'text': 'Save & Search', + 'events': { + 'click': self.save.bind(self) + } + }) + ) + ).inject(self.movie, 'top'); + + Array.each(self.movie.data.library.titles, function(alt){ + new Element('option', { + 'text': alt.title + }).inject(self.title_select); + + if(alt['default']) + self.title_select.set('value', alt.title); + }); + + + Quality.getActiveProfiles().each(function(profile){ + + var profile_id = profile.id ? profile.id : profile.data.id; + + new Element('option', { + 'value': profile_id, + 'text': profile.label ? profile.label : profile.data.label + }).inject(self.profile_select); + + if(self.movie.profile && self.movie.profile.data && self.movie.profile.data.id == profile_id) + self.profile_select.set('value', profile_id); + }); + + } + + self.movie.slide('in', self.options_container); + }, + + save: function(e){ + (e).preventDefault(); + var self = this; + + Api.request('movie.edit', { + 'data': { + 'id': self.movie.get('id'), + 'default_title': self.title_select.get('value'), + 'profile_id': self.profile_select.get('value') + }, + 'useSpinner': true, + 'spinnerTarget': $(self.movie), + 'onComplete': function(){ + self.movie.quality.set('text', self.profile_select.getSelected()[0].get('text')); + self.movie.title.set('text', self.title_select.getSelected()[0].get('text')); + } + }); + + self.movie.slide('out'); + } + +}) + +MA.Refresh = new Class({ + + Extends: MovieAction, + + create: function(){ + var self = this; + + self.el = new Element('a.refresh', { + 'title': 'Refresh the movie info and do a forced search', + 'events': { + 'click': self.doRefresh.bind(self) + } + }); + + }, + + doRefresh: function(e){ + var self = this; + (e).preventDefault(); + + Api.request('movie.refresh', { + 'data': { + 'id': self.movie.get('id') + } + }); + } + +}); + +MA.Readd = new Class({ + + Extends: MovieAction, + + create: function(){ + var self = this; + + var movie_done = Status.get(self.movie.data.status_id).identifier == 'done'; + if(!movie_done) + var snatched = self.movie.data.releases.filter(function(release){ + return release.status && (release.status.identifier == 'snatched' || release.status.identifier == 'downloaded' || release.status.identifier == 'done'); + }).length; + + if(movie_done || snatched && snatched > 0) + self.el = new Element('a.readd', { + 'title': 'Readd the movie and mark all previous snatched/downloaded as ignored', + 'events': { + 'click': self.doReadd.bind(self) + } + }); + + }, + + doReadd: function(e){ + var self = this; + (e).preventDefault(); + + Api.request('movie.add', { + 'data': { + 'identifier': self.movie.get('identifier'), + 'ignore_previous': 1 + } + }); + } + +}); + +MA.Delete = new Class({ + + Extends: MovieAction, + + Implements: [Chain], + + create: function(){ + var self = this; + + self.el = new Element('a.delete', { + 'title': 'Remove the movie from this CP list', + 'events': { + 'click': self.showConfirm.bind(self) + } + }); + + }, + + showConfirm: function(e){ + var self = this; + (e).preventDefault(); + + if(!self.delete_container){ + self.delete_container = new Element('div.buttons.delete_container').adopt( + new Element('a.cancel', { + 'text': 'Cancel', + 'events': { + 'click': self.hideConfirm.bind(self) + } + }), + new Element('span.or', { + 'text': 'or' + }), + new Element('a.button.delete', { + 'text': 'Delete ' + self.movie.title.get('text'), + 'events': { + 'click': self.del.bind(self) + } + }) + ).inject(self.movie, 'top'); + } + + self.movie.slide('in', self.delete_container); + + }, + + hideConfirm: function(e){ + var self = this; + (e).preventDefault(); + + self.movie.slide('out'); + }, + + del: function(e){ + (e).preventDefault(); + var self = this; + + var movie = $(self.movie); + + self.chain( + function(){ + self.callChain(); + }, + function(){ + Api.request('movie.delete', { + 'data': { + 'id': self.movie.get('id'), + 'delete_from': self.movie.list.options.identifier + }, + 'onComplete': function(){ + movie.set('tween', { + 'duration': 300, + 'onComplete': function(){ + self.movie.destroy() + } + }); + movie.tween('height', 0); + } + }); + } + ); + + self.callChain(); + + } + +}); + +MA.Files = new Class({ + + Extends: MovieAction, + + create: function(){ + var self = this; + + self.el = new Element('a.directory', { + 'title': 'Available files', + 'events': { + 'click': self.showFiles.bind(self) + } + }); + + }, + + showFiles: function(e){ + var self = this; + (e).preventDefault(); + + if(!self.options_container){ + self.options_container = new Element('div.options').adopt( + self.files_container = new Element('div.files.table') + ).inject(self.movie, 'top'); + + // Header + new Element('div.item.head').adopt( + new Element('span.name', {'text': 'File'}), + new Element('span.type', {'text': 'Type'}), + new Element('span.is_available', {'text': 'Available'}) + ).inject(self.files_container) + + Array.each(self.movie.data.releases, function(release){ + + var rel = new Element('div.release').inject(self.files_container); + + Array.each(release.files, function(file){ + new Element('div.file.item').adopt( + new Element('span.name', {'text': file.path}), + new Element('span.type', {'text': File.Type.get(file.type_id).name}), + new Element('span.available', {'text': file.available}) + ).inject(rel) + }); + }); + + } + + self.movie.slide('in', self.options_container); + }, + +}); \ No newline at end of file diff --git a/couchpotato/core/plugins/movie/static/movie.css b/couchpotato/core/plugins/movie/static/movie.css index 9d67e62..0b97f68 100644 --- a/couchpotato/core/plugins/movie/static/movie.css +++ b/couchpotato/core/plugins/movie/static/movie.css @@ -1,11 +1,24 @@ .movies { padding: 60px 0 20px; + position: relative; } .movies h2 { margin-bottom: 20px; } + .movies > .description { + position: absolute; + top: 30px; + right: 0; + font-style: italic; + text-shadow: none; + opacity: 0.8; + } + .movies:hover > .description { + opacity: 1; + } + .movies.thumbs_list { padding: 20px 0 20px; } @@ -303,6 +316,11 @@ line-height: 0; margin-top: -25px; } + .movies.thumbs_list .data .actions { + bottom: 8px; + right: 10px; + } + .movies .data:hover .action { opacity: 0.6; } .movies .data:hover .action:hover { opacity: 1; } .movies.mass_edit_list .data .actions { diff --git a/couchpotato/core/plugins/movie/static/movie.js b/couchpotato/core/plugins/movie/static/movie.js index 283bb41..37ed0ad 100644 --- a/couchpotato/core/plugins/movie/static/movie.js +++ b/couchpotato/core/plugins/movie/static/movie.js @@ -80,7 +80,7 @@ var Movie = new Class({ var self = this; self.mask = new Element('div.mask', { 'styles': { - 'z-index': '1' + 'z-index': '1000' } }).inject(self.el, 'top').fade('hide'); }, @@ -290,395 +290,4 @@ var Movie = new Class({ return this.el; } -}); - -var MovieAction = new Class({ - - class_name: 'action icon', - - initialize: function(movie){ - var self = this; - self.movie = movie; - - self.create(); - if(self.el) - self.el.addClass(self.class_name) - }, - - create: function(){}, - - disable: function(){ - this.el.addClass('disable') - }, - - enable: function(){ - this.el.removeClass('disable') - }, - - createMask: function(){ - var self = this; - self.mask = new Element('div.mask', { - 'styles': { - 'z-index': '1' - } - }).inject(self.movie, 'top').fade('hide'); - //self.positionMask(); - }, - - positionMask: function(){ - var self = this, - movie = $(self.movie), - s = movie.getSize() - - return; - - return self.mask.setStyles({ - 'width': s.x, - 'height': s.y - }).position({ - 'relativeTo': movie - }) - }, - - toElement: function(){ - return this.el || null - } - -}); - -var IMDBAction = new Class({ - - Extends: MovieAction, - id: null, - - create: function(){ - var self = this; - - self.id = self.movie.get('identifier'); - - self.el = new Element('a.imdb', { - 'title': 'Go to the IMDB page of ' + self.movie.getTitle(), - 'href': 'http://www.imdb.com/title/'+self.id+'/', - 'target': '_blank' - }); - - if(!self.id) self.disable(); - } - -}); - -var ReleaseAction = new Class({ - - Extends: MovieAction, - - create: function(){ - var self = this; - - self.el = new Element('a.releases.icon.download', { - 'title': 'Show the releases that are available for ' + self.movie.getTitle(), - 'events': { - 'click': self.show.bind(self) - } - }); - - if(self.movie.data.releases.length == 0){ - self.el.hide() - } - else { - - var buttons_done = false; - - self.movie.data.releases.sortBy('-info.score').each(function(release){ - if(buttons_done) return; - - var status = Status.get(release.status_id); - - if((self.next_release && (status.identifier == 'ignored' || status.identifier == 'failed')) || (!self.next_release && status.identifier == 'available')){ - self.hide_on_click = false; - self.show(); - buttons_done = true; - } - - }); - - } - - }, - - show: function(e){ - var self = this; - if(e) - (e).preventDefault(); - - if(!self.options_container){ - self.options_container = new Element('div.options').adopt( - self.release_container = new Element('div.releases.table').adopt( - self.trynext_container = new Element('div.buttons.try_container') - ) - ).inject(self.movie, 'top'); - - // Header - new Element('div.item.head').adopt( - new Element('span.name', {'text': 'Release name'}), - new Element('span.status', {'text': 'Status'}), - new Element('span.quality', {'text': 'Quality'}), - new Element('span.size', {'text': 'Size'}), - new Element('span.age', {'text': 'Age'}), - new Element('span.score', {'text': 'Score'}), - new Element('span.provider', {'text': 'Provider'}) - ).inject(self.release_container) - - self.movie.data.releases.sortBy('-info.score').each(function(release){ - - var status = Status.get(release.status_id), - quality = Quality.getProfile(release.quality_id) || {}, - info = release.info, - provider = self.get(release, 'provider') + (release.info['provider_extra'] ? self.get(release, 'provider_extra') : ''); - release.status = status; - - // Create release - new Element('div', { - 'class': 'item '+status.identifier, - 'id': 'release_'+release.id - }).adopt( - new Element('span.name', {'text': self.get(release, 'name'), 'title': self.get(release, 'name')}), - new Element('span.status', {'text': status.identifier, 'class': 'release_status '+status.identifier}), - new Element('span.quality', {'text': quality.get('label') || 'n/a'}), - new Element('span.size', {'text': release.info['size'] ? Math.floor(self.get(release, 'size')) : 'n/a'}), - new Element('span.age', {'text': self.get(release, 'age')}), - new Element('span.score', {'text': self.get(release, 'score')}), - new Element('span.provider', { 'text': provider, 'title': provider }), - release.info['detail_url'] ? new Element('a.info.icon', { - 'href': release.info['detail_url'], - 'target': '_blank' - }) : null, - new Element('a.download.icon', { - 'events': { - 'click': function(e){ - (e).preventDefault(); - if(!this.hasClass('completed')) - self.download(release); - } - } - }), - new Element('a.delete.icon', { - 'events': { - 'click': function(e){ - (e).preventDefault(); - self.ignore(release); - this.getParent('.item').toggleClass('ignored') - } - } - }) - ).inject(self.release_container) - - if(status.identifier == 'ignored' || status.identifier == 'failed' || status.identifier == 'snatched'){ - if(!self.last_release || (self.last_release && self.last_release.status.identifier != 'snatched' && status.identifier == 'snatched')) - self.last_release = release; - } - else if(!self.next_release && status.identifier == 'available'){ - self.next_release = release; - } - }); - - if(self.last_release){ - self.release_container.getElement('#release_'+self.last_release.id).addClass('last_release'); - } - - if(self.next_release){ - self.release_container.getElement('#release_'+self.next_release.id).addClass('next_release'); - } - - if(self.next_release || self.last_release){ - - self.trynext_container.adopt( - new Element('span.or', { - 'text': 'This movie is snatched, if anything went wrong, download' - }), - self.last_release ? new Element('a.button.orange', { - 'text': 'the same release again', - 'events': { - 'click': self.trySameRelease.bind(self) - } - }) : null, - self.next_release && self.last_release ? new Element('span.or', { - 'text': ',' - }) : null, - self.next_release ? [new Element('a.button.green', { - 'text': self.last_release ? 'another release' : 'the best release', - 'events': { - 'click': self.tryNextRelease.bind(self) - } - }), - new Element('span.or', { - 'text': 'or pick one below' - })] : null - ) - } - - } - - self.movie.slide('in', self.options_container); - }, - - get: function(release, type){ - return release.info[type] || 'n/a' - }, - - download: function(release){ - var self = this; - - var release_el = self.release_container.getElement('#release_'+release.id), - icon = release_el.getElement('.download.icon'); - - icon.addClass('spinner'); - - Api.request('release.download', { - 'data': { - 'id': release.id - }, - 'onComplete': function(json){ - icon.removeClass('spinner') - if(json.success) - icon.addClass('completed'); - else - icon.addClass('attention').set('title', 'Something went wrong when downloading, please check logs.'); - } - }); - }, - - ignore: function(release){ - var self = this; - - Api.request('release.ignore', { - 'data': { - 'id': release.id - } - }) - - }, - - tryNextRelease: function(movie_id){ - var self = this; - - if(self.last_release) - self.ignore(self.last_release); - - if(self.next_release) - self.download(self.next_release); - - }, - - trySameRelease: function(movie_id){ - var self = this; - - if(self.last_release) - self.download(self.last_release); - - } - -}); - -var TrailerAction = new Class({ - - Extends: MovieAction, - id: null, - - create: function(){ - var self = this; - - self.el = new Element('a.trailer', { - 'title': 'Watch the trailer of ' + self.movie.getTitle(), - 'events': { - 'click': self.watch.bind(self) - } - }); - - }, - - watch: function(offset){ - var self = this; - - var data_url = 'http://gdata.youtube.com/feeds/videos?vq="{title}" {year} trailer&max-results=1&alt=json-in-script&orderby=relevance&sortorder=descending&format=5&fmt=18' - var url = data_url.substitute({ - 'title': encodeURI(self.movie.getTitle()), - 'year': self.movie.get('year'), - 'offset': offset || 1 - }), - size = $(self.movie).getSize(), - height = (size.x/16)*9, - id = 'trailer-'+randomString(); - - self.player_container = new Element('div[id='+id+']'); - self.container = new Element('div.hide.trailer_container') - .adopt(self.player_container) - .inject($(self.movie), 'top'); - - self.container.setStyle('height', 0); - self.container.removeClass('hide'); - - self.close_button = new Element('a.hide.hide_trailer', { - 'text': 'Hide trailer', - 'events': { - 'click': self.stop.bind(self) - } - }).inject(self.movie); - - self.container.setStyle('height', height); - $(self.movie).setStyle('height', height); - - new Request.JSONP({ - 'url': url, - 'onComplete': function(json){ - var video_url = json.feed.entry[0].id.$t.split('/'), - video_id = video_url[video_url.length-1]; - - self.player = new YT.Player(id, { - 'height': height, - 'width': size.x, - 'videoId': video_id, - 'playerVars': { - 'autoplay': 1, - 'showsearch': 0, - 'wmode': 'transparent', - 'iv_load_policy': 3 - } - }); - - self.close_button.removeClass('hide'); - - var quality_set = false; - var change_quality = function(state){ - if(!quality_set && (state.data == 1 || state.data || 2)){ - try { - self.player.setPlaybackQuality('hd720'); - quality_set = true; - } - catch(e){ - - } - } - } - self.player.addEventListener('onStateChange', change_quality); - - } - }).send() - - }, - - stop: function(){ - var self = this; - - self.player.stopVideo(); - self.container.addClass('hide'); - self.close_button.addClass('hide'); - $(self.movie).setStyle('height', null); - - setTimeout(function(){ - self.container.destroy() - self.close_button.destroy(); - }, 1800) - } - - }); \ No newline at end of file diff --git a/couchpotato/core/plugins/searcher/main.py b/couchpotato/core/plugins/searcher/main.py index deb3544..0285917 100644 --- a/couchpotato/core/plugins/searcher/main.py +++ b/couchpotato/core/plugins/searcher/main.py @@ -165,7 +165,7 @@ class Searcher(Plugin): # See if better quality is available for release in movie['releases']: - if release['quality']['order'] <= quality_type['quality']['order'] and release['status_id'] not in [available_status.get('id'), ignored_status.get('id')]: + if release['quality']['order'] < quality_type['quality']['order'] and release['status_id'] not in [available_status.get('id'), ignored_status.get('id')]: has_better_quality += 1 # Don't search for quality lower then already available. @@ -294,7 +294,10 @@ class Searcher(Plugin): db = get_session() rls = db.query(Release).filter_by(identifier = md5(data['url'])).first() if rls: - rls.status_id = snatched_status.get('id') + renamer_enabled = Env.setting('enabled', 'renamer') + + done_status = fireEvent('status.get', 'done', single = True) + rls.status_id = done_status.get('id') if not renamer_enabled else snatched_status.get('id') db.commit() log_movie = '%s (%s) in %s' % (getTitle(movie['library']), movie['library']['year'], rls.quality.label) @@ -303,7 +306,7 @@ class Searcher(Plugin): fireEvent('movie.snatched', message = snatch_message, data = rls.to_dict()) # If renamer isn't used, mark movie done - if not Env.setting('enabled', 'renamer'): + if not renamer_enabled: active_status = fireEvent('status.get', 'active', single = True) done_status = fireEvent('status.get', 'done', single = True) try: diff --git a/couchpotato/static/images/icon.readd.png b/couchpotato/static/images/icon.readd.png new file mode 100644 index 0000000000000000000000000000000000000000..dacb432f2c6685ae1eb2dd95a73e3adeeeac6f11 GIT binary patch literal 778 zcmY+CZAepL6vvNu(@mYV%H>O;U=Bf%X+kE!#>~ilNK8V%Skqg!AV>;KQ>WN+p(chH zhHopw`d}))N2HrhDKV8&3ZtAW%WS&Mz1@4a-FtJc4|d==2cE1kOzk?T2Zza1ZGrSk!h50W(Tg_1Tj?};Y`G~F5M^It^MLRMS8_jFIkrieSv zP1P!D>J?Rbk=CV?s)RUs72DDY;wsj+%4=Rm>=vR;E#U)ZEo~as-5L=#sv<63pXkae zi0+}98&3YYO$q|XC}~8As~iv8gV%YWItygB=h5e&1QkYW`b>p4ctx_Fh~G5<^;E$0JakW9LK>74hUg! z!y{|X-}7*GjHY){Ov36wfTX}+H-Q!p!?wm?2lY*2)HRJ*}BcCYz^_ z)J1&qFF1T4m=)+|aJkj#*}EvzN&Gyr*jy+L6Uxa+^M*mD?9+`Wx}8pi-aebd5iF~* z$yC>ZN+jyYxiLQ$?Ovgc3YVx-PE2Ts?Ly=qh1`l+tdA)UGme!^nSW_dDMuq$5_8M$ zsH$4q6=IvwH2r&Ht!&xYUd*c?)aCZZDuNUgdF0VF+Em+L8a=7u*Y%orzMi9_$Sbc3 z=Xjcjqq1YK-(>cau@p<4>K$uT{5KRi;VtZ30?#h*Sz87SWF8F`FQm#m_mVRiX}zg= GcmD!)?tHBP literal 0 HcmV?d00001 diff --git a/couchpotato/static/scripts/page/home.js b/couchpotato/static/scripts/page/home.js index caf4d64..c9f3344 100644 --- a/couchpotato/static/scripts/page/home.js +++ b/couchpotato/static/scripts/page/home.js @@ -8,8 +8,14 @@ Page.Home = new Class({ indexAction: function(param){ var self = this; - if(self.soon_list) + if(self.soon_list){ + + // Reset lists + self.available_list.update(); + self.late_list.update(); + return + } // Snatched self.available_list = new MovieList({ @@ -17,48 +23,54 @@ Page.Home = new Class({ 'identifier': 'snatched', 'load_more': false, 'view': 'list', - 'actions': MovieActions, + 'actions': [MA.IMDB, MA.Trailer, MA.Files, MA.Release, MA.Edit, MA.Readd, MA.Refresh, MA.Delete], 'title': 'Snatched & Available', + 'on_empty_element': new Element('div'), 'filter': { 'release_status': 'snatched,available' } }); - // Downloaded - // self.downloaded_list = new MovieList({ - // 'navigation': false, - // 'identifier': 'downloaded', - // 'load_more': false, - // 'view': 'titles', - // 'filter': { - // 'release_status': 'done', - // 'order': 'release_order' - // } - // }); - // self.el.adopt( - // new Element('h2', { - // 'text': 'Just downloaded' - // }), - // $(self.downloaded_list) - // ); - - // Comming Soon + // Coming Soon self.soon_list = new MovieList({ 'navigation': false, 'identifier': 'soon', - 'limit': 24, - 'title': 'Soon', + 'limit': 18, + 'title': 'Available soon', + 'description': 'These are being searches for and should be available soon as they will be released on DVD in the next few weeks.', + 'on_empty_element': new Element('div').grab( + new Element('span', {'text': 'There are no movies available soon'}) + ), 'filter': { 'random': true }, + 'actions': [MA.IMDB, MA.Refresh], 'load_more': false, 'view': 'thumbs', 'api_call': 'dashboard.soon' }); + // Still not available + self.late_list = new MovieList({ + 'navigation': false, + 'identifier': 'late', + 'limit': 50, + 'title': 'Still not available', + 'description': 'Try another quality profile or maybe add more providers in Settings.', + 'on_empty_element': new Element('div'), + 'filter': { + 'late': true + }, + 'load_more': false, + 'view': 'list', + 'actions': [MA.IMDB, MA.Trailer, MA.Edit, MA.Refresh, MA.Delete], + 'api_call': 'dashboard.soon' + }); + self.el.adopt( $(self.available_list), - $(self.soon_list) + $(self.soon_list), + $(self.late_list) ); // Suggest diff --git a/couchpotato/static/scripts/page/manage.js b/couchpotato/static/scripts/page/manage.js index ec293f8..aef1f3c 100644 --- a/couchpotato/static/scripts/page/manage.js +++ b/couchpotato/static/scripts/page/manage.js @@ -30,7 +30,7 @@ Page.Manage = new Class({ 'filter': { 'release_status': 'done' }, - 'actions': MovieActions, + 'actions': [MA.IMDB, MA.Trailer, MA.Files, MA.Readd, MA.Edit, MA.Delete], 'menu': [self.refresh_button, self.refresh_quick], 'on_empty_element': new Element('div.empty_manage').adopt( new Element('div', { diff --git a/couchpotato/static/scripts/page/wanted.js b/couchpotato/static/scripts/page/wanted.js index a9e0bc7..6e32997 100644 --- a/couchpotato/static/scripts/page/wanted.js +++ b/couchpotato/static/scripts/page/wanted.js @@ -22,7 +22,7 @@ Page.Wanted = new Class({ self.wanted = new MovieList({ 'identifier': 'wanted', 'status': 'active', - 'actions': MovieActions, + 'actions': [MA.IMDB, MA.Trailer, MA.Release, MA.Edit, MA.Refresh, MA.Readd, MA.Delete], 'add_new': true, 'menu': [self.manual_search], 'on_empty_element': App.createUserscriptButtons().addClass('empty_wanted') @@ -71,283 +71,4 @@ Page.Wanted = new Class({ } -}); - -var MovieActions = {}; -window.addEvent('domready', function(){ - - MovieActions.Wanted = { - 'IMDB': IMDBAction - ,'Trailer': TrailerAction - ,'Releases': ReleaseAction - ,'Edit': new Class({ - - Extends: MovieAction, - - create: function(){ - var self = this; - - self.el = new Element('a.edit', { - 'title': 'Change movie information, like title and quality.', - 'events': { - 'click': self.editMovie.bind(self) - } - }); - - }, - - editMovie: function(e){ - var self = this; - (e).preventDefault(); - - if(!self.options_container){ - self.options_container = new Element('div.options').adopt( - new Element('div.form').adopt( - self.title_select = new Element('select', { - 'name': 'title' - }), - self.profile_select = new Element('select', { - 'name': 'profile' - }), - new Element('a.button.edit', { - 'text': 'Save & Search', - 'events': { - 'click': self.save.bind(self) - } - }) - ) - ).inject(self.movie, 'top'); - - Array.each(self.movie.data.library.titles, function(alt){ - new Element('option', { - 'text': alt.title - }).inject(self.title_select); - - if(alt['default']) - self.title_select.set('value', alt.title); - }); - - - Quality.getActiveProfiles().each(function(profile){ - - var profile_id = profile.id ? profile.id : profile.data.id; - - new Element('option', { - 'value': profile_id, - 'text': profile.label ? profile.label : profile.data.label - }).inject(self.profile_select); - - if(self.movie.profile && self.movie.profile.data && self.movie.profile.data.id == profile_id) - self.profile_select.set('value', profile_id); - }); - - } - - self.movie.slide('in', self.options_container); - }, - - save: function(e){ - (e).preventDefault(); - var self = this; - - Api.request('movie.edit', { - 'data': { - 'id': self.movie.get('id'), - 'default_title': self.title_select.get('value'), - 'profile_id': self.profile_select.get('value') - }, - 'useSpinner': true, - 'spinnerTarget': $(self.movie), - 'onComplete': function(){ - self.movie.quality.set('text', self.profile_select.getSelected()[0].get('text')); - self.movie.title.set('text', self.title_select.getSelected()[0].get('text')); - } - }); - - self.movie.slide('out'); - } - - }) - - ,'Refresh': new Class({ - - Extends: MovieAction, - - create: function(){ - var self = this; - - self.el = new Element('a.refresh', { - 'title': 'Refresh the movie info and do a forced search', - 'events': { - 'click': self.doRefresh.bind(self) - } - }); - - }, - - doRefresh: function(e){ - var self = this; - (e).preventDefault(); - - Api.request('movie.refresh', { - 'data': { - 'id': self.movie.get('id') - } - }); - } - - }) - - ,'Delete': new Class({ - - Extends: MovieAction, - - Implements: [Chain], - - create: function(){ - var self = this; - - self.el = new Element('a.delete', { - 'title': 'Remove the movie from this CP list', - 'events': { - 'click': self.showConfirm.bind(self) - } - }); - - }, - - showConfirm: function(e){ - var self = this; - (e).preventDefault(); - - if(!self.delete_container){ - self.delete_container = new Element('div.buttons.delete_container').adopt( - new Element('a.cancel', { - 'text': 'Cancel', - 'events': { - 'click': self.hideConfirm.bind(self) - } - }), - new Element('span.or', { - 'text': 'or' - }), - new Element('a.button.delete', { - 'text': 'Delete ' + self.movie.title.get('text'), - 'events': { - 'click': self.del.bind(self) - } - }) - ).inject(self.movie, 'top'); - } - - self.movie.slide('in', self.delete_container); - - }, - - hideConfirm: function(e){ - var self = this; - (e).preventDefault(); - - self.movie.slide('out'); - }, - - del: function(e){ - (e).preventDefault(); - var self = this; - - var movie = $(self.movie); - - self.chain( - function(){ - self.callChain(); - }, - function(){ - Api.request('movie.delete', { - 'data': { - 'id': self.movie.get('id'), - 'delete_from': self.movie.list.options.identifier - }, - 'onComplete': function(){ - movie.set('tween', { - 'duration': 300, - 'onComplete': function(){ - self.movie.destroy() - } - }); - movie.tween('height', 0); - } - }); - } - ); - - self.callChain(); - - } - - }) - }; - - MovieActions.Snatched = { - 'IMDB': IMDBAction - ,'Delete': MovieActions.Wanted.Delete - }; - - MovieActions.Done = { - 'IMDB': IMDBAction - ,'Edit': MovieActions.Wanted.Edit - ,'Trailer': TrailerAction - ,'Files': new Class({ - - Extends: MovieAction, - - create: function(){ - var self = this; - - self.el = new Element('a.directory', { - 'title': 'Available files', - 'events': { - 'click': self.showFiles.bind(self) - } - }); - - }, - - showFiles: function(e){ - var self = this; - (e).preventDefault(); - - if(!self.options_container){ - self.options_container = new Element('div.options').adopt( - self.files_container = new Element('div.files.table') - ).inject(self.movie, 'top'); - - // Header - new Element('div.item.head').adopt( - new Element('span.name', {'text': 'File'}), - new Element('span.type', {'text': 'Type'}), - new Element('span.is_available', {'text': 'Available'}) - ).inject(self.files_container) - - Array.each(self.movie.data.releases, function(release){ - - var rel = new Element('div.release').inject(self.files_container); - - Array.each(release.files, function(file){ - new Element('div.file.item').adopt( - new Element('span.name', {'text': file.path}), - new Element('span.type', {'text': File.Type.get(file.type_id).name}), - new Element('span.available', {'text': file.available}) - ).inject(rel) - }); - }); - - } - - self.movie.slide('in', self.options_container); - }, - - }) - ,'Delete': MovieActions.Wanted.Delete - }; - -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/couchpotato/static/style/main.css b/couchpotato/static/style/main.css index ff29222..ecb0468 100644 --- a/couchpotato/static/style/main.css +++ b/couchpotato/static/style/main.css @@ -157,6 +157,7 @@ body > .spinner, .mask{ .icon.folder { background-image: url('../images/icon.folder.png'); } .icon.imdb { background-image: url('../images/icon.imdb.png'); } .icon.refresh { background-image: url('../images/icon.refresh.png'); } +.icon.readd { background-image: url('../images/icon.readd.png'); } .icon.rating { background-image: url('../images/icon.rating.png'); } .icon.files { background-image: url('../images/icon.files.png'); } .icon.info { background-image: url('../images/icon.info.png'); }