def process_artwork(self, details, tmdbtype): try: if self.dbtype not in ['movies', 'tvshows', 'episodes' ] and tmdbtype not in ['movie', 'tv']: return pre_item = self.pre_item details = self.get_fanarttv_artwork( details, tmdbtype) if self.addon.getSettingBool( 'service_fanarttv_lookup') else details details = self.get_kodi_artwork( details, self.dbtype, self.dbid) if self.addon.getSettingBool( 'local_db') else details if not self.is_same_item(update=False, pre_item=pre_item): return self.set_iter_properties(details, _setmain_artwork) # Crop Image if details.get('clearlogo') and xbmc.getCondVisibility( "Skin.HasSetting(TMDbHelper.EnableCrop)"): self.crop_img = ImageFunctions( method='crop', artwork=details.get('clearlogo')) self.crop_img.setName('crop_img') self.crop_img.start() except Exception as exc: utils.kodi_log(u'Func: process_ratings\n{}'.format(exc), 1)
def library_autoupdate(self): utils.kodi_log('UPDATING TV SHOWS LIBRARY', 1) basedir_tv = self.addon.getSettingString('tvshows_library') or 'special://profile/addon_data/plugin.video.themoviedb.helper/tvshows/' for f in xbmcvfs.listdir(basedir_tv)[0]: try: folder = basedir_tv + f + '/' # Get nfo file nfo = None for x in xbmcvfs.listdir(folder)[1]: if x.endswith('.nfo'): nfo = x if not nfo: continue # Read nfo file vfs_file = xbmcvfs.File(folder + nfo) content = '' try: content = vfs_file.read() finally: vfs_file.close() tmdb_id = content.replace('https://www.themoviedb.org/tv/', '') if not tmdb_id: continue # Get the tvshow url = 'plugin://plugin.video.themoviedb.helper/?info=seasons&tmdb_id={}&type=tv'.format(tmdb_id) context.library_addtvshow(basedir=basedir_tv, folder=f, url=url, tmdb_id=tmdb_id) except Exception as exc: utils.kodi_log('LIBRARY AUTO UPDATE ERROR:\n{}'.format(exc)) if self.addon.getSettingBool('auto_update'): xbmc.executebuiltin('UpdateLibrary(video, {})'.format(basedir_tv))
def library_createfile(filename, content, *args, **kwargs): """ Create the file and folder structure: filename=.strm file, content= content of file. *args = folders to create. """ file_ext = kwargs.pop('file_ext', 'strm') clean_url = kwargs.pop('clean_url', True) path = kwargs.pop('basedir', '') path = path.replace('\\', '/') if not path: utils.kodi_log(u'ADD LIBRARY -- No basedir specified!', 2) return content = library_cleancontent(content) if clean_url else content for folder in args: folder = utils.validify_filename(folder) path = '{}{}/'.format(path, folder) if not content: utils.kodi_log(u'ADD LIBRARY -- No content specified!', 2) return if not filename: utils.kodi_log(u'ADD LIBRARY -- No filename specified!', 2) return if not library_createpath(path): xbmcgui.Dialog().ok( xbmc.getLocalizedString(20444), _addon.getLocalizedString(32122) + ' [B]{}[/B]'.format(path), _addon.getLocalizedString(32123)) utils.kodi_log(u'ADD LIBRARY -- XBMCVFS unable to create path:\n{}'.format(path), 2) return filepath = '{}{}.{}'.format(path, utils.validify_filename(filename), file_ext) f = xbmcvfs.File(filepath, 'w') f.write(utils.try_encode_string(content)) f.close() utils.kodi_log(u'ADD LIBRARY -- Successfully added:\n{}\n{}'.format(filepath, content), 2) return filepath
def play(self): utils.kodi_log('Script -- Attempting to play item:\n{0}'.format(self.params), 2) if not self.params.get('play') or not self.params.get('tmdb_id'): return Player().play( itemtype=self.params.get('play'), tmdb_id=self.params.get('tmdb_id'), season=self.params.get('season'), episode=self.params.get('episode'))
def build_selectbox(self, clearsetting=False, assertplayers=True): self.itemlist, self.actions = [], [] if clearsetting: self.itemlist.append( xbmcgui.ListItem( xbmc.getLocalizedString(13403))) # Clear Default if self.is_local: self.append_playeraction(u'{0} {1}'.format( self.addon.getLocalizedString(32061), 'Kodi'), self.is_local, identifier='play_kodi') for i in sorted(self.play_movie, key=lambda x: x[1]): self.build_playeraction(i[0], 'play_movie', assertplayers=assertplayers) for i in sorted(self.search_movie, key=lambda x: x[1]): self.build_playeraction(i[0], 'search_movie', assertplayers=assertplayers) for i in sorted(self.play_episode, key=lambda x: x[1]): self.build_playeraction(i[0], 'play_episode', assertplayers=assertplayers) for i in sorted(self.search_episode, key=lambda x: x[1]): self.build_playeraction(i[0], 'search_episode', assertplayers=assertplayers) utils.kodi_log( u'Player -- Built {} Players!\n{}'.format(len(self.itemlist), self.identifierlist), 2)
def on_authenticated(self, auth_dialog=True): """Triggered when device authentication has been completed""" utils.kodi_log(u'Trakt Authenticated Successfully!', 1) self.addon.setSettingString('trakt_token', dumps(self.authorization)) self.headers['Authorization'] = 'Bearer {0}'.format(self.authorization.get('access_token')) if auth_dialog: self.auth_dialog.close()
def get_stored_token(self): try: token = loads(self.addon.getSettingString('trakt_token')) or {} except Exception as exc: token = {} utils.kodi_log(exc, 1) return token
def set_property(self, key, value): try: if value is None: _homewindow.clearProperty('{}.{}'.format(self.property_basename, key)) else: _homewindow.setProperty('{}.{}'.format(self.property_basename, key), u'{0}'.format(value)) except Exception as exc: utils.kodi_log(u'{0}{1}'.format(key, exc), 1)
def get_tmdb_id(self, itemtype, imdb_id=None, query=None, year=None): try: if imdb_id and imdb_id.startswith('tt'): return self.tmdb.get_tmdb_id(itemtype=itemtype, imdb_id=imdb_id) return self.tmdb.get_tmdb_id(itemtype=itemtype, query=query, year=year) except Exception as exc: utils.kodi_log(u'Func: get_tmdb_id\n{0}'.format(exc), 1) return
def library_autoupdate(self, list_slug=None, user_slug=None): busy_dialog = True if self.params.get('busy_dialog') else False utils.kodi_log(u'UPDATING TV SHOWS LIBRARY', 1) xbmcgui.Dialog().notification('TMDbHelper', 'Auto-Updating Library...') basedir_tv = self.addon.getSettingString( 'tvshows_library' ) or 'special://profile/addon_data/plugin.video.themoviedb.helper/tvshows/' list_slug = list_slug or self.addon.getSettingString( 'monitor_userlist') or '' user_slug = user_slug or TraktAPI().get_usernameslug() if user_slug and list_slug: for i in list_slug.split(' | '): context.library_userlist(user_slug=user_slug, list_slug=i, confirmation_dialog=False, allow_update=False, busy_dialog=busy_dialog) p_dialog = xbmcgui.DialogProgressBG() if busy_dialog else None p_dialog.create('TMDbHelper', 'Adding items to library...') if p_dialog else None for f in xbmcvfs.listdir(basedir_tv)[0]: try: folder = basedir_tv + f + '/' # Get nfo file nfo = None for x in xbmcvfs.listdir(folder)[1]: if x.endswith('.nfo'): nfo = x if not nfo: continue # Read nfo file vfs_file = xbmcvfs.File(folder + nfo) content = '' try: content = vfs_file.read() finally: vfs_file.close() tmdb_id = content.replace('https://www.themoviedb.org/tv/', '') tmdb_id = tmdb_id.replace('&islocal=True', '') if not tmdb_id: continue # Get the tvshow url = 'plugin://plugin.video.themoviedb.helper/?info=seasons&tmdb_id={}&type=tv'.format( tmdb_id) context.library_addtvshow(basedir=basedir_tv, folder=f, url=url, tmdb_id=tmdb_id, p_dialog=p_dialog) except Exception as exc: utils.kodi_log(u'LIBRARY AUTO UPDATE ERROR:\n{}'.format(exc)) p_dialog.close() if p_dialog else None if self.addon.getSettingBool('auto_update'): xbmc.executebuiltin('UpdateLibrary(video)')
def colors(self, source): filename = '{}.png'.format(utils.md5hash(source)) destination = self.save_path + filename try: if xbmcvfs.exists(destination): os.utime(destination, None) img = Image.open(xbmcvfs.translatepath(destination)) else: img = _openimage(source, self.save_path, filename) img.thumbnail((256, 256)) img = img.convert('RGB') img.save(destination) avg_rgb = self.get_avg_color(img) maincolor_rgb = self.get_maincolor(*avg_rgb) maincolor_hex = self.rgb_to_hex(*self.get_color_lumsat( *maincolor_rgb)) compcolor_rgb = self.get_compcolor(*avg_rgb) compcolor_hex = self.rgb_to_hex(*self.get_color_lumsat( *compcolor_rgb)) maincolor_propname = self.save_prop + '.Main' maincolor_propchek = self.save_prop + '.MainCheck' maincolor_propvalu = _homewindow.getProperty(maincolor_propname) if not maincolor_propvalu: _homewindow.setProperty(maincolor_propname, maincolor_hex) else: _homewindow.setProperty(maincolor_propchek, maincolor_propvalu) thread_maincolor = Thread(target=self.set_prop_colorgradient, args=[ maincolor_propname, maincolor_propvalu, maincolor_hex, maincolor_propchek ]) thread_maincolor.start() compcolor_propname = self.save_prop + '.Comp' compcolor_propchek = self.save_prop + '.CompCheck' compcolor_propvalu = _homewindow.getProperty(compcolor_propname) if not compcolor_propvalu: _homewindow.setProperty(compcolor_propname, compcolor_hex) else: _homewindow.setProperty(compcolor_propchek, compcolor_propvalu) thread_compcolor = Thread(target=self.set_prop_colorgradient, args=[ compcolor_propname, compcolor_propvalu, compcolor_hex, compcolor_propchek ]) thread_compcolor.start() img.close() return maincolor_hex except Exception as exc: utils.kodi_log(exc, 1) return ''
def library_createpath(path): if xbmcvfs.exists(path): return path if xbmcvfs.mkdirs(path): utils.kodi_log(u'ADD LIBRARY -- Created path:\n{}'.format(path), 2) return path if _addon.getSettingBool('ignore_folderchecking'): utils.kodi_log(u'ADD LIBRARY -- xbmcvfs reports folder does NOT exist:\n{}\nIGNORING ERROR: User set folder checking to ignore'.format(path), 2) return path
def play(self): utils.kodi_log(u'Script -- Attempting to play item:\n{0}'.format(self.params), 2) if not self.params.get('play') or not self.params.get('tmdb_id'): return Player().play( itemtype=self.params.get('play'), tmdb_id=self.params.get('tmdb_id'), season=self.params.get('season'), episode=self.params.get('episode'), force_dialog=self.params.get('force_dialog')) self.home.clearProperty('TMDbHelper.Player.ResolvedUrl') # Clear our lock property
def get_listitem(self): try: self.get_container() if self.is_same_item(): return # Current item was the previous item so no need to do a look-up self.cur_folder = '{0}{1}{2}'.format( self.container, xbmc.getInfoLabel(self.get_dbtype()), xbmc.getInfoLabel('{0}NumItems'.format(self.container))) if self.cur_folder != self.pre_folder: self.clear_properties() # Clear props if the folder changed self.pre_folder = self.cur_folder if self.get_infolabel('Label') == '..': self.clear_properties() return # Parent folder so clear properties and don't do look-up if self.dbtype in ['tvshows', 'seasons', 'episodes']: tmdbtype = 'tv' elif self.dbtype in ['movies']: tmdbtype = 'movie' elif self.dbtype in ['sets']: tmdbtype = 'collection' elif self.dbtype in ['actors', 'directors']: tmdbtype = 'person' else: return self.home.setProperty('TMDbHelper.IsUpdating', 'True') tmdb_id = self.get_tmdb_id( tmdbtype, self.imdb_id, self.query, self.year if tmdbtype == 'movie' else None) details = self.tmdb.get_detailed_item(tmdbtype, tmdb_id, season=self.season, episode=self.episode) details = self.get_kodi_person_stats( details) if tmdbtype == 'person' else details details = self.get_omdb_ratings( details) if tmdbtype == 'movie' else details details = self.get_top250_rank( details) if tmdbtype == 'movie' else details details = self.get_fanarttv_artwork(details, tmdbtype) details = self.get_trakt_ratings( details, tmdbtype, tmdb_id, self.season, self.episode) if tmdbtype in ['movie', 'tv'] else details if not details or not self.is_same_item(): self.clear_properties( ) # No details or the item changed so let's clear everything return self.set_properties(details) except Exception as exc: utils.kodi_log('Func: get_listitem\n{0}'.format(exc), 1)
def player_resolveurl(self, player=None): if not player or not player[1] or not isinstance(player[1], list): return player # No player configured or not a list of actions so return keyboard_input = None player_actions = player[1] player = (False, player_actions[0] ) # player tuple is: isPlayable flag; path URI to call. for action in player_actions[1:]: # If playable item was found in last action then let's break and play it if player[0]: break # Start thread with keyboard inputter if needed if action.get('keyboard'): if action.get('keyboard') in [ 'Up', 'Down', 'Left', 'Right', 'Select' ]: keyboard_input = KeyboardInputter( action="Input.{}".format(action.get('keyboard'))) else: keyboard_input = KeyboardInputter(text=string_format_map( action.get('keyboard', ''), self.item)) keyboard_input.setName('keyboard_input') keyboard_input.start() continue # Go to next action # Get the next folder from the plugin with utils.busy_dialog(): folder = KodiLibrary().get_directory( string_format_map(player[1], self.item)) # Kill our keyboard inputter thread if keyboard_input: keyboard_input.exit = True keyboard_input = None # Special option to show dialog of items to select from if action.get('dialog'): auto = True if action.get('dialog', '').lower() == 'auto' else False return self.player_dialogselect(folder, auto=auto) utils.kodi_log( 'Player -- Retrieved Folder\n{}'.format( string_format_map(player[1], self.item)), 2) # Iterate through plugin folder looking for item that matches rules player = self.player_applyrules(folder, action) or player if player == -1: break return player
def call_service(self): call_id = utils.try_parse_int(self.params.get('call_auto')) kodi_id = call_id + 10000 if call_id < 10000 else call_id # Convert to Kodi ID in 10000 range my_call_id = None if self.first_run else call_id # Close info dialogs if still open if xbmc.getCondVisibility('Window.IsVisible({})'.format(ID_VIDEOINFO)): xbmc.executebuiltin('Dialog.Close({})'.format(ID_VIDEOINFO)) if not self.wait_for_id(to_close=True, window_id=ID_VIDEOINFO, call_id=my_call_id): return self.call_reset() # Clear and exit if timeout or user closed base window # If we're at 0 then close and exit if self.get_position() == 0: xbmc.executebuiltin('Action(Back)') return self.call_reset(openinfo=True) # Clear and exit but reopen original info dialog # Open our call_id window if first run if self.first_run: xbmc.executebuiltin('ActivateWindow({})'.format(call_id)) if not self.wait_for_id(window_id=call_id, poll=0.5): return self.call_reset() # Clear and exit if timeout window = xbmcgui.Window(kodi_id) # Check that list 9999 exists controllist = window.getControl(9999) if not controllist: utils.kodi_log(u'SKIN ERROR!\nList control 9999 not available in Window {0}'.format(call_id), 1) return self.call_reset() # Clear and exit if timeout or user closed base window controllist.reset() # Wait until container updates self.monitor.waitForAbort(1) if not self.wait_for_update(call_id=call_id): return self.call_reset() # Clear and exit if timeout or user closed base window # Open info dialog window.setFocus(controllist) xbmc.executebuiltin('SetFocus(9999,0,absolute)') xbmc.executebuiltin('Action(Info)') if not self.wait_for_id(window_id=ID_VIDEOINFO, call_id=call_id): return self.call_reset() # Clear and exit if timeout or user closed base window # Wait for action func = None while not self.monitor.abortRequested() and not func: current_path = self.home.getProperty(self.prefixcurrent) if not xbmc.getCondVisibility("Window.IsVisible({})".format(call_id)): func = self.call_reset # User closed out everything so let's do the nuclear option elif not xbmc.getCondVisibility("Window.IsVisible({})".format(ID_VIDEOINFO)): func = self.call_previous # Dialog closed so we should delete the path and call loopback elif self.added_path != current_path: self.added_path = current_path func = self.call_service # New path added so call loopback self.monitor.waitForAbort(0.5) # Poll every X self.first_run = False func()
def clear_dir(self, folder): for filename in os.listdir(folder): file_path = os.path.join(folder, filename) try: if os.path.isfile(file_path): os.unlink(file_path) elif os.path.isdir(file_path): self.recursive_delete_dir(file_path) except Exception as e: utils.kodi_log(u'Could not delete file {0}: {1}'.format(file_path, str(e)))
def set_list_properties(self, items, key, prop): if not isinstance(items, list): return try: joinlist = [i.get(key) for i in items[:10] if i.get(key)] joinlist = ' / '.join(joinlist) self.properties.add(prop) self.set_property(prop, joinlist) except Exception as exc: utils.kodi_log(u'Func: set_list_properties\n{0}'.format(exc), 1)
def get_jsonrpc(self, method=None, params=None): if not method or not params: return {} query = {"jsonrpc": "2.0", "params": params, "method": method, "id": 1} try: response = json.loads(xbmc.executeJSONRPC(json.dumps(query))) except Exception as exc: utils.kodi_log(u'TMDbHelper - JSONRPC Error:\n{}'.format(exc), 1) response = {} return response
def _openimage(image, targetpath, filename): """ Open image helper with thanks to sualfred """ # some paths require unquoting to get a valid cached thumb hash cached_image_path = urllib.unquote(image.replace('image://', '')) if cached_image_path.endswith('/'): cached_image_path = cached_image_path[:-1] cached_files = [] for path in [xbmc.getCacheThumbName(cached_image_path), xbmc.getCacheThumbName(image)]: cached_files.append(os.path.join('special://profile/Thumbnails/', path[0], path[:-4] + '.jpg')) cached_files.append(os.path.join('special://profile/Thumbnails/', path[0], path[:-4] + '.png')) cached_files.append(os.path.join('special://profile/Thumbnails/Video/', path[0], path)) for i in range(1, 4): try: ''' Try to get cached image at first ''' for cache in cached_files: if xbmcvfs.exists(cache): try: img = Image.open(xbmc.translatePath(cache)) return img except Exception as error: utils.kodi_log('Image error: Could not open cached image --> %s' % error, 2) ''' Skin images will be tried to be accessed directly. For all other ones the source will be copied to the addon_data folder to get access. ''' if xbmc.skinHasImage(image): if not image.startswith('special://skin'): image = os.path.join('special://skin/media/', image) try: # in case image is packed in textures.xbt img = Image.open(xbmc.translatePath(image)) return img except Exception: return '' else: targetfile = os.path.join(targetpath, filename) if not xbmcvfs.exists(targetfile): xbmcvfs.copy(image, targetfile) img = Image.open(targetfile) return img except Exception as error: utils.kodi_log('Image error: Could not get image for %s (try %d) -> %s' % (image, i, error), 2) xbmc.sleep(500) pass return ''
def add_userlist(user_slug=None, list_slug=None, confirm=True, allow_update=True, busy_dialog=True, force=False): user_slug = user_slug or sys.listitem.getProperty('Item.user_slug') list_slug = list_slug or sys.listitem.getProperty('Item.list_slug') request = get_userlist(user_slug=user_slug, list_slug=list_slug, confirm=confirm, busy_dialog=busy_dialog) i_total = len(request) p_dialog = xbmcgui.DialogProgressBG() if busy_dialog else None p_dialog.create('TMDbHelper', _addon.getLocalizedString(32166)) if p_dialog else None all_movies = [] all_tvshows = [] for i_count, i in enumerate(request): i_type = i.get('type') if i_type not in ['movie', 'show']: continue item = i.get(i_type, {}) tmdb_id = item.get('ids', {}).get('tmdb') imdb_id = item.get('ids', {}).get('imdb') tvdb_id = item.get('ids', {}).get('tvdb') if not tmdb_id: # Extra request for ID lookup is too expensive so skip utils.kodi_log(u'{} ({}) - Missing TMDb ID! Skipping...'.format(item.get('title'), item.get('year')), 2) continue if p_dialog: p_dialog.update( ((i_count + 1) * 100) // i_total, message=u'Adding {} ({})...'.format(item.get('title'), item.get('year'))) if i_type == 'movie': playlist_item = add_movie(tmdb_id=tmdb_id, imdb_id=imdb_id, title=item.get('title'), year=item.get('year')) all_movies.append(playlist_item) if i_type == 'show': playlist_item = ('title', item.get('title')) all_tvshows.append(playlist_item) add_tvshow( basedir=_basedir_tv, folder=u'{}'.format(item.get('title')), url='plugin://plugin.video.themoviedb.helper/?info=seasons&nextpage=True&tmdb_id={}&type=tv'.format(tmdb_id), tmdb_id=tmdb_id, imdb_id=imdb_id, tvdb_id=tvdb_id, p_dialog=p_dialog, force=force) if p_dialog: p_dialog.close() if all_movies: create_playlist(all_movies, 'movies', user_slug, list_slug) if all_tvshows: create_playlist(all_tvshows, 'tvshows', user_slug, list_slug) if allow_update and _addon.getSettingBool('auto_update'): xbmc.executebuiltin('UpdateLibrary(video)')
def library_addtvshow(basedir=None, folder=None, url=None, tmdb_id=None, tvdb_id=None, imdb_id=None, p_dialog=None): if not basedir or not folder or not url: return nfo_tmdbid = library_getnfo_tmdbid(basedir, folder) # Check the nfo file in the folder to make sure it matches the TMDB ID if nfo_tmdbid and utils.try_parse_int(nfo_tmdbid) != utils.try_parse_int(tmdb_id): folder += ' (TMDB {})'.format(tmdb_id) # If different tvshow with same name exists create new folder with TMDB ID added details_tvshow = _plugin.tmdb.get_request_sc('tv', tmdb_id) # Get base tv show if not details_tvshow: return library_create_nfo('tv', tmdb_id, folder, basedir=basedir) # Create .nfo for tvshow seasons = [i.get('season_number') for i in details_tvshow.get('seasons', []) if i.get('season_number', 0) != 0] # Don't get specials s_count, s_total = 0, len(seasons) # Used to update p_dialog progress for season in seasons: s_count += 1 season_name = u'Season {}'.format(season) # Originally made a bad choice here to localise season name but reverted that now if p_dialog: # Update our progress dialog p_dialog_val = (s_count * 100) // s_total p_dialog_msg = u'Adding {} - {} to library...'.format(details_tvshow.get('original_name'), season_name) p_dialog.update(p_dialog_val, message=p_dialog_msg) details_season = _plugin.tmdb.get_request_sc('tv', tmdb_id, 'season', season) # Get season if not details_season: return episodes = [i for i in details_season.get('episodes', []) if i.get('episode_number', 0) != 0] # Only get non-special seasons e_count, e_total = 0, len(episodes) # Used to update p_dialog progress for episode in episodes: e_count += 1 episode_name = 'S{:02d}E{:02d} - {}'.format( utils.try_parse_int(season), utils.try_parse_int(episode.get('episode_number')), utils.validify_filename(episode.get('name'))) # Skip future episodes if _addon.getSettingBool('hide_unaired_episodes'): if not episode.get('air_date') or utils.convert_timestamp(episode.get('air_date'), "%Y-%m-%d", 10) > datetime.datetime.now(): p_dialog.update((e_count * 100) // e_total, message=u'{} not aired yet. Skipping...'.format(episode_name)) if p_dialog else None continue # Check if item has already been added if _plugin.get_db_info(info='dbid', tmdbtype='episode', imdb_id=imdb_id, tmdb_id=tmdb_id, season=season, episode=episode.get('episode_number')): utils.kodi_log(u'Add to Library\nFound {} - {} in library. Skipping...'.format(episode.get('showtitle'), episode_name)) p_dialog.update((e_count * 100) // e_total, message=u'Found {} in library. Skipping...'.format(episode_name)) if p_dialog else None continue p_dialog.update((e_count * 100) // e_total, message=u'Adding {} to library...'.format(episode_name)) if p_dialog else None episode_path = 'plugin://plugin.video.themoviedb.helper/?info=play&type=episode&islocal=True' episode_path += '&tmdb_id={}&season={}&episode={}'.format(tmdb_id, season, episode.get('episode_number')) library_createfile(episode_name, episode_path, folder, season_name, basedir=basedir)
def build_details(self): self.item['id'] = self.tmdb_id self.item['tmdb'] = self.tmdb_id self.item['imdb'] = self.details.get('infolabels', {}).get('imdbnumber') self.item['name'] = u'{0} ({1})'.format(self.item.get('title'), self.item.get('year')) self.item['premiered'] = self.item['firstaired'] = self.item['released'] = self.details.get('infolabels', {}).get('premiered') self.item['plot'] = self.details.get('infolabels', {}).get('plot') self.item['cast'] = self.item['actors'] = " / ".join([i.get('name') for i in self.details.get('cast', []) if i.get('name')]) self.item['showname'] = self.item['clearname'] = self.item['tvshowtitle'] = self.item['title'] = self.item.get('title') self.item['thumbnail'] = self.details.get('thumb') self.item['poster'] = self.details.get('poster') self.item['fanart'] = self.details.get('fanart') self.item['now'] = datetime.datetime.now().strftime('%Y%m%d%H%M%S%f') if self.traktapi: slug_type = utils.type_convert(self.tmdbtype, 'trakt') trakt_details = self.traktapi.get_details(slug_type, self.traktapi.get_traktslug(slug_type, 'tmdb', self.tmdb_id)) self.item['trakt'] = trakt_details.get('ids', {}).get('trakt') self.item['imdb'] = self.details.get('infoproperties', {}).get('tvshow.imdb_id') or trakt_details.get('ids', {}).get('imdb') self.item['tvdb'] = self.details.get('infoproperties', {}).get('tvshow.tvdb_id') or trakt_details.get('ids', {}).get('tvdb') self.item['slug'] = trakt_details.get('ids', {}).get('slug') if self.itemtype == 'episode': # Do some special episode stuff self.item['id'] = self.item.get('tvdb') self.item['title'] = self.details.get('infolabels', {}).get('title') # Set Episode Title self.item['name'] = u'{0} S{1:02d}E{2:02d}'.format(self.item.get('showname'), int(utils.try_parse_int(self.season)), int(utils.try_parse_int(self.episode))) self.item['season'] = self.season self.item['episode'] = self.episode self.item['showpremiered'] = self.details.get('infoproperties', {}).get('tvshow.premiered') self.item['showyear'] = self.details.get('infoproperties', {}).get('tvshow.year') if self.traktapi and self.itemtype == 'episode': trakt_details = self.traktapi.get_details(slug_type, self.item.get('slug'), season=self.season, episode=self.episode) self.item['epid'] = self.details.get('infoproperties', {}).get('tvdb_id') or trakt_details.get('ids', {}).get('tvdb') self.item['epimdb'] = trakt_details.get('ids', {}).get('imdb') self.item['eptmdb'] = self.details.get('infoproperties', {}).get('tmdb_id') or trakt_details.get('ids', {}).get('tmdb') self.item['eptrakt'] = trakt_details.get('ids', {}).get('trakt') utils.kodi_log(u'Player Details - Item:\n{}'.format(self.item), 2) for k, v in self.item.copy().items(): if k not in constants.PLAYER_URLENCODE: continue v = u'{0}'.format(v) for key, value in {k: v, '{}_meta'.format(k): dumps(v)}.items(): self.item[key] = value.replace(',', '') self.item[key + '_+'] = value.replace(',', '').replace(' ', '+') self.item[key + '_-'] = value.replace(',', '').replace(' ', '-') self.item[key + '_escaped'] = quote(quote(utils.try_encode_string(value))) self.item[key + '_escaped+'] = quote(quote_plus(utils.try_encode_string(value))) self.item[key + '_url'] = quote(utils.try_encode_string(value)) self.item[key + '_url+'] = quote_plus(utils.try_encode_string(value))
def close_dialog(self): self.reset_props() xbmc.executebuiltin('Dialog.Close({})'.format(ID_VIDEOINFO)) close_id = utils.try_parse_int(self.params.get('close_dialog')) if close_id and xbmc.getCondVisibility('Window.IsVisible({})'.format(close_id)): close_id = close_id + 10000 if close_id < 10000 else close_id try: xbmcgui.Window(close_id).close() except Exception as exc: utils.kodi_log(exc, 1) if self.params.get('playmedia'): xbmc.executebuiltin('PlayMedia({})'.format(self.params.get('playmedia'))) self.call_window()
def process_ratings(self, details, tmdbtype, tmdb_id): try: if tmdbtype not in ['movie', 'tv']: return pre_item = self.pre_item details = self.get_omdb_ratings(details) details = self.get_top250_rank(details) if tmdbtype == 'movie' else details details = self.get_trakt_ratings(details, tmdbtype, tmdb_id, self.season, self.episode) if tmdbtype in ['movie', 'tv'] else details if not self.is_same_item(update=False, pre_item=pre_item): return self.set_iter_properties(details.get('infoproperties', {}), _setprop_ratings) except Exception as exc: utils.kodi_log(u'Func: process_ratings\n{}'.format(exc), 1)
def add_movie(tmdb_id, imdb_id=None, title='', year=''): content = 'plugin://plugin.video.themoviedb.helper/?info=play&tmdb_id={}&type=movie'.format(tmdb_id) folder = u'{} ({})'.format(title, year) db_file = _plugin.get_db_info(info='file', tmdbtype='movie', imdb_id=imdb_id, tmdb_id=tmdb_id) if not db_file: log_msg = u'Adding {} to library...'.format(folder) db_file = create_file(folder, content, folder, basedir=_basedir_movie) create_nfo('movie', tmdb_id, folder, basedir=_basedir_movie) else: log_msg = u'Found {} in library.'.format(folder) utils.kodi_log(log_msg) return ('filename', db_file.replace('\\', '/').split('/')[-1])
def player_applyrules(self, folder, action): for x, f in enumerate(folder): for k, v in action.items(): # Iterate through our key (infolabel) / value (infolabel must match) pairs of our action if k == 'position': # We're looking for an item position not an infolabel if utils.try_parse_int(string_format_map(v, self.item)) != x + 1: # Format our position value and add one since people are dumb and don't know that arrays start at 0 break # Not the item position we want so let's go to next item in folder elif not f.get(k) or not re.match(string_format_map(v, self.item), u'{}'.format(f.get(k, ''))): # Format our value and check if it regex matches the infolabel key break # Item's key value doesn't match value we are looking for so let's got to next item in folder else: # Item matched our criteria so let's return it utils.kodi_log('Player -- Found Match!\n{}'.format(f), 2) resolve_url = True if f.get('filetype') == 'file' else False # Set true for files so we can play return (resolve_url, f.get('file')) # Get ListItem.FolderPath for item and return as player utils.kodi_log('Player -- Failed to find match!\n{}'.format(action), 2) return -1 # Got through the entire folder without a match so ask user to select new player
def get_api_request(self, request=None, is_json=True, postdata=None, headers=None, dictify=True): """ Make the request to the API by passing a url request string """ if utils.get_timestamp(self.req_connect_err): return {} if dictify else None # Connection error in last minute for this api so don't keep trying if self.req_wait_time: utils.rate_limiter(addon_name=self.cache_name, wait_time=self.req_wait_time, api_name=self.req_api_name) try: response = requests.post(request, data=postdata, headers=headers) if postdata else requests.get(request, headers=headers) # Request our data except Exception as err: self.req_connect_err = utils.set_timestamp() xbmcgui.Window(10000).setProperty(self.req_connect_err_prop, str(self.req_connect_err)) utils.kodi_log(u'ConnectionError: {}\nSuppressing retries for 1 minute'.format(err), 1) return {} if dictify else None if not response.status_code == requests.codes.ok and utils.try_parse_int(response.status_code) >= 400: # Error Checking if response.status_code == 401: utils.kodi_log(u'HTTP Error Code: {0}\nRequest: {1}\nPostdata: {2}\nHeaders: {3}\nResponse: {4}'.format(response.status_code, request, postdata, headers, response), 1) self.invalid_apikey() elif response.status_code == 500: self.req_connect_err = utils.set_timestamp() xbmcgui.Window(10000).setProperty(self.req_connect_err_prop, str(self.req_connect_err)) utils.kodi_log(u'HTTP Error Code: {0}\nRequest: {1}\nSuppressing retries for 1 minute'.format(response.status_code, request), 1) elif utils.try_parse_int(response.status_code) > 400: # Don't write 400 error to log utils.kodi_log(u'HTTP Error Code: {0}\nRequest: {1}'.format(response.status_code, request), 1) return {} if dictify else None if dictify and is_json: response = response.json() # Make the response nice elif dictify: response = self.translate_xml(response) return response
def play_external(self, playerindex=-1, force_dialog=False): playerindex = self.player_getnewindex(playerindex, force_dialog=force_dialog) # User cancelled dialog if not playerindex > -1: utils.kodi_log(u'Player -- User cancelled', 2) return False # Run through player actions player = self.player_resolveurl(self.actions[playerindex]) # Previous player failed so ask user to select a new one if player == -1: return self.play_external(playerindex, force_dialog=force_dialog) # Play/Search found item if player and player[1]: action = string_format_map(player[1], self.item) if player[0] and (action.endswith('.strm') or self.identifierlist[playerindex] == 'play_kodi'): # Action is play and is a strm/local so PlayMedia utils.kodi_log(u'Player -- Found strm or local.\nAttempting PLAYMEDIA({})'.format(action), 1) xbmc.executebuiltin(utils.try_decode_string(u'PlayMedia(\"{0}\")'.format(action))) elif player[0]: # Action is play and not a strm so play with player utils.kodi_log(u'Player -- Found file.\nAttempting to PLAY: {}'.format(action), 2) xbmcgui.Window(10000).setProperty('TMDbHelper.PlayerInfoString', self.playerstring) if self.playerstring else None xbmc.Player().play(action, ListItem(library='video', **self.details).set_listitem()) else: action = u'Container.Update({0})'.format(action) if xbmc.getCondVisibility("Window.IsMedia") else u'ActivateWindow(videos,{0},return)'.format(action) utils.kodi_log(u'Player -- Found folder.\nAttempting to OPEN: {}'.format(action), 2) xbmc.executebuiltin(utils.try_encode_string(utils.try_decode_string(action))) return action
def set_url_props(self, url, prefix='Item'): for k, v in url.items(): if not k or not v: continue try: self.infoproperties[u'{}.{}'.format( prefix, utils.try_decode_string(k))] = u'{}'.format( utils.try_decode_string(v)) except Exception as exc: utils.kodi_log( u'ERROR in ListItem set_url_props\nk:{} v:{}'.format( utils.try_decode_string(k), utils.try_decode_string(v)), 1) utils.kodi_log(exc, 1)