def exported(self, pathitems=None): """List all items that are exported to the Kodi library""" chunked_video_list, perpetual_range_selector = lib_utils.list_contents(self.perpetual_range_start) if chunked_video_list: self._exported_directory(pathitems, chunked_video_list, perpetual_range_selector) else: ui.show_notification(common.get_local_string(30111)) xbmcplugin.endOfDirectory(G.PLUGIN_HANDLE, succeeded=False)
def _activate_profile(self, guid): pin_result = verify_profile_pin(guid) if not pin_result: if pin_result is not None: ui.show_notification(common.get_local_string(30106), time=8000) return False common.make_call('activate_profile', guid) return True
def set_autoupdate_device(self, pathitems): # pylint: disable=unused-argument """Set the current device to manage auto-update of the shared-library (MySQL)""" if _check_auto_update_running(): return random_uuid = common.get_random_uuid() G.LOCAL_DB.set_value('client_uuid', random_uuid) G.SHARED_DB.set_value('auto_update_device_uuid', random_uuid) ui.show_notification(common.get_local_string(30209), time=8000)
def play(videoid): """Play an episode or movie as specified by the path""" common.info('Playing {}', videoid) is_up_next_enabled = g.ADDON.getSettingBool('UpNextNotifier_enabled') metadata = [{}, {}] try: metadata = api.metadata(videoid) common.debug('Metadata is {}', metadata) except MetadataNotAvailable: common.warn('Metadata not available for {}', videoid) # Parental control PIN pin_result = _verify_pin(metadata[0].get('requiresPin', False)) if not pin_result: if pin_result is not None: ui.show_notification(common.get_local_string(30106), time=10000) xbmcplugin.endOfDirectory(g.PLUGIN_HANDLE, succeeded=False) return list_item = get_inputstream_listitem(videoid) infos, art = infolabels.add_info_for_playback(videoid, list_item, is_up_next_enabled) # Workaround for resuming strm files from library resume_position = infos.get('resume', {}).get('position') \ if g.IS_SKIN_CALL and g.ADDON.getSettingBool('ResumeManager_enabled') else None if resume_position: index_selected = ui.ask_for_resume( resume_position) if g.ADDON.getSettingBool( 'ResumeManager_dialog') else None if index_selected == -1: xbmcplugin.setResolvedUrl(handle=g.PLUGIN_HANDLE, succeeded=False, listitem=list_item) return if index_selected == 1: resume_position = None xbmcplugin.setResolvedUrl(handle=g.PLUGIN_HANDLE, succeeded=True, listitem=list_item) upnext_info = get_upnext_info(videoid, (infos, art), metadata) if is_up_next_enabled else None common.debug('Sending initialization signal') common.send_signal(common.Signals.PLAYBACK_INITIATED, { 'videoid': videoid.to_dict(), 'infos': infos, 'art': art, 'timeline_markers': get_timeline_markers(metadata[0]), 'upnext_info': upnext_info, 'resume_position': resume_position }, non_blocking=True) xbmcplugin.setResolvedUrl(handle=g.PLUGIN_HANDLE, succeeded=True, listitem=list_item)
def _display_search_results(search_term): search_results = api.search(search_term) if search_results.videos: listings.build_video_listing(search_results, g.MAIN_MENU_ITEMS['search']) return else: ui.show_notification(common.get_local_string(30013)) xbmcplugin.endOfDirectory(g.PLUGIN_HANDLE, succeeded=False)
def check_autoupdate_device(self, pathitems): # pylint: disable=unused-argument """Check if the current device manage the auto-updates of the shared-library (MySQL)""" uuid = G.SHARED_DB.get_value('auto_update_device_uuid') if uuid is None: msg = common.get_local_string(30212) else: client_uuid = G.LOCAL_DB.get_value('client_uuid') msg = common.get_local_string(30210 if client_uuid == uuid else 30211) ui.show_notification(msg, time=8000)
def logout(self): """Logout of the current account and reset the session""" common.debug('Logging out of current account') cookies.delete(self.account_hash) self._get('logout') common.purge_credentials() common.info('Logout successful') ui.show_notification(common.get_local_string(30113)) self._init_session()
def library_playback_set_profile(self, pathitems=None): # pylint: disable=unused-argument """Save the GUID for the playback from Kodi library""" G.LOCAL_DB.set_value('library_playback_profile_guid', self.params['profile_guid']) profile_name = G.LOCAL_DB.get_profile_config( 'profileName', '???', self.params['profile_guid']) common.container_refresh() ui.show_notification(profile_name, title=common.get_local_string(30052))
def purge_cache(self, pathitems=None): # pylint: disable=unused-argument """Clear the cache. If on_disk param is supplied, also clear cached items from disk""" g.CACHE.invalidate(self.params.get('on_disk', False)) if self.params.get('on_disk', False): common.delete_file( 'resources.lib.services.playback.stream_continuity.ndb') if not self.params.get('no_notification', False): ui.show_notification(common.get_local_string(30135))
def sync_mylist_sel_profile(self, pathitems): # pylint: disable=unused-argument """ Set the current profile for the synchronization of Netflix "My List" with the Kodi library """ g.SHARED_DB.set_value('sync_mylist_profile_guid', g.LOCAL_DB.get_active_profile_guid()) profile_name = g.LOCAL_DB.get_profile_config('profileName', '') ui.show_notification( common.get_local_string(30223).format(profile_name), time=10000)
def library_playback_remove_profile(self, pathitems): # pylint: disable=unused-argument """Remove the GUID for the playback from Kodi library""" G.LOCAL_DB.set_value('library_playback_profile_guid', '') profile_name = G.LOCAL_DB.get_profile_config( 'profileName', '???', self.params['profile_guid']) G.IS_CONTAINER_REFRESHED = True common.container_refresh() ui.show_notification(profile_name, title=common.get_local_string(30053))
def trailer(self, videoid): """Get the trailer list""" video_list = api.supplemental_video_list(videoid, 'trailers') if video_list.videos: url = common.build_url(['supplemental', videoid.value, videoid.mediatype, 'trailers'], mode=g.MODE_DIRECTORY) xbmc.executebuiltin('Container.Update({})'.format(url)) else: ui.show_notification(common.get_local_string(30111))
def activate_profile(guid): """Activate a profile and ask the PIN if required""" pin_result = verify_profile_pin(guid) if not pin_result: if pin_result is not None: ui.show_notification(common.get_local_string(30106), time=8000) return False common.make_call('activate_profile', guid) return True
def autoselect_remove_profile(self, pathitems): # pylint: disable=unused-argument """Remove the GUID from auto-selection""" G.LOCAL_DB.set_value('autoselect_profile_guid', '') profile_name = G.LOCAL_DB.get_profile_config( 'profileName', '???', self.params['profile_guid']) G.IS_CONTAINER_REFRESHED = True common.container_refresh() ui.show_notification(profile_name, title=common.get_local_string(30056))
def autoselect_set_profile(self, pathitems): # pylint: disable=unused-argument """Save the GUID for profile auto-selection""" G.LOCAL_DB.set_value('autoselect_profile_guid', self.params['profile_guid']) profile_name = G.LOCAL_DB.get_profile_config( 'profileName', '???', self.params['profile_guid']) common.container_refresh() ui.show_notification(profile_name, title=common.get_local_string(30055))
def api_error_wrapper(*args, **kwargs): try: return func(*args, **kwargs) except APIError as exc: # This error is raised only when in the API response data the key 'status' has 'error' value # (see _raise_api_error in session/http_requests.py) LOG.error('{} the API call has returned an error: {}', func.__name__, exc) ui.show_notification(common.get_local_string(30118).format(exc))
def _display_search_results(pathitems, perpetual_range_start, dir_update_listing): search_term = pathitems[2] search_results = api.search(search_term, perpetual_range_start) if search_results.videos: listings.build_video_listing(search_results, g.MAIN_MENU_ITEMS['search'], pathitems) _handle_endofdirectory(dir_update_listing) else: ui.show_notification(common.get_local_string(30013)) xbmcplugin.endOfDirectory(g.PLUGIN_HANDLE, succeeded=False)
def search_query(row_id, perpetual_range_start, dir_update_listing): """Perform the research""" # Get item from database search_item = G.LOCAL_DB.get_search_item(row_id) if not search_item: ui.show_error_info('Search error', 'Item not found in the database.') return False # Update the last access data (move on top last used items) if not perpetual_range_start: G.LOCAL_DB.update_search_item_last_access(row_id) # Perform the path call menu_data = G.MAIN_MENU_ITEMS['search'] search_type = search_item['Type'] if search_type == 'text': call_args = { 'menu_data': menu_data, 'search_term': search_item['Value'], 'pathitems': ['search', 'search', row_id], 'perpetual_range_start': perpetual_range_start } list_data, extra_data = common.make_call('get_video_list_search', call_args) elif search_type == 'audio_lang': call_args = { 'menu_data': menu_data, 'pathitems': ['search', 'search', row_id], 'perpetual_range_start': perpetual_range_start, 'context_name': 'spokenAudio', 'context_id': common.convert_from_string(search_item['Parameters'], dict)['lang_code'] } list_data, extra_data = common.make_call('get_video_list_sorted_sp', call_args) elif search_type == 'subtitles_lang': call_args = { 'menu_data': menu_data, 'pathitems': ['search', 'search', row_id], 'perpetual_range_start': perpetual_range_start, 'context_name': 'subtitles', 'context_id': common.convert_from_string(search_item['Parameters'], dict)['lang_code'] } list_data, extra_data = common.make_call('get_video_list_sorted_sp', call_args) elif search_type == 'genre_id': call_args = { 'menu_data': menu_data, 'pathitems': ['search', 'search', row_id], 'perpetual_range_start': perpetual_range_start, 'context_name': 'genres', 'context_id': common.convert_from_string(search_item['Parameters'], dict)['genre_id'] } list_data, extra_data = common.make_call('get_video_list_sorted_sp', call_args) else: raise NotImplementedError('Search type {} not implemented'.format(search_type)) # Show the results if not list_data: ui.show_notification(common.get_local_string(30407)) return False _search_results_directory(search_item['Value'], menu_data, list_data, extra_data, dir_update_listing) return True
def exported(self, pathitems=None): """List all items that are exported to the Kodi library""" # pylint: disable=unused-argument library_contents = library.list_contents() if library_contents: listings.build_video_listing(api.custom_video_list(library_contents), g.MAIN_MENU_ITEMS['exported']) _handle_endofdirectory(self.dir_update_listing) else: ui.show_notification(common.get_local_string(30013)) xbmcplugin.endOfDirectory(g.PLUGIN_HANDLE, succeeded=False)
def purge_cache(self, pathitems=None): # pylint: disable=unused-argument """Clear the cache. If on_disk param is supplied, also clear cached items from disk""" g.CACHE.invalidate(self.params.get('on_disk', False)) common.send_signal(signal=common.Signals.INVALIDATE_SERVICE_CACHE, data={ 'on_disk': self.params.get('on_disk', False), 'bucket_names': None }) if not self.params.get('no_notification', False): ui.show_notification(common.get_local_string(30135))
def _tick_and_wait_for_abort(self): try: self.library_updater.on_service_tick() G.CACHE_MANAGEMENT.on_service_tick() except Exception as exc: # pylint: disable=broad-except import traceback from resources.lib.kodi.ui import show_notification LOG.error(traceback.format_exc()) show_notification(': '.join((exc.__class__.__name__, str(exc)))) return G.SETTINGS_MONITOR.waitForAbort(1)
def _tick_and_wait_for_abort(self): try: self.controller.on_service_tick() self.library_updater.on_service_tick() except Exception as exc: # pylint: disable=broad-except import traceback from resources.lib.kodi.ui import show_notification error(traceback.format_exc()) show_notification(': '.join((exc.__class__.__name__, unicode(exc)))) return self.controller.waitForAbort(1)
def _tick_and_wait_for_abort(self): # pylint: disable=broad-except try: self.controller.on_playback_tick() self.library_updater.on_tick() except Exception as exc: common.error(traceback.format_exc()) ui.show_notification(': '.join( (exc.__class__.__name__, exc.message))) return self.controller.waitForAbort(1)
def check_autoupdate_device(self, pathitems): """Check if the current device manage the auto-updates of the shared-library (MySQL)""" uuid = g.SHARED_DB.get_value('auto_update_device_uuid') if uuid is None: msg = common.get_local_string(30212) else: current_device_uuid = common.get_device_uuid() msg = common.get_local_string(30210) \ if current_device_uuid == uuid else common.get_local_string(30211) ui.show_notification(msg, time=8000)
def _search_add_bygenreid(search_type, genre_id): # If the genre ID exists, the title of the list will be returned title = api.get_genre_title(genre_id) if not title: ui.show_notification(common.get_local_string(30407)) return None # In this case the 'value' is used only as title for the ListItem and not for the query title += ' [{}]'.format(genre_id) row_id = G.LOCAL_DB.insert_search_item(search_type, title, {'genre_id': genre_id}) return row_id
def update_lolomo_context(context_name): """Update the lolomo list by context""" lolomo_root = g.LOCAL_DB.get_value('lolomo_root_id', '', TABLE_SESSION) context_index = g.LOCAL_DB.get_value( 'lolomo_{}_index'.format(context_name.lower()), '', TABLE_SESSION) context_id = g.LOCAL_DB.get_value( 'lolomo_{}_id'.format(context_name.lower()), '', TABLE_SESSION) if not context_index: common.warn( 'Update lolomo context {} skipped due to missing lolomo index', context_name) return path = [['lolomos', lolomo_root, 'refreshListByContext']] # The fourth parameter is like a request-id, but it doesn't seem to match to # serverDefs/date/requestId of reactContext (g.LOCAL_DB.get_value('request_id', table=TABLE_SESSION)) # nor to request_id of the video event request # has a kind of relationship with renoMessageId suspect with the logblob but i'm not sure because my debug crashed, # and i am no longer able to trace the source. # I noticed also that this request can also be made with the fourth parameter empty, # but it still doesn't update the continueWatching list of lolomo, that is strange because of no error params = [ common.enclose_quotes(context_id), context_index, common.enclose_quotes(context_name), '' ] # path_suffixs = [ # [['trackIds', 'context', 'length', 'genreId', 'videoId', 'displayName', 'isTallRow', 'isShowAsARow', # 'impressionToken', 'showAsARow', 'id', 'requestId']], # [{'from': 0, 'to': 100}, 'reference', 'summary'], # [{'from': 0, 'to': 100}, 'reference', 'title'], # [{'from': 0, 'to': 100}, 'reference', 'titleMaturity'], # [{'from': 0, 'to': 100}, 'reference', 'userRating'], # [{'from': 0, 'to': 100}, 'reference', 'userRatingRequestId'], # [{'from': 0, 'to': 100}, 'reference', 'boxarts', '_342x192', 'jpg'], # [{'from': 0, 'to': 100}, 'reference', 'promoVideo'] # ] callargs = { 'callpaths': path, 'params': params, # 'path_suffixs': path_suffixs } try: response = common.make_http_call('callpath_request', callargs) common.debug('refreshListByContext response: {}', response) # The call response return the new context id of the previous invalidated lolomo context_id # and if path_suffixs is added return also the new video list data except Exception: # pylint: disable=broad-except if not common.is_debug_verbose(): return ui.show_notification( title=common.get_local_string(30105), msg='An error prevented the update the lolomo context on netflix', time=10000)
def update_my_list(videoid, operation, params): """Call API to update my list with either add or remove action""" common.debug('My List: {} {}', operation, videoid) common.make_call( 'post', {'component': 'update_my_list', 'data': { 'operation': operation, 'videoId': videoid.value}}) ui.show_notification(common.get_local_string(30119)) _update_mylist_cache(videoid, operation, params)
def _prefetch_login(self): """Check if we have stored credentials. If so, do the login before the user requests it""" try: common.get_credentials() if not self._is_logged_in(): self._login() except MissingCredentialsError: common.info('Login prefetch: No stored credentials are available') except LoginFailedError: ui.show_notification(common.get_local_string(30009))
def login(): """Perform a login""" g.CACHE.invalidate() try: ui.ask_credentials() common.make_call('login') except (MissingCredentialsError, LoginFailedError) as exc: msg = 30009 if isinstance(exc, LoginFailedError) else 30112 ui.show_notification(common.get_local_string(msg)) return False return True
def logout(self): """Logout of the current account and reset the session""" common.debug('Logging out of current account') cookies.delete(self.account_hash) self._get('logout') common.purge_credentials() common.info('Logout successful') ui.show_notification(common.get_local_string(30113)) self._init_session() xbmc.executebuiltin('XBMC.Container.Update(path,replace)') # Clean path history xbmc.executebuiltin('XBMC.ActivateWindow(Home)')