@@ -82,16 +82,6 @@ class ShowBase(MediaBase):
|
|||||||
|
|
||||||
def add(self, params = {}, force_readd = True, search_after = True, update_library = False, status_id = None):
|
def add(self, params = {}, force_readd = True, search_after = True, update_library = False, status_id = None):
|
||||||
"""
|
"""
|
||||||
1. Add Show
|
|
||||||
2. Add All Episodes
|
|
||||||
3. 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
|
|
||||||
|
|
||||||
params
|
params
|
||||||
{'category_id': u'-1',
|
{'category_id': u'-1',
|
||||||
'identifier': u'tt1519931',
|
'identifier': u'tt1519931',
|
||||||
@@ -101,36 +91,92 @@ class ShowBase(MediaBase):
|
|||||||
"""
|
"""
|
||||||
log.debug("show.add")
|
log.debug("show.add")
|
||||||
|
|
||||||
# Add show parent to db first
|
# Add show parent to db first; need to update library so maps will be in place (if any)
|
||||||
parent = self.addToDatabase(params = params, type = 'show')
|
parent = self.addToDatabase(params = params, update_library = True, type = 'show')
|
||||||
|
|
||||||
|
# TODO: add by airdate
|
||||||
|
|
||||||
|
# Add by Season/Episode numbers
|
||||||
|
self.addBySeasonEpisode(parent,
|
||||||
|
params = params,
|
||||||
|
force_readd = force_readd,
|
||||||
|
search_after = search_after,
|
||||||
|
update_library = update_library,
|
||||||
|
status_id = status_id
|
||||||
|
)
|
||||||
|
|
||||||
|
def addBySeasonEpisode(self, parent, params = {}, force_readd = True, search_after = True, update_library = False, status_id = None):
|
||||||
identifier = params.get('id')
|
identifier = params.get('id')
|
||||||
|
# 'tvdb' will always be the master for our purpose. All mapped data can be mapped
|
||||||
|
# to another source for downloading, but it will always be remapped back to tvdb numbering
|
||||||
|
# when renamed so media can be used in media players that use tvdb for info provider
|
||||||
|
#
|
||||||
|
# This currently means the episode must actually exist in tvdb in order to be found but
|
||||||
|
# the numbering can be different
|
||||||
|
|
||||||
# XXX: Fix so we dont have a nested list [0] (fireEvent)
|
#master = 'tvdb'
|
||||||
try:
|
#destination = 'scene'
|
||||||
seasons = fireEvent('season.info', identifier = identifier)[0]
|
#destination = 'anidb'
|
||||||
except: return None
|
#destination = 'rage'
|
||||||
|
#destination = 'trakt'
|
||||||
|
# TODO: auto mode. if anime exists use it. if scene exists use it else use tvdb
|
||||||
|
|
||||||
|
# XXX: We should abort adding show, etc if either tvdb or xem is down or we will have incorrent mappings
|
||||||
|
# I think if tvdb gets error we wont have anydata anyway, but we must make sure XEM returns!!!!
|
||||||
|
|
||||||
|
# Only the master should return results here; all other info providers should just return False
|
||||||
|
# since we are just interested in the structure at this point.
|
||||||
|
seasons = fireEvent('season.info', merge = True, identifier = identifier)
|
||||||
if seasons is not None:
|
if seasons is not None:
|
||||||
for season in seasons:
|
for season in seasons:
|
||||||
season['title'] = season.get('title', None)
|
# Make sure we are only dealing with 'tvdb' responses at this point
|
||||||
|
if season.get('primary_provider', None) != 'thetvdb':
|
||||||
|
continue
|
||||||
season_id = season.get('id', None)
|
season_id = season.get('id', None)
|
||||||
if season_id is None: continue
|
if season_id is None: continue
|
||||||
season['identifier'] = season_id
|
|
||||||
season['parent_identifier'] = identifier
|
|
||||||
self.addToDatabase(params=season, type = "season")
|
|
||||||
|
|
||||||
# XXX: Fix so we dont have a nested list [0] (fireEvent)
|
season_params = {'season_identifier': season_id}
|
||||||
try:
|
# Calling all info providers; merge your info now for individual season
|
||||||
episodes = fireEvent('episode.info', identifier = identifier, season_identifier = season_id)[0]
|
single_season = fireEvent('season.info', merge = True, identifier = identifier, params = season_params)
|
||||||
except: continue
|
single_season['title'] = single_season.get('original_title', None)
|
||||||
|
single_season['identifier'] = season_id
|
||||||
|
single_season['parent_identifier'] = identifier
|
||||||
|
log.info("Adding Season %s" % season_id)
|
||||||
|
s = self.addToDatabase(params = single_season, type = "season")
|
||||||
|
|
||||||
|
episode_params = {'season_identifier': season_id}
|
||||||
|
episodes = fireEvent('episode.info', merge = True, identifier = identifier, params = episode_params)
|
||||||
if episodes is not None:
|
if episodes is not None:
|
||||||
for episode in episodes:
|
for episode in episodes:
|
||||||
episode['title'] = episode.get('titles', None)[0] # XXX. [0] will create exception. FIX!
|
# Make sure we are only dealing with 'tvdb' responses at this point
|
||||||
|
if episode.get('primary_provider', None) != 'thetvdb':
|
||||||
|
continue
|
||||||
episode_id = episode.get('id', None)
|
episode_id = episode.get('id', None)
|
||||||
if episode_id is None: continue
|
if episode_id is None: continue
|
||||||
episode['identifier'] = episode_id
|
try:
|
||||||
episode['parent_identifier'] = season['identifier']
|
episode_number = int(episode.get('episodenumber', None))
|
||||||
self.addToDatabase(params=episode, type = "episode")
|
except (ValueError, TypeError):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
absolute_number = int(episode.get('absolute_number', None))
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
absolute_number = None
|
||||||
|
|
||||||
|
episode_params = {'season_identifier': season_id,
|
||||||
|
'episode_identifier': episode_id,
|
||||||
|
'episode': episode_number}
|
||||||
|
if absolute_number:
|
||||||
|
episode_params['absolute'] = absolute_number
|
||||||
|
# Calling all info providers; merge your info now for individual episode
|
||||||
|
single_episode = fireEvent('episode.info', merge = True, identifier = identifier, params = episode_params)
|
||||||
|
single_episode['title'] = single_episode.get('original_title', None)
|
||||||
|
single_episode['identifier'] = episode_id
|
||||||
|
single_episode['parent_identifier'] = single_season['identifier']
|
||||||
|
log.info("Adding [%sx%s] %s - %s" % (season_id,
|
||||||
|
episode_number,
|
||||||
|
params['title'],
|
||||||
|
single_episode.get('original_title', '')))
|
||||||
|
e = self.addToDatabase(params = single_episode, type = "episode")
|
||||||
|
|
||||||
return parent
|
return parent
|
||||||
|
|
||||||
|
|||||||
@@ -88,8 +88,9 @@ class EpisodeLibraryPlugin(LibraryBase):
|
|||||||
if library.status_id == done_status.get('id') and not force:
|
if library.status_id == done_status.get('id') and not force:
|
||||||
do_update = False
|
do_update = False
|
||||||
|
|
||||||
info = fireEvent('episode.info', merge = True, season_identifier = parent_identifier, \
|
episode_params = {'season_identifier': parent_identifier,
|
||||||
episode_identifier = identifier)
|
'episode_identifier': identifier}
|
||||||
|
info = fireEvent('episode.info', merge = True, params = episode_params)
|
||||||
|
|
||||||
# Don't need those here
|
# Don't need those here
|
||||||
try: del info['in_wanted']
|
try: del info['in_wanted']
|
||||||
@@ -156,8 +157,7 @@ class EpisodeLibraryPlugin(LibraryBase):
|
|||||||
except:
|
except:
|
||||||
log.debug('Failed to attach to library: %s', traceback.format_exc())
|
log.debug('Failed to attach to library: %s', traceback.format_exc())
|
||||||
|
|
||||||
library_dict = library.to_dict(self.default_dict)
|
library_dict = library.to_dict(self.default_dict)
|
||||||
|
|
||||||
db.expire_all()
|
db.expire_all()
|
||||||
return library_dict
|
return library_dict
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ class SeasonLibraryPlugin(LibraryBase):
|
|||||||
handle('library.update.season', identifier = l.identifier, default_title = toUnicode(attrs.get('title', '')))
|
handle('library.update.season', identifier = l.identifier, default_title = toUnicode(attrs.get('title', '')))
|
||||||
|
|
||||||
library_dict = l.to_dict(self.default_dict)
|
library_dict = l.to_dict(self.default_dict)
|
||||||
|
|
||||||
db.expire_all()
|
db.expire_all()
|
||||||
return library_dict
|
return library_dict
|
||||||
|
|
||||||
@@ -88,8 +87,8 @@ class SeasonLibraryPlugin(LibraryBase):
|
|||||||
if library.status_id == done_status.get('id') and not force:
|
if library.status_id == done_status.get('id') and not force:
|
||||||
do_update = False
|
do_update = False
|
||||||
|
|
||||||
info = fireEvent('season.info', merge = True, identifier = parent_identifier, \
|
season_params = {'season_identifier': identifier}
|
||||||
season_identifier = identifier)
|
info = fireEvent('season.info', merge = True, identifier = parent_identifier, params = season_params)
|
||||||
|
|
||||||
# Don't need those here
|
# Don't need those here
|
||||||
try: del info['in_wanted']
|
try: del info['in_wanted']
|
||||||
@@ -148,13 +147,11 @@ class SeasonLibraryPlugin(LibraryBase):
|
|||||||
file_obj = db.query(File).filter_by(id = file_obj.get('id')).one()
|
file_obj = db.query(File).filter_by(id = file_obj.get('id')).one()
|
||||||
library.files.append(file_obj)
|
library.files.append(file_obj)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
log.debug('Failed to attach to library: %s', traceback.format_exc())
|
log.debug('Failed to attach to library: %s', traceback.format_exc())
|
||||||
|
|
||||||
library_dict = library.to_dict(self.default_dict)
|
library_dict = library.to_dict(self.default_dict)
|
||||||
|
|
||||||
db.expire_all()
|
db.expire_all()
|
||||||
return library_dict
|
return library_dict
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ class ShowLibraryPlugin(LibraryBase):
|
|||||||
handle('library.update.show', identifier = l.identifier, default_title = toUnicode(attrs.get('title', '')))
|
handle('library.update.show', identifier = l.identifier, default_title = toUnicode(attrs.get('title', '')))
|
||||||
|
|
||||||
library_dict = l.to_dict(self.default_dict)
|
library_dict = l.to_dict(self.default_dict)
|
||||||
|
|
||||||
db.expire_all()
|
db.expire_all()
|
||||||
return library_dict
|
return library_dict
|
||||||
|
|
||||||
|
|||||||
@@ -98,11 +98,11 @@ class Dashboard(Plugin):
|
|||||||
if len(movie_ids) > 0:
|
if len(movie_ids) > 0:
|
||||||
|
|
||||||
# Get all movie information
|
# Get all movie information
|
||||||
movies_raw = db.query(Movie) \
|
movies_raw = db.query(Media) \
|
||||||
.options(joinedload_all('library.titles')) \
|
.options(joinedload_all('library.titles')) \
|
||||||
.options(joinedload_all('library.files')) \
|
.options(joinedload_all('library.files')) \
|
||||||
.options(joinedload_all('files')) \
|
.options(joinedload_all('files')) \
|
||||||
.filter(Movie.id.in_(movie_ids)) \
|
.filter(Media.id.in_(movie_ids)) \
|
||||||
.all()
|
.all()
|
||||||
|
|
||||||
# Create dict by movie id
|
# Create dict by movie id
|
||||||
|
|||||||
@@ -110,13 +110,15 @@ class TheTVDb(ShowProvider):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def getSeasonInfo(self, identifier=None, season_identifier=None):
|
def getSeasonInfo(self, identifier = None, params = {}):
|
||||||
"""Either return a list of all seasons or a single season by number.
|
"""Either return a list of all seasons or a single season by number.
|
||||||
identifier is the show 'id'
|
identifier is the show 'id'
|
||||||
"""
|
"""
|
||||||
if not identifier:
|
if not identifier:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
season_identifier = params.get('season_identifier', None)
|
||||||
|
|
||||||
# season_identifier must contain the 'show id : season number' since there is no tvdb id
|
# 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
|
# for season and we need a reference to both the show id and season number
|
||||||
if season_identifier:
|
if season_identifier:
|
||||||
@@ -147,10 +149,13 @@ class TheTVDb(ShowProvider):
|
|||||||
self.setCache(cache_key, result)
|
self.setCache(cache_key, result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def getEpisodeInfo(self, identifier=None, season_identifier=None, episode_identifier=None):
|
def getEpisodeInfo(self, identifier = None, params = {}):
|
||||||
"""Either return a list of all episodes or a single episode.
|
"""Either return a list of all episodes or a single episode.
|
||||||
If episode_identifer contains an episode number to search for
|
If episode_identifer contains an episode number to search for
|
||||||
"""
|
"""
|
||||||
|
season_identifier = params.get('season_identifier', None)
|
||||||
|
episode_identifier = params.get('episode_identifier', None)
|
||||||
|
|
||||||
if not identifier and season_identifier is None:
|
if not identifier and season_identifier is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
from .main import Xem
|
||||||
|
|
||||||
|
def start():
|
||||||
|
return Xem()
|
||||||
|
|
||||||
|
config = [{
|
||||||
|
'name': 'xem',
|
||||||
|
'groups': [
|
||||||
|
{
|
||||||
|
'tab': 'providers',
|
||||||
|
'name': 'xem',
|
||||||
|
'label': 'TheXem',
|
||||||
|
'hidden': True,
|
||||||
|
'description': 'Used for all calls to TheXem.',
|
||||||
|
'options': [
|
||||||
|
{
|
||||||
|
'name': 'enabled',
|
||||||
|
'default': True,
|
||||||
|
'label': 'Enabled',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}]
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
from couchpotato.core.event import addEvent
|
||||||
|
from couchpotato.core.logger import CPLog
|
||||||
|
from couchpotato.core.providers.info.base import ShowProvider
|
||||||
|
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
log = CPLog(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Xem(ShowProvider):
|
||||||
|
'''
|
||||||
|
Mapping Information
|
||||||
|
===================
|
||||||
|
|
||||||
|
Single
|
||||||
|
------
|
||||||
|
You will need the id / identifier of the show e.g. tvdb-id for American Dad! is 73141
|
||||||
|
the origin is the name of the site/entity the episode, season (and/or absolute) numbers are based on
|
||||||
|
|
||||||
|
http://thexem.de/map/single?id=&origin=&episode=&season=&absolute=
|
||||||
|
|
||||||
|
episode, season and absolute are all optional but it wont work if you don't provide either episode and season OR absolute in
|
||||||
|
addition you can provide destination as the name of the wished destination, if not provided it will output all available
|
||||||
|
|
||||||
|
When a destination has two or more addresses another entry will be added as _ ... for now the second address gets the index "2"
|
||||||
|
(the first index is omitted) and so on
|
||||||
|
|
||||||
|
http://thexem.de/map/single?id=7529&origin=anidb&season=1&episode=2&destination=trakt
|
||||||
|
{
|
||||||
|
"result":"success",
|
||||||
|
"data":{
|
||||||
|
"trakt": {"season":1,"episode":3,"absolute":3},
|
||||||
|
"trakt_2":{"season":1,"episode":4,"absolute":4}
|
||||||
|
},
|
||||||
|
"message":"single mapping for 7529 on anidb."
|
||||||
|
}
|
||||||
|
|
||||||
|
All
|
||||||
|
---
|
||||||
|
Basically same as "single" just a little easier
|
||||||
|
The origin address is added into the output too!!
|
||||||
|
|
||||||
|
http://thexem.de/map/all?id=7529&origin=anidb
|
||||||
|
|
||||||
|
All Names
|
||||||
|
---------
|
||||||
|
Get all names xem has to offer
|
||||||
|
non optional params: origin(an entity string like 'tvdb')
|
||||||
|
optional params: season, language
|
||||||
|
- season: a season number or a list like: 1,3,5 or a compare operator like ne,gt,ge,lt,le,eq and a season number. default would
|
||||||
|
return all
|
||||||
|
- language: a language string like 'us' or 'jp' default is all
|
||||||
|
- defaultNames: 1(yes) or 0(no) should the default names be added to the list ? default is 0(no)
|
||||||
|
|
||||||
|
http://thexem.de/map/allNames?origin=tvdb&season=le1
|
||||||
|
|
||||||
|
{
|
||||||
|
"result": "success",
|
||||||
|
"data": {
|
||||||
|
"248812": ["Dont Trust the Bitch in Apartment 23", "Don't Trust the Bitch in Apartment 23"],
|
||||||
|
"257571": ["Nazo no Kanojo X"],
|
||||||
|
"257875": ["Lupin III - Mine Fujiko to Iu Onna", "Lupin III Fujiko to Iu Onna", "Lupin the Third - Mine Fujiko to Iu Onna"]
|
||||||
|
},
|
||||||
|
"message": ""
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
addEvent('show.info', self.getShowInfo, priority = 5)
|
||||||
|
addEvent('episode.info', self.getEpisodeInfo, priority = 5)
|
||||||
|
|
||||||
|
self.config = {}
|
||||||
|
self.config['base_url'] = "http://thexem.de"
|
||||||
|
self.config['url_single'] = u"%(base_url)s/map/single?" % self.config
|
||||||
|
self.config['url_all'] = u"%(base_url)s/map/all?" % self.config
|
||||||
|
self.config['url_names'] = u"%(base_url)s/map/names?" % self.config
|
||||||
|
self.config['url_all_names'] = u"%(base_url)s/map/allNames?" % self.config
|
||||||
|
|
||||||
|
# TODO: Also get show aliases (store as titles)
|
||||||
|
def getShowInfo(self, identifier = None):
|
||||||
|
if self.isDisabled():
|
||||||
|
return {}
|
||||||
|
|
||||||
|
cache_key = 'xem.cache.%s' % identifier
|
||||||
|
log.debug('Getting showInfo: %s', cache_key)
|
||||||
|
result = self.getCache(cache_key) or {}
|
||||||
|
if result:
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Create season/episode and absolute mappings
|
||||||
|
url = self.config['url_all'] + "id=%s&origin=tvdb" % tryUrlencode(identifier)
|
||||||
|
response = self.getJsonData(url)
|
||||||
|
if response:
|
||||||
|
if response.get('result') == 'success':
|
||||||
|
data = response.get('data', None)
|
||||||
|
result = self._parse(data)
|
||||||
|
|
||||||
|
# Create name alias mappings
|
||||||
|
url = self.config['url_names'] + "id=%s&origin=tvdb" % tryUrlencode(identifier)
|
||||||
|
response = self.getJsonData(url)
|
||||||
|
if response:
|
||||||
|
if response.get('result') == 'success':
|
||||||
|
data = response.get('data', None)
|
||||||
|
result.update({'map_names': data})
|
||||||
|
|
||||||
|
self.setCache(cache_key, result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def getEpisodeInfo(self, identifier = None, params = {}):
|
||||||
|
episode = params.get('episode', None)
|
||||||
|
if episode is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
season_identifier = params.get('season_identifier', None)
|
||||||
|
if season_identifier is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
episode_identifier = params.get('episode_identifier', None)
|
||||||
|
absolute = params.get('absolute', None)
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
try:
|
||||||
|
identifier, season_identifier = season_identifier.split(':')
|
||||||
|
season = int(season_identifier)
|
||||||
|
except: return False
|
||||||
|
|
||||||
|
result = self.getShowInfo(identifier)
|
||||||
|
map = {}
|
||||||
|
if result:
|
||||||
|
map_episode = result.get('map_episode', {}).get(season, {}).get(episode, {})
|
||||||
|
if map_episode:
|
||||||
|
map.update({'map_episode': map_episode})
|
||||||
|
|
||||||
|
if absolute:
|
||||||
|
map_absolute = result.get('map_absolute', {}).get(absolute, {})
|
||||||
|
if map_absolute:
|
||||||
|
map.update({'map_absolute': map_absolute})
|
||||||
|
|
||||||
|
map_names = result.get('map_names', {}).get(season, {}).get(episode, {})
|
||||||
|
if map_names:
|
||||||
|
map.update({'map_names': map_names})
|
||||||
|
|
||||||
|
return map
|
||||||
|
|
||||||
|
|
||||||
|
def _parse(self, data, master = 'tvdb'):
|
||||||
|
'''parses xem map and returns a custom formatted dict map
|
||||||
|
|
||||||
|
To retreive map for scene:
|
||||||
|
if 'scene' in map['map_episode'][1][1]:
|
||||||
|
print map['map_episode'][1][1]['scene']['season']
|
||||||
|
'''
|
||||||
|
if not isinstance(data, list):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
map = {'map_episode': {}, 'map_absolute': {}}
|
||||||
|
for maps in data:
|
||||||
|
origin = maps.pop(master, None)
|
||||||
|
if origin is None:
|
||||||
|
continue # No master origin to map to
|
||||||
|
map.get('map_episode').setdefault(origin['season'], {}).setdefault(origin['episode'], maps.copy())
|
||||||
|
map.get('map_absolute').setdefault(origin['absolute'], maps.copy())
|
||||||
|
|
||||||
|
return map
|
||||||
|
|
||||||
|
def isDisabled(self):
|
||||||
|
if __name__ == '__main__':
|
||||||
|
return False
|
||||||
|
if self.conf('enabled'):
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
#XXX: REMOVE, just for degugging
|
||||||
|
def main():
|
||||||
|
"""Simple example of using xem
|
||||||
|
"""
|
||||||
|
xem_instance = Xem()
|
||||||
|
print xem_instance.getShowInfo(identifier=73141) # (American Dad)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user