Browse Source

EOD commit (WIP). So close to writing tv objects to database but too tired to finish. Currently storing a show as a movie using imdb metadata. Added another search button beside movie button

pull/2038/head
Jason Mehring 12 years ago
parent
commit
31cd993506
  1. 1
      couchpotato/core/media/movie/_base/main.py
  2. 28
      couchpotato/core/media/movie/_base/static/search.js
  3. 155
      couchpotato/core/media/show/_base/main.py
  4. 4
      couchpotato/core/media/show/_base/static/search.css
  5. 9
      couchpotato/core/media/show/_base/static/search.js
  6. 3
      couchpotato/core/plugins/library/main.py
  7. 101
      couchpotato/core/providers/show/thetvdb/main.py
  8. 1
      couchpotato/core/settings/model.py
  9. 1
      couchpotato/static/scripts/couchpotato.js

1
couchpotato/core/media/movie/_base/main.py

@ -346,7 +346,6 @@ class MovieBase(MovieTypeBase):
}
def add(self, params = {}, force_readd = True, search_after = True, update_library = False, status_id = None):
if not params.get('identifier'):
msg = 'Can\'t add movie without imdb identifier.'
log.error(msg)

28
couchpotato/core/media/movie/_base/static/search.js

@ -11,7 +11,7 @@ Block.Search = new Class({
self.el = new Element('div.search_form').adopt(
new Element('div.input').adopt(
self.input = new Element('input', {
'placeholder': 'Search & add a new show',
'placeholder': 'Search & add a new movie',
'events': {
'keyup': self.keyup.bind(self),
'focus': function(){
@ -66,7 +66,7 @@ Block.Search = new Class({
self.input.set('value', '');
self.input.focus()
self.shows = []
self.movies = []
self.results.empty()
self.el.removeClass('filled')
@ -131,7 +131,7 @@ Block.Search = new Class({
if(!self.spinner)
self.spinner = createSpinner(self.mask);
self.api_request = Api.request('show.search', {
self.api_request = Api.request('movie.search', {
'data': {
'q': q
},
@ -150,17 +150,17 @@ Block.Search = new Class({
self.cache[q] = json
self.shows = {}
self.movies = {}
self.results.empty()
Object.each(json.shows, function(show){
Object.each(json.movies, function(movie){
var m = new Block.Search.Item(show);
var m = new Block.Search.Item(movie);
$(m).inject(self.results)
self.shows[show.imdb || 'r-'+Math.floor(Math.random()*10000)] = m
self.movies[movie.imdb || 'r-'+Math.floor(Math.random()*10000)] = m
if(q == show.imdb)
m.showOptions()
if(q == movie.imdb)
m.movieOptions()
});
@ -201,7 +201,7 @@ Block.Search.Item = new Class({
var self = this,
info = self.info;
self.el = new Element('div.show_result', {
self.el = new Element('div.movie_result', {
'id': info.imdb
}).adopt(
self.thumbnail = info.images && info.images.poster.length > 0 ? new Element('img.thumbnail', {
@ -212,7 +212,7 @@ Block.Search.Item = new Class({
self.options_el = new Element('div.options.inlay'),
self.data_container = new Element('div.data', {
'events': {
'click': self.showOptions.bind(self)
'click': self.movieOptions.bind(self)
}
}).adopt(
self.info_container = new Element('div.info').adopt(
@ -256,7 +256,7 @@ Block.Search.Item = new Class({
return this.info[key]
},
showOptions: function(){
movieOptions: function(){
var self = this;
self.createOptions();
@ -281,7 +281,7 @@ Block.Search.Item = new Class({
self.loadingMask();
Api.request('show.add', {
Api.request('movie.add', {
'data': {
'identifier': self.info.imdb,
'title': self.title_select.get('value'),
@ -366,7 +366,7 @@ Block.Search.Item = new Class({
if(categories.length == 0)
self.category_select.hide();
else {
self.category_select.show();
self.category_select.movie();
categories.each(function(category){
new Element('option', {
'value': category.data.id,

155
couchpotato/core/media/show/_base/main.py

@ -55,6 +55,16 @@ class ShowBase(MediaBase):
'shows': array, shows found,
}"""}
})
addApiView('show.add', self.addView, docs = {
'desc': 'Add new movie to the wanted list',
'params': {
'identifier': {'desc': 'IMDB id of the movie your want to add.'},
'profile_id': {'desc': 'ID of quality profile you want the add the movie in. If empty will use the default profile.'},
'title': {'desc': 'Movie title to use for searches. Has to be one of the titles returned by movie.search.'},
}
})
addEvent('show.add', self.add)
def search(self, q = '', **kwargs):
@ -75,3 +85,148 @@ class ShowBase(MediaBase):
'shows': shows,
}
def addView(self, **kwargs):
movie_dict = fireEvent('show.add', params=kwargs) # XXX: Temp added so we can catch a breakpoint
#movie_dict = self.add(params = kwargs)
return {
'success': True,
'added': True if movie_dict else False,
'movie': movie_dict,
}
def add(self, params = {}, force_readd = True, search_after = True, update_library = False, status_id = None):
"""
1. Add Show
1. Add All Episodes
2. Add All Seasons
Notes, not to forget:
- relate parent and children, possible grandparent to grandchild so episodes know it belong to show, etc
- looks like we dont send info to library; it comes later
- change references to plot to description
- change Model to Media
"""
log.debug("show.add")
identifier = params.get('thetvdb_id')
episodes = fireEvent('show.episodes', identifier = identifier)
# XXX: Fix so we dont have a nested list
for episode in episodes[0]:
self.add2(params=episode)
return self.add2(params = params)
def add2(self, params = {}, force_readd = True, search_after = True, update_library = False, status_id = None):
log.debug("show.add2")
if not params.get('identifier'):
msg = 'Can\'t add show without imdb identifier.'
log.error(msg)
fireEvent('notify.frontend', type = 'show.is_tvshow', message = msg)
return False
#else:
#try:
#is_show = fireEvent('movie.is_show', identifier = params.get('identifier'), single = True)
#if not is_show:
#msg = 'Can\'t add show, seems to be a TV show.'
#log.error(msg)
#fireEvent('notify.frontend', type = 'show.is_tvshow', message = msg)
#return False
#except:
#pass
library = fireEvent('library.add', single = True, attrs = params, update_after = update_library)
# Status
status_active, snatched_status, ignored_status, done_status, downloaded_status = \
fireEvent('status.get', ['active', 'snatched', 'ignored', 'done', 'downloaded'], single = True)
default_profile = fireEvent('profile.default', single = True)
cat_id = params.get('category_id', None)
db = get_session()
m = db.query(Movie).filter_by(library_id = library.get('id')).first()
added = True
do_search = False
if not m:
m = Movie(
library_id = library.get('id'),
profile_id = params.get('profile_id', default_profile.get('id')),
status_id = status_id if status_id else status_active.get('id'),
category_id = tryInt(cat_id) if cat_id is not None and tryInt(cat_id) > 0 else None,
)
db.add(m)
db.commit()
onComplete = None
if search_after:
onComplete = self.createOnComplete(m.id)
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 in [downloaded_status.get('id'), snatched_status.get('id'), done_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'))
m.category_id = tryInt(cat_id) if cat_id is not None and tryInt(cat_id) > 0 else None
else:
log.debug('Movie already exists, not updating: %s', params)
added = False
if force_readd:
m.status_id = status_id if status_id else status_active.get('id')
m.last_edit = int(time.time())
do_search = True
db.commit()
# Remove releases
available_status = fireEvent('status.get', 'available', single = True)
for rel in m.releases:
if rel.status_id is available_status.get('id'):
db.delete(rel)
db.commit()
show_dict = m.to_dict(self.default_dict)
if do_search and search_after:
onComplete = self.createOnComplete(m.id)
onComplete()
if added:
fireEvent('notify.frontend', type = 'show.added', data = show_dict, message = 'Successfully added "%s" to your wanted list.' % params.get('title', ''))
db.expire_all()
return show_dict
def createOnComplete(self, movie_id):
def onComplete():
db = get_session()
movie = db.query(Movie).filter_by(id = movie_id).first()
fireEventAsync('movie.searcher.single', movie.to_dict(self.default_dict), on_complete = self.createNotifyFront(movie_id))
db.expire_all()
return onComplete
def createNotifyFront(self, movie_id):
def notifyFront():
db = get_session()
movie = db.query(Movie).filter_by(id = movie_id).first()
fireEvent('notify.frontend', type = 'show.update.%s' % movie.id, data = movie.to_dict(self.default_dict))
db.expire_all()
return notifyFront

4
couchpotato/core/media/show/_base/static/search.css

@ -2,9 +2,9 @@
display: inline-block;
vertical-align: middle;
position: absolute;
right: 205px;
right: 135px;
top: 0;
text-align: right;
text-align: left;
height: 100%;
border-bottom: 4px solid transparent;
transition: all .4s cubic-bezier(0.9,0,0.1,1);

9
couchpotato/core/media/show/_base/static/search.js

@ -1,4 +1,4 @@
Block.Search = new Class({
Block.ShowSearch = new Class({
Extends: BlockBase,
@ -11,7 +11,7 @@ Block.Search = new Class({
self.el = new Element('div.show_search_form').adopt(
new Element('div.input').adopt(
self.input = new Element('input', {
'placeholder': 'Search & add a new show,
'placeholder': 'Search & add a new *show*',
'events': {
'keyup': self.keyup.bind(self),
'focus': function(){
@ -155,7 +155,7 @@ Block.Search = new Class({
Object.each(json.shows, function(show){
var m = new Block.Search.Item(show);
var m = new Block.ShowSearch.Item(show);
$(m).inject(self.results)
self.shows[show.imdb || 'r-'+Math.floor(Math.random()*10000)] = m
@ -183,7 +183,7 @@ Block.Search = new Class({
});
Block.Search.Item = new Class({
Block.ShowSearch.Item = new Class({
Implements: [Options, Events],
@ -284,6 +284,7 @@ Block.Search.Item = new Class({
Api.request('show.add', {
'data': {
'identifier': self.info.imdb,
'thetvdb_id': self.info.thetvdb_id,
'title': self.title_select.get('value'),
'profile_id': self.profile_select.get('value'),
'category_id': self.category_select.get('value')

3
couchpotato/core/plugins/library/main.py

@ -20,7 +20,7 @@ class LibraryPlugin(Plugin):
addEvent('library.update', self.update)
addEvent('library.update_release_date', self.updateReleaseDate)
def add(self, attrs = {}, update_after = True):
def add(self, attrs = {}, update_after = True, type='movie'):
db = get_session()
@ -28,6 +28,7 @@ class LibraryPlugin(Plugin):
if not l:
status = fireEvent('status.get', 'needs_update', single = True)
l = Library(
type = type,
year = attrs.get('year'),
identifier = attrs.get('identifier'),
plot = toUnicode(attrs.get('plot')),

101
couchpotato/core/providers/show/thetvdb/main.py

@ -14,6 +14,7 @@ class TheTVDb(ShowProvider):
#addEvent('show.by_hash', self.byHash)
addEvent('show.search', self.search, priority = 1)
addEvent('show.info', self.getInfo, priority = 1)
addEvent('show.episodes', self.getEpisodes, priority = 1)
#addEvent('show.info_by_thetvdb', self.getInfoByTheTVDBId)
# Use base wrapper
@ -100,11 +101,24 @@ class TheTVDb(ShowProvider):
return results
def getInfo(self, identifier = None):
def getEpisodes(self, identifier=None):
if not identifier:
return {}
return []
try:
show = self.tvdb[int(identifier)]
except:
return []
result = []
for season in show.values():
for episode in season.values():
# Consider cache
result.append(self.parseEpisode(episode))
return result
def getInfo(self, identifier = None):
cache_key = 'thetvdb.cache.%s' % identifier
result = self.getCache(cache_key)
@ -217,6 +231,85 @@ class TheTVDb(ShowProvider):
#show_data['titles'].append(alt_name)
return show_data
def parseEpisode(self, episode):
"""
('episodenumber', u'1'),
('thumb_added', None),
('rating', u'7.7'),
('overview',
u'Experienced waitress Max Black meets her new co-worker, former rich-girl Caroline Channing, and puts her skills to the test at an old but re-emerging Brooklyn diner. Despite her initial distaste for Caroline, Max eventually softens and the two team up for a new business venture.'),
('dvd_episodenumber', None),
('dvd_discid', None),
('combined_episodenumber', u'1'),
('epimgflag', u'7'),
('id', u'4099506'),
('seasonid', u'465948'),
('thumb_height', u'225'),
('tms_export', u'1374789754'),
('seasonnumber', u'1'),
('writer', u'|Michael Patrick King|Whitney Cummings|'),
('lastupdated', u'1371420338'),
('filename', u'http://thetvdb.com/banners/episodes/248741/4099506.jpg'),
('absolute_number', u'1'),
('ratingcount', u'102'),
('combined_season', u'1'),
('thumb_width', u'400'),
('imdb_id', u'tt1980319'),
('director', u'James Burrows'),
('dvd_chapter', None),
('dvd_season', None),
('gueststars',
u'|Brooke Lyons|Noah Mills|Shoshana Bush|Cale Hartmann|Adam Korson|Alex Enriquez|Matt Cook|Bill Parks|Eugene Shaw|Sergey Brusilovsky|Greg Lewis|Cocoa Brown|Nick Jameson|'),
('seriesid', u'248741'),
('language', u'en'),
('productioncode', u'296793'),
('firstaired', u'2011-09-19'),
('episodename', u'Pilot')]
"""
## Images
#poster = self.getImage(episode, type = 'poster', size = 'cover')
#backdrop = self.getImage(episode, type = 'fanart', size = 'w1280')
##poster_original = self.getImage(episode, type = 'poster', size = 'original')
##backdrop_original = self.getImage(episode, type = 'backdrop', size = 'original')
poster = []
backdrop = []
## Genres
genres = []
## Year (not really needed for episode)
year = None
episode_data = {
'via_thetvdb': True,
'thetvdb_id': int(episode['id']),
'titles': [episode['episodename'], ],
'original_title': episode['episodename'],
'images': {
'poster': [poster] if poster else [],
'backdrop': [backdrop] if backdrop else [],
'poster_original': [],
'backdrop_original': [],
},
'imdb': episode['imdb_id'],
'runtime': None,
'released': episode['firstaired'],
'year': year,
'plot': episode['overview'],
'genres': genres,
}
episode_data = dict((k, v) for k, v in episode_data.iteritems() if v)
## Add alternative names
#for alt in ['original_name', 'alternative_name']:
#alt_name = toUnicode(episode.get(alt))
#if alt_name and not alt_name in episode_data['titles'] and alt_name.lower() != 'none' and alt_name != None:
#episode_data['titles'].append(alt_name)
return episode_data
def getImage(self, show, type = 'poster', size = 'cover'):
""""""

1
couchpotato/core/settings/model.py

@ -91,6 +91,7 @@ class Library(Entity):
""""""
# For Movies, CPS uses three: omdbapi (no prio !?), tmdb (prio 2) and couchpotatoapi (prio 1)
type = Field(String(10), default="movie", index=True)
provider = Field(String(10), default="imdb", index=True)
year = Field(Integer)
identifier = Field(String(20), index = True)

1
couchpotato/static/scripts/couchpotato.js

@ -71,6 +71,7 @@
new Element('div').adopt(
self.block.navigation = new Block.Navigation(self, {}),
self.block.search = new Block.Search(self, {}),
self.block.search = new Block.ShowSearch(self, {}),
self.block.more = new Block.Menu(self, {'button_class': 'icon2.cog'})
)
),

Loading…
Cancel
Save