Browse Source

Merge branch 'tv_interface' into tv

pull/3730/merge
Dean Gardiner 11 years ago
parent
commit
74caecbe89
  1. 40
      couchpotato/core/media/_base/library/main.py
  2. 2
      couchpotato/core/media/show/_base/static/1_wanted.js
  3. 71
      couchpotato/core/media/show/_base/static/episode.js
  4. 635
      couchpotato/core/media/show/_base/static/list.js
  5. 1077
      couchpotato/core/media/show/_base/static/show.css
  6. 103
      couchpotato/core/media/show/_base/static/show.episodes.js
  7. 347
      couchpotato/core/media/show/_base/static/show.js
  8. 13
      couchpotato/core/media/show/episode.py
  9. 4
      couchpotato/core/media/show/library/episode.py
  10. 4
      couchpotato/core/media/show/library/season.py
  11. 17
      couchpotato/core/media/show/providers/info/thetvdb.py
  12. 2
      couchpotato/core/media/show/searcher/episode.py
  13. 2
      couchpotato/core/media/show/season.py

40
couchpotato/core/media/_base/library/main.py

@ -11,10 +11,13 @@ class Library(LibraryBase):
def __init__(self):
addEvent('library.title', self.title)
addEvent('library.related', self.related)
addEvent('library.tree', self.tree)
addEvent('library.root', self.root)
addApiView('library.query', self.queryView)
addApiView('library.related', self.relatedView)
addApiView('library.tree', self.treeView)
def queryView(self, media_id, **kwargs):
db = get_db()
@ -32,6 +35,14 @@ class Library(LibraryBase):
'result': fireEvent('library.related', media, single = True)
}
def treeView(self, media_id, **kwargs):
db = get_db()
media = db.get('id', media_id)
return {
'result': fireEvent('library.tree', media, single = True)
}
def title(self, library):
return fireEvent(
'library.query',
@ -52,7 +63,9 @@ class Library(LibraryBase):
while cur and cur.get('parent_id'):
cur = db.get('id', cur['parent_id'])
result[cur['type']] = cur
parts = cur['type'].split('.')
result[parts[-1]] = cur
return result
@ -64,3 +77,28 @@ class Library(LibraryBase):
cur = db.get('id', cur['parent_id'])
return cur
def tree(self, media):
result = media
db = get_db()
items = db.get_many('media_children', media['_id'], with_doc = True)
keys = []
for item in items:
parts = item['doc']['type'].split('.')
key = parts[-1] + 's'
if key not in result:
result[key] = {}
if key not in keys:
keys.append(key)
result[key][item['_id']] = fireEvent('library.tree', item['doc'], single = True)
for key in keys:
result[key] = result[key].values()
return result

2
couchpotato/core/media/show/_base/static/1_wanted.js

@ -12,7 +12,7 @@ Page.Shows = new Class({
if(!self.wanted){
// Wanted movies
self.wanted = new MovieList({
self.wanted = new ShowList({
'identifier': 'wanted',
'status': 'active',
'type': 'show',

71
couchpotato/core/media/show/_base/static/episode.js

@ -0,0 +1,71 @@
var Episode = new Class({
Extends: BlockBase,
action: {},
initialize: function(show, data){
var self = this;
self.show = show;
self.data = data;
self.el = new Element('div.item');
self.el_actions = new Element('div.actions');
self.create();
},
create: function(){
var self = this;
self.el.set('id', 'episode_'+self.data._id);
self.el.adopt(
new Element('span.episode', {'text': (self.data.info.number || 0)}),
new Element('span.name', {'text': self.getTitle()}),
new Element('span.firstaired', {'text': self.data.info.firstaired})
);
self.el_actions.inject(self.el);
if(self.data.identifiers && self.data.identifiers.imdb) {
new Element('a.imdb.icon2', {
'title': 'Go to the IMDB page of ' + self.show.getTitle(),
'href': 'http://www.imdb.com/title/' + self.data.identifiers.imdb + '/',
'target': '_blank'
}).inject(self.el_actions);
}
new Element('a.refresh.icon2', {
'title': 'Refresh the episode info and do a forced search',
'events': {
'click': self.doRefresh.bind(self)
}
}).inject(self.el_actions);
},
getTitle: function(){
var self = this;
var title = '';
if(self.data.info.titles && self.data.info.titles.length > 0) {
title = self.data.info.titles[0];
} else {
title = 'Episode ' + self.data.info.number;
}
return title;
},
doRefresh: function(e) {
var self = this;
Api.request('media.refresh', {
'data': {
'id': self.data._id
}
});
}
});

635
couchpotato/core/media/show/_base/static/list.js

@ -0,0 +1,635 @@
var ShowList = new Class({
Implements: [Events, Options],
options: {
navigation: true,
limit: 50,
load_more: true,
loader: true,
menu: [],
add_new: false,
force_view: false
},
movies: [],
movies_added: {},
total_movies: 0,
letters: {},
filter: null,
initialize: function(options){
var self = this;
self.setOptions(options);
self.offset = 0;
self.filter = self.options.filter || {
'starts_with': null,
'search': null
};
self.el = new Element('div.shows').adopt(
self.title = self.options.title ? new Element('h2', {
'text': self.options.title,
'styles': {'display': 'none'}
}) : null,
self.description = self.options.description ? new Element('div.description', {
'html': self.options.description,
'styles': {'display': 'none'}
}) : null,
self.movie_list = new Element('div'),
self.load_more = self.options.load_more ? new Element('a.load_more', {
'events': {
'click': self.loadMore.bind(self)
}
}) : null
);
if($(window).getSize().x <= 480 && !self.options.force_view)
self.changeView('list');
else
self.changeView(self.getSavedView() || self.options.view || 'details');
self.getMovies();
App.on('movie.added', self.movieAdded.bind(self));
App.on('movie.deleted', self.movieDeleted.bind(self))
},
movieDeleted: function(notification){
var self = this;
if(self.movies_added[notification.data._id]){
self.movies.each(function(movie){
if(movie.get('_id') == notification.data._id){
movie.destroy();
delete self.movies_added[notification.data._id];
self.setCounter(self.counter_count-1);
self.total_movies--;
}
})
}
self.checkIfEmpty();
},
movieAdded: function(notification){
var self = this;
self.fireEvent('movieAdded', notification);
if(self.options.add_new && !self.movies_added[notification.data._id] && notification.data.status == self.options.status){
window.scroll(0,0);
self.createMovie(notification.data, 'top');
self.setCounter(self.counter_count+1);
self.checkIfEmpty();
}
},
create: function(){
var self = this;
// Create the alphabet nav
if(self.options.navigation)
self.createNavigation();
if(self.options.load_more)
self.scrollspy = new ScrollSpy({
min: function(){
var c = self.load_more.getCoordinates();
return c.top - window.document.getSize().y - 300
},
onEnter: self.loadMore.bind(self)
});
self.created = true;
},
addMovies: function(movies, total){
var self = this;
if(!self.created) self.create();
// do scrollspy
if(movies.length < self.options.limit && self.scrollspy){
self.load_more.hide();
self.scrollspy.stop();
}
Object.each(movies, function(movie){
self.createMovie(movie);
});
self.total_movies += total;
self.setCounter(total);
},
setCounter: function(count){
var self = this;
if(!self.navigation_counter) return;
self.counter_count = count;
self.navigation_counter.set('text', (count || 0) + ' movies');
if (self.empty_message) {
self.empty_message.destroy();
self.empty_message = null;
}
if(self.total_movies && count == 0 && !self.empty_message){
var message = (self.filter.search ? 'for "'+self.filter.search+'"' : '') +
(self.filter.starts_with ? ' in <strong>'+self.filter.starts_with+'</strong>' : '');
self.empty_message = new Element('.message', {
'html': 'No movies found ' + message + '.<br/>'
}).grab(
new Element('a', {
'text': 'Reset filter',
'events': {
'click': function(){
self.filter = {
'starts_with': null,
'search': null
};
self.navigation_search_input.set('value', '');
self.reset();
self.activateLetter();
self.getMovies(true);
self.last_search_value = '';
}
}
})
).inject(self.movie_list);
}
},
createMovie: function(movie, inject_at){
var self = this;
var m = new Show(self, {
'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);
self.movies_added[movie._id] = true;
},
createNavigation: function(){
var self = this;
var chars = '#ABCDEFGHIJKLMNOPQRSTUVWXYZ';
self.el.addClass('with_navigation');
self.navigation = new Element('div.alph_nav').adopt(
self.mass_edit_form = new Element('div.mass_edit_form').adopt(
new Element('span.select').adopt(
self.mass_edit_select = new Element('input[type=checkbox].inlay', {
'events': {
'change': self.massEditToggleAll.bind(self)
}
}),
self.mass_edit_selected = new Element('span.count', {'text': 0}),
self.mass_edit_selected_label = new Element('span', {'text': 'selected'})
),
new Element('div.quality').adopt(
self.mass_edit_quality = new Element('select'),
new Element('a.button.orange', {
'text': 'Change quality',
'events': {
'click': self.changeQualitySelected.bind(self)
}
})
),
new Element('div.delete').adopt(
new Element('span[text=or]'),
new Element('a.button.red', {
'text': 'Delete',
'events': {
'click': self.deleteSelected.bind(self)
}
})
),
new Element('div.refresh').adopt(
new Element('span[text=or]'),
new Element('a.button.green', {
'text': 'Refresh',
'events': {
'click': self.refreshSelected.bind(self)
}
})
)
),
new Element('div.menus').adopt(
self.navigation_counter = new Element('span.counter[title=Total]'),
self.filter_menu = new Block.Menu(self, {
'class': 'filter'
}),
self.navigation_actions = new Element('ul.actions', {
'events': {
'click:relay(li)': function(e, el){
var a = 'active';
self.navigation_actions.getElements('.'+a).removeClass(a);
self.changeView(el.get('data-view'));
this.addClass(a);
el.inject(el.getParent(), 'top');
el.getSiblings().hide();
setTimeout(function(){
el.getSiblings().setStyle('display', null);
}, 100)
}
}
}),
self.navigation_menu = new Block.Menu(self, {
'class': 'extra'
})
)
).inject(self.el, 'top');
// Mass edit
self.mass_edit_select_class = new Form.Check(self.mass_edit_select);
Quality.getActiveProfiles().each(function(profile){
new Element('option', {
'value': profile.get('_id'),
'text': profile.get('label')
}).inject(self.mass_edit_quality)
});
self.filter_menu.addLink(
self.navigation_search_input = new Element('input', {
'title': 'Search through ' + self.options.identifier,
'placeholder': 'Search through ' + self.options.identifier,
'events': {
'keyup': self.search.bind(self),
'change': self.search.bind(self)
}
})
).addClass('search');
var available_chars;
self.filter_menu.addEvent('open', function(){
self.navigation_search_input.focus();
// Get available chars and highlight
if(!available_chars && (self.navigation.isDisplayed() || self.navigation.isVisible()))
Api.request('media.available_chars', {
'data': Object.merge({
'status': self.options.status
}, self.filter),
'onSuccess': function(json){
available_chars = json.chars;
available_chars.each(function(c){
self.letters[c.capitalize()].addClass('available')
})
}
});
});
self.filter_menu.addLink(
self.navigation_alpha = new Element('ul.numbers', {
'events': {
'click:relay(li.available)': function(e, el){
self.activateLetter(el.get('data-letter'));
self.getMovies(true)
}
}
})
);
// Actions
['mass_edit', 'details', 'list'].each(function(view){
var current = self.current_view == view;
new Element('li', {
'class': 'icon2 ' + view + (current ? ' active ' : ''),
'data-view': view
}).inject(self.navigation_actions, current ? 'top' : 'bottom');
});
// All
self.letters['all'] = new Element('li.letter_all.available.active', {
'text': 'ALL'
}).inject(self.navigation_alpha);
// Chars
chars.split('').each(function(c){
self.letters[c] = new Element('li', {
'text': c,
'class': 'letter_'+c,
'data-letter': c
}).inject(self.navigation_alpha);
});
// Add menu or hide
if (self.options.menu.length > 0)
self.options.menu.each(function(menu_item){
self.navigation_menu.addLink(menu_item);
});
else
self.navigation_menu.hide();
},
calculateSelected: function(){
var self = this;
var selected = 0,
movies = self.movies.length;
self.movies.each(function(movie){
selected += movie.isSelected() ? 1 : 0
});
var indeterminate = selected > 0 && selected < movies,
checked = selected == movies && selected > 0;
self.mass_edit_select.set('indeterminate', indeterminate);
self.mass_edit_select_class[checked ? 'check' : 'uncheck']();
self.mass_edit_select_class.element[indeterminate ? 'addClass' : 'removeClass']('indeterminate');
self.mass_edit_selected.set('text', selected);
},
deleteSelected: function(){
var self = this,
ids = self.getSelectedMovies(),
help_msg = self.identifier == 'wanted' ? 'If you do, you won\'t be able to watch them, as they won\'t get downloaded!' : 'Your files will be safe, this will only delete the reference from the CouchPotato manage list';
var qObj = new Question('Are you sure you want to delete '+ids.length+' movie'+ (ids.length != 1 ? 's' : '') +'?', help_msg, [{
'text': 'Yes, delete '+(ids.length != 1 ? 'them' : 'it'),
'class': 'delete',
'events': {
'click': function(e){
(e).preventDefault();
this.set('text', 'Deleting..');
Api.request('media.delete', {
'method': 'post',
'data': {
'id': ids.join(','),
'delete_from': self.options.identifier
},
'onSuccess': function(){
qObj.close();
var erase_movies = [];
self.movies.each(function(movie){
if (movie.isSelected()){
$(movie).destroy();
erase_movies.include(movie);
}
});
erase_movies.each(function(movie){
self.movies.erase(movie);
movie.destroy();
self.setCounter(self.counter_count-1);
self.total_movies--;
});
self.calculateSelected();
}
});
}
}
}, {
'text': 'Cancel',
'cancel': true
}]);
},
changeQualitySelected: function(){
var self = this;
var ids = self.getSelectedMovies();
Api.request('movie.edit', {
'method': 'post',
'data': {
'id': ids.join(','),
'profile_id': self.mass_edit_quality.get('value')
},
'onSuccess': self.search.bind(self)
});
},
refreshSelected: function(){
var self = this;
var ids = self.getSelectedMovies();
Api.request('media.refresh', {
'method': 'post',
'data': {
'id': ids.join(',')
}
});
},
getSelectedMovies: function(){
var self = this;
var ids = [];
self.movies.each(function(movie){
if (movie.isSelected())
ids.include(movie.get('_id'))
});
return ids
},
massEditToggleAll: function(){
var self = this;
var select = self.mass_edit_select.get('checked');
self.movies.each(function(movie){
movie.select(select)
});
self.calculateSelected()
},
reset: function(){
var self = this;
self.movies = [];
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();
self.scrollspy.start();
}
},
activateLetter: function(letter){
var self = this;
self.reset();
self.letters[letter || 'all'].addClass('active');
self.filter.starts_with = letter;
},
changeView: function(new_view){
var self = this;
self.el
.removeClass(self.current_view+'_list')
.addClass(new_view+'_list');
self.current_view = new_view;
Cookie.write(self.options.identifier+'_view2', new_view, {duration: 1000});
},
getSavedView: function(){
var self = this;
return Cookie.read(self.options.identifier+'_view2');
},
search: function(){
var self = this;
if(self.search_timer) clearTimeout(self.search_timer);
self.search_timer = (function(){
var search_value = self.navigation_search_input.get('value');
if (search_value == self.last_search_value) return;
self.reset();
self.activateLetter();
self.filter.search = search_value;
self.getMovies(true);
self.last_search_value = search_value;
}).delay(250);
},
update: function(){
var self = this;
self.reset();
self.getMovies(true);
},
getMovies: function(reset){
var self = this;
if(self.scrollspy){
self.scrollspy.stop();
self.load_more.set('text', 'loading...');
}
if(self.movies.length == 0 && self.options.loader){
self.loader_first = new Element('div.loading').adopt(
new Element('div.message', {'text': self.options.title ? 'Loading \'' + self.options.title + '\'' : 'Loading...'})
).inject(self.el, 'top');
createSpinner(self.loader_first, {
radius: 4,
length: 4,
width: 1
});
self.el.setStyle('min-height', 93);
}
Api.request(self.options.api_call || 'media.list', {
'data': Object.merge({
'type': self.options.type || 'movie',
'status': self.options.status,
'limit_offset': self.options.limit ? self.options.limit + ',' + self.offset : null
}, self.filter),
'onSuccess': function(json){
if(reset)
self.movie_list.empty();
if(self.loader_first){
var lf = self.loader_first;
self.loader_first.addClass('hide');
self.loader_first = null;
setTimeout(function(){
lf.destroy();
}, 20000);
self.el.setStyle('min-height', null);
}
self.store(json.movies);
self.addMovies(json.movies, json.total || json.movies.length);
if(self.scrollspy) {
self.load_more.set('text', 'load more movies');
self.scrollspy.start();
}
self.checkIfEmpty();
self.fireEvent('loaded');
}
});
},
loadMore: function(){
var self = this;
if(self.offset >= self.options.limit)
self.getMovies()
},
store: function(movies){
var self = this;
self.offset += movies.length;
},
checkIfEmpty: function(){
var self = this;
var is_empty = self.movies.length == 0 && (self.total_movies == 0 || self.total_movies === undefined);
if(self.title)
self.title[is_empty ? 'hide' : 'show']();
if(self.description)
self.description.setStyle('display', [is_empty ? 'none' : '']);
if(is_empty && self.options.on_empty_element){
self.options.on_empty_element.inject(self.loader_first || self.title || self.movie_list, 'after');
if(self.navigation)
self.navigation.hide();
self.empty_element = self.options.on_empty_element;
}
else if(self.empty_element){
self.empty_element.destroy();
if(self.navigation)
self.navigation.show();
}
},
toElement: function(){
return this.el;
}
});

1077
couchpotato/core/media/show/_base/static/show.css

File diff suppressed because it is too large

103
couchpotato/core/media/show/_base/static/show.episodes.js

@ -0,0 +1,103 @@
var Episodes = new Class({
initialize: function(show) {
var self = this;
self.show = show;
},
open: function(){
var self = this;
if(!self.container){
self.container = new Element('div.options').grab(
self.episodes_container = new Element('div.episodes.table')
);
self.container.inject(self.show, 'top');
Api.request('library.tree', {
'data': {
'media_id': self.show.data._id
},
'onComplete': function(json){
self.data = json.result;
self.createEpisodes();
}
});
}
self.show.slide('in', self.container);
},
createEpisodes: function() {
var self = this;
self.data.seasons.sort(self.sortSeasons);
self.data.seasons.each(function(season) {
self.createSeason(season);
season.episodes.sort(self.sortEpisodes);
season.episodes.each(function(episode) {
self.createEpisode(episode);
});
});
},
createSeason: function(season) {
var self = this,
title = '';
if(season.info.number) {
title = 'Season ' + season.info.number;
} else {
// Season 0 / Specials
title = 'Specials';
}
season['el'] = new Element('div', {
'class': 'item head',
'id': 'season_'+season._id
}).adopt(
new Element('span.name', {'text': title})
).inject(self.episodes_container);
},
createEpisode: function(episode){
var self = this,
e = new Episode(self.show, episode);
$(e).inject(self.episodes_container);
},
sortSeasons: function(a, b) {
// Move "Specials" to the bottom of the list
if(!a.info.number) {
return 1;
}
if(!b.info.number) {
return -1;
}
// Order seasons descending
if(a.info.number < b.info.number)
return -1;
if(a.info.number > b.info.number)
return 1;
return 0;
},
sortEpisodes: function(a, b) {
// Order episodes descending
if(a.info.number < b.info.number)
return -1;
if(a.info.number > b.info.number)
return 1;
return 0;
}
});

347
couchpotato/core/media/show/_base/static/show.js

@ -0,0 +1,347 @@
var Show = new Class({
Extends: BlockBase,
action: {},
initialize: function(list, options, data){
var self = this;
self.data = data;
self.view = options.view || 'details';
self.list = list;
self.el = new Element('div.show');
self.episodes = new Episodes(self);
self.profile = Quality.getProfile(data.profile_id) || {};
self.category = CategoryList.getCategory(data.category_id) || {};
self.parent(self, options);
self.addEvents();
},
addEvents: function(){
var self = this;
self.global_events = {};
// Do refresh with new data
self.global_events['movie.update'] = function(notification){
if(self.data._id != notification.data._id) return;
self.busy(false);
self.removeView();
self.update.delay(2000, self, notification);
};
App.on('movie.update', self.global_events['movie.update']);
// Add spinner on load / search
['media.busy', 'movie.searcher.started'].each(function(listener){
self.global_events[listener] = function(notification){
if(notification.data && (self.data._id == notification.data._id || (typeOf(notification.data._id) == 'array' && notification.data._id.indexOf(self.data._id) > -1)))
self.busy(true);
};
App.on(listener, self.global_events[listener]);
});
// Remove spinner
self.global_events['movie.searcher.ended'] = function(notification){
if(notification.data && self.data._id == notification.data._id)
self.busy(false)
};
App.on('movie.searcher.ended', self.global_events['movie.searcher.ended']);
// Reload when releases have updated
self.global_events['release.update_status'] = function(notification){
var data = notification.data;
if(data && self.data._id == data.movie_id){
if(!self.data.releases)
self.data.releases = [];
self.data.releases.push({'quality': data.quality, 'status': data.status});
self.updateReleases();
}
};
App.on('release.update_status', self.global_events['release.update_status']);
},
destroy: function(){
var self = this;
self.el.destroy();
delete self.list.movies_added[self.get('id')];
self.list.movies.erase(self);
self.list.checkIfEmpty();
// Remove events
Object.each(self.global_events, function(handle, listener){
App.off(listener, handle);
});
},
busy: function(set_busy, timeout){
var self = this;
if(!set_busy){
setTimeout(function(){
if(self.spinner){
self.mask.fade('out');
setTimeout(function(){
if(self.mask)
self.mask.destroy();
if(self.spinner)
self.spinner.el.destroy();
self.spinner = null;
self.mask = null;
}, timeout || 400);
}
}, timeout || 1000)
}
else if(!self.spinner) {
self.createMask();
self.spinner = createSpinner(self.mask);
self.mask.fade('in');
}
},
createMask: function(){
var self = this;
self.mask = new Element('div.mask', {
'styles': {
'z-index': 4
}
}).inject(self.el, 'top').fade('hide');
},
update: function(notification){
var self = this;
self.data = notification.data;
self.el.empty();
self.removeView();
self.profile = Quality.getProfile(self.data.profile_id) || {};
self.category = CategoryList.getCategory(self.data.category_id) || {};
self.create();
self.busy(false);
},
create: function(){
var self = this;
self.el.addClass('status_'+self.get('status'));
self.el.adopt(
self.select_checkbox = new Element('input[type=checkbox].inlay', {
'events': {
'change': function(){
self.fireEvent('select')
}
}
}),
self.thumbnail = (self.data.files && self.data.files.image_poster) ? new Element('img', {
'class': 'type_image poster',
'src': Api.createUrl('file.cache') + self.data.files.image_poster[0].split(Api.getOption('path_sep')).pop()
}): null,
self.data_container = new Element('div.data.inlay.light').adopt(
self.info_container = new Element('div.info').adopt(
new Element('div.title').adopt(
self.title = new Element('a', {
'events': {
'click': function(e){
self.episodes.open();
}
},
'text': self.getTitle() || 'n/a'
}),
self.year = new Element('div.year', {
'text': self.data.info.year || 'n/a'
})
),
self.description = new Element('div.description.tiny_scroll', {
'text': self.data.info.plot
}),
self.quality = new Element('div.quality', {
'events': {
'click': function(e){
var releases = self.el.getElement('.actions .releases');
if(releases.isVisible())
releases.fireEvent('click', [e])
}
}
})
),
self.actions = new Element('div.actions')
)
);
if(!self.thumbnail)
self.el.addClass('no_thumbnail');
//self.changeView(self.view);
self.select_checkbox_class = new Form.Check(self.select_checkbox);
// Add profile
if(self.profile.data)
self.profile.getTypes().each(function(type){
var q = self.addQuality(type.get('quality'), type.get('3d'));
if((type.finish == true || type.get('finish')) && !q.hasClass('finish')){
q.addClass('finish');
q.set('title', q.get('title') + ' Will finish searching for this movie if this quality is found.')
}
});
// Add releases
self.updateReleases();
Object.each(self.options.actions, function(action, key){
self.action[key.toLowerCase()] = action = new self.options.actions[key](self);
if(action.el)
self.actions.adopt(action)
});
},
updateReleases: function(){
var self = this;
if(!self.data.releases || self.data.releases.length == 0) return;
self.data.releases.each(function(release){
var q = self.quality.getElement('.q_'+ release.quality+(release.is_3d ? '.is_3d' : ':not(.is_3d)')),
status = release.status;
if(!q && (status == 'snatched' || status == 'seeding' || status == 'done'))
q = self.addQuality(release.quality, release.is_3d || false);
if (q && !q.hasClass(status)){
q.addClass(status);
q.set('title', (q.get('title') ? q.get('title') : '') + ' status: '+ status)
}
});
},
addQuality: function(quality, is_3d){
var self = this;
var q = Quality.getQuality(quality);
return new Element('span', {
'text': q.label + (is_3d ? ' 3D' : ''),
'class': 'q_'+q.identifier + (is_3d ? ' is_3d' : ''),
'title': ''
}).inject(self.quality);
},
getTitle: function(){
var self = this;
if(self.data.title)
return self.getUnprefixedTitle(self.data.title);
else if(self.data.info.titles.length > 0)
return self.getUnprefixedTitle(self.data.info.titles[0]);
return 'Unknown movie'
},
getUnprefixedTitle: function(t){
if(t.substr(0, 4).toLowerCase() == 'the ')
t = t.substr(4) + ', The';
else if(t.substr(0, 3).toLowerCase() == 'an ')
t = t.substr(3) + ', An';
else if(t.substr(0, 2).toLowerCase() == 'a ')
t = t.substr(2) + ', A';
return t;
},
slide: function(direction, el){
var self = this;
if(direction == 'in'){
self.temp_view = self.view;
self.changeView('details');
self.el.addEvent('outerClick', function(){
self.removeView();
self.slide('out')
});
el.show();
self.el.addClass('expanded');
self.el.getElements('.table').addClass('expanded');
self.data_container.addClass('hide_right');
}
else {
self.el.removeEvents('outerClick');
setTimeout(function(){
if(self.el)
{
self.el.getElements('> :not(.data):not(.poster):not(.movie_container)').hide();
self.el.getElements('.table').removeClass('expanded');
}
}, 600);
self.el.removeClass('expanded');
self.data_container.removeClass('hide_right');
}
},
changeView: function(new_view){
var self = this;
if(self.el)
self.el
.removeClass(self.view+'_view')
.addClass(new_view+'_view');
self.view = new_view;
},
removeView: function(){
var self = this;
self.el.removeClass(self.view+'_view')
},
getIdentifier: function(){
var self = this;
try {
return self.get('identifiers').imdb;
}
catch (e){ }
return self.get('imdb');
},
get: function(attr){
return this.data[attr] || this.data.info[attr]
},
select: function(bool){
var self = this;
self.select_checkbox_class[bool ? 'check' : 'uncheck']()
},
isSelected: function(){
return this.select_checkbox.get('checked');
},
toElement: function(){
return this.el;
}
});

13
couchpotato/core/media/show/episode.py

@ -26,7 +26,7 @@ class Episode(MediaBase):
# Add Season
episode_info = {
'_t': 'media',
'type': 'episode',
'type': 'show.episode',
'identifiers': identifiers,
'parent_id': parent_id,
'info': info, # Returned dict by providers
@ -63,7 +63,16 @@ class Episode(MediaBase):
# Get new info
if not info:
info = fireEvent('episode.info', episode.get('identifiers'), merge = True)
season = db.get('id', episode['parent_id'])
show = db.get('id', season['parent_id'])
info = fireEvent(
'episode.info', show.get('identifiers'), {
'season_identifier': season.get('info', {}).get('number'),
'episode_identifier': episode.get('identifiers')
},
merge = True
)
# Update/create media
if force:

4
couchpotato/core/media/show/library/episode.py

@ -14,7 +14,7 @@ class EpisodeLibraryPlugin(LibraryBase):
addEvent('library.identifier', self.identifier)
def query(self, media, first = True, condense = True, include_identifier = True, **kwargs):
if media.get('type') != 'episode':
if media.get('type') != 'show.episode':
return
related = fireEvent('library.related', media, single = True)
@ -43,7 +43,7 @@ class EpisodeLibraryPlugin(LibraryBase):
return titles
def identifier(self, media):
if media.get('type') != 'episode':
if media.get('type') != 'show.episode':
return
identifier = {

4
couchpotato/core/media/show/library/season.py

@ -14,7 +14,7 @@ class SeasonLibraryPlugin(LibraryBase):
addEvent('library.identifier', self.identifier)
def query(self, media, first = True, condense = True, include_identifier = True, **kwargs):
if media.get('type') != 'season':
if media.get('type') != 'show.season':
return
related = fireEvent('library.related', media, single = True)
@ -44,7 +44,7 @@ class SeasonLibraryPlugin(LibraryBase):
return titles
def identifier(self, media):
if media.get('type') != 'season':
if media.get('type') != 'show.season':
return
return {

17
couchpotato/core/media/show/providers/info/thetvdb.py

@ -172,15 +172,16 @@ class TheTVDb(ShowProvider):
"""Either return a list of all episodes or a single episode.
If episode_identifer contains an episode number to search for
"""
season_identifier = params.get('season_identifier', None)
episode_identifier = params.get('episode_identifier', None)
season_identifier = self.getIdentifier(params.get('season_identifier', None))
episode_identifier = self.getIdentifier(params.get('episode_identifier', None))
identifier = self.getIdentifier(identifier)
if not identifier and season_identifier is None:
return False
# season_identifier must contain the 'show id : season number' since there is no tvdb id
# for season and we need a reference to both the show id and season number
if season_identifier:
if not identifier and season_identifier:
try:
identifier, season_identifier = season_identifier.split(':')
season_identifier = int(season_identifier)
@ -205,15 +206,21 @@ class TheTVDb(ShowProvider):
for episode in season.values():
if episode_identifier is not None and episode['id'] == toUnicode(episode_identifier):
result = self._parseEpisode(show, episode)
result = self._parseEpisode(episode)
self.setCache(cache_key, result)
return result
else:
result.append(self._parseEpisode(show, episode))
result.append(self._parseEpisode(episode))
self.setCache(cache_key, result)
return result
def getIdentifier(self, value):
if type(value) is dict:
return value.get('thetvdb')
return value
def _parseShow(self, show):
#

2
couchpotato/core/media/show/searcher/episode.py

@ -116,7 +116,7 @@ class EpisodeSearcher(SearcherBase, ShowTypeBase):
log.info2('Too early to search for %s, %s', (too_early_to_search, query))
def correctRelease(self, release = None, media = None, quality = None, **kwargs):
if media.get('type') != 'episode':
if media.get('type') != 'show.episode':
return
retention = Env.setting('retention', section = 'nzb')

2
couchpotato/core/media/show/season.py

@ -28,7 +28,7 @@ class Season(MediaBase):
# Add Season
season_info = {
'_t': 'media',
'type': 'season',
'type': 'show.season',
'identifiers': identifiers,
'parent_id': parent_id,
'info': info, # Returned dict by providers

Loading…
Cancel
Save