def _check_watched_status_sync(): """Check if NF watched status sync setting is changed""" progress_manager_enabled = G.ADDON.getSettingBool('ProgressManager_enabled') progress_manager_enabled_old = G.LOCAL_DB.get_value('progress_manager_enabled', False, TABLE_SETTINGS_MONITOR) if progress_manager_enabled != progress_manager_enabled_old: G.LOCAL_DB.set_value('progress_manager_enabled', progress_manager_enabled, TABLE_SETTINGS_MONITOR) common.send_signal(signal=common.Signals.SWITCH_EVENTS_HANDLER, data=progress_manager_enabled)
def play(videoid): """Play an episode or movie as specified by the path""" common.debug('Playing {}'.format(videoid)) metadata = api.metadata(videoid) common.debug('Metadata is {}'.format(metadata)) if not _verify_pin(metadata[0].get('requiresPin', False)): ui.show_notification(common.get_local_string(30106)) xbmcplugin.endOfDirectory(g.PLUGIN_HANDLE, succeeded=False) return list_item = get_inputstream_listitem(videoid) infos, art = infolabels.add_info_for_playback(videoid, list_item) 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': get_upnext_info(videoid, (infos, art), metadata) }) xbmcplugin.setResolvedUrl(handle=g.PLUGIN_HANDLE, succeeded=True, listitem=list_item)
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 _on_playback_stopped(self): self.tracking = False self.active_player_id = None # Immediately send the request to release the license common.send_signal(signal=common.Signals.RELEASE_LICENSE, non_blocking=True) self._notify_all(PlaybackActionManager.on_playback_stopped) self.action_managers = None
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 _on_playback_stopped(self): self.tracking_tick = False self.active_player_id = None # Immediately send the request to release the license common.send_signal(signal=common.Signals.RELEASE_LICENSE, non_blocking=True) self._notify_all(ActionManager.call_on_playback_stopped, self._last_player_state) self.action_managers = None self.init_count -= 1
def request_kodi_library_update(**kwargs): """Request to scan and/or clean the Kodi library database""" # Particular way to start Kodi library scan/clean (details on request_kodi_library_update in library_updater.py) if not kwargs: raise Exception( 'request_kodi_library_update: you must specify kwargs "scan=True" and/or "clean=True"' ) common.send_signal(common.Signals.REQUEST_KODI_LIBRARY_UPDATE, data=kwargs, non_blocking=True)
def _send_event(self, event_type, event_data, player_state): if not player_state: LOG.warn('AMVideoEvents: the event [{}] cannot be sent, missing player_state data', event_type) return event_data['allow_request_update_loco'] = self.allow_request_update_loco common.send_signal(common.Signals.QUEUE_VIDEO_EVENT, { 'event_type': event_type, 'event_data': event_data, 'player_state': player_state }, non_blocking=True)
def _send_event(event_type, event_data, player_state): if not player_state: common.warn( 'ProgressManager: the event [{}] cannot be sent, missing player_state data', event_type) return common.send_signal(common.Signals.QUEUE_VIDEO_EVENT, { 'event_type': event_type, 'event_data': event_data, 'player_state': player_state }, non_blocking=True)
def kodi_library_update_wrapper(videoid, task_handler, *args, **kwargs): """Either trigger an update of the Kodi library or remove the items associated with videoid, depending on the invoked task_handler""" is_remove = task_handler == [remove_item] if is_remove: _remove_from_kodi_library(videoid) library_operation(videoid, task_handler, *args, **kwargs) if not is_remove: # Update Kodi library through service # This prevents a second call to cancel the update common.debug('Notify service to update the library') common.send_signal(common.Signals.LIBRARY_UPDATE_REQUESTED)
def website_extract_session_data(self, content, **kwargs): """Extract session data and handle errors""" try: return website.extract_session_data(content, **kwargs) except (WebsiteParsingError, InvalidMembershipStatusAnonymous, LoginValidateErrorIncorrectPassword) as exc: common.warn('Session data not valid, login can be expired or the password has been changed ({})', type(exc).__name__) if isinstance(exc, (InvalidMembershipStatusAnonymous, LoginValidateErrorIncorrectPassword)): common.purge_credentials() self.session.cookies.clear() common.send_signal(signal=common.Signals.CLEAR_USER_ID_TOKENS) raise NotLoggedInError raise
def website_extract_session_data(self, content, **kwargs): """Extract session data and handle errors""" try: return website.extract_session_data(content, **kwargs) except WebsiteParsingError as exc: LOG.error('An error occurs in extract session data: {}', exc) raise except (LoginValidateError, MbrStatusAnonymousError) as exc: LOG.warn('The session data is not more valid ({})', type(exc).__name__) common.purge_credentials() self.session.cookies.clear() common.send_signal(signal=common.Signals.CLEAR_USER_ID_TOKENS) raise_from(NotLoggedInError, exc)
def on_service_tick(self): """Check if update is due and trigger it""" if not self.enabled or self.next_schedule is None: return if self.next_schedule <= datetime.now() and self.is_idle(): # Check if the schedule value is changed # (when a manual update/full sync is done, we avoid to perform again the update) self.next_schedule = _compute_next_schedule() if self.next_schedule >= datetime.now(): return LOG.debug('Triggering auto update library') # Send signal to nfsession to run the library auto update common.send_signal('library_auto_update') # Compute the next schedule self.next_schedule = _compute_next_schedule(datetime.now())
def logout(self, url): """Logout of the current account and reset the session""" common.debug('Logging out of current account') # Perform the website logout self._get('logout') g.settings_monitor_suspend(True) # Disable and reset auto-update / auto-sync features g.ADDON.setSettingInt('lib_auto_upd_mode', 1) g.ADDON.setSettingBool('lib_sync_mylist', False) g.SHARED_DB.delete_key('sync_mylist_profile_guid') # Disable and reset the auto-select profile g.LOCAL_DB.set_value('autoselect_profile_guid', '') g.ADDON.setSetting('autoselect_profile_name', '') g.ADDON.setSettingBool('autoselect_profile_enabled', False) # Reset of selected profile guid for library playback g.LOCAL_DB.set_value('library_playback_profile_guid', '') g.ADDON.setSetting('library_playback_profile', '') g.settings_monitor_suspend(False) # Delete cookie and credentials self.session.cookies.clear() cookies.delete(self.account_hash) common.purge_credentials() # Reset the ESN obtained from website/generated g.LOCAL_DB.set_value('esn', '', TABLE_SESSION) # Reinitialize the MSL handler (delete msl data file, then reset everything) common.send_signal(signal=common.Signals.REINITIALIZE_MSL_HANDLER, data=True) g.CACHE.clear(clear_database=True) common.info('Logout successful') ui.show_notification(common.get_local_string(30113)) self._init_session() xbmc.executebuiltin('Container.Update(path,replace)' ) # Go to a fake page to clear screen # Open root page xbmc.executebuiltin('Container.Update({},replace)'.format( url)) # replace=reset history
def _check_esn(): """Check if the custom esn is changed""" custom_esn = G.ADDON.getSetting('esn') custom_esn_old = G.LOCAL_DB.get_value('custom_esn', '', TABLE_SETTINGS_MONITOR) if custom_esn != custom_esn_old: G.LOCAL_DB.set_value('custom_esn', custom_esn, TABLE_SETTINGS_MONITOR) common.send_signal(signal=common.Signals.ESN_CHANGED) if not custom_esn: # Check if "Force identification as L3 Widevine device" is changed (ANDROID ONLY) is_l3_forced = bool(G.ADDON.getSettingBool('force_widevine_l3')) is_l3_forced_old = G.LOCAL_DB.get_value('force_widevine_l3', False, TABLE_SETTINGS_MONITOR) if is_l3_forced != is_l3_forced_old: G.LOCAL_DB.set_value('force_widevine_l3', is_l3_forced, TABLE_SETTINGS_MONITOR) # If user has changed setting is needed clear previous ESN and perform a new handshake with the new one G.LOCAL_DB.set_value('esn', generate_android_esn() or '', TABLE_SESSION) common.send_signal(signal=common.Signals.ESN_CHANGED)
def try_refresh_session_data(self, raise_exception=False): """Refresh session_data from the Netflix website""" # pylint: disable=broad-except try: self.auth_url = website.extract_session_data( self._get('browse'))['auth_url'] self.update_session_data() common.debug('Successfully refreshed session data') return True except InvalidMembershipStatusError: raise except (WebsiteParsingError, InvalidMembershipStatusAnonymous, LoginValidateErrorIncorrectPassword) as exc: # Possible known causes: # -Cookies may not work anymore most likely due to updates in the website # -Login password has been changed # -Expired cookie profiles? might cause InvalidMembershipStatusAnonymous (i am not really sure) import traceback common.warn( 'Failed to refresh session data, login can be expired or the password has been changed ({})', type(exc).__name__) common.debug(g.py2_decode(traceback.format_exc(), 'latin-1')) self.session.cookies.clear() if isinstance(exc, (InvalidMembershipStatusAnonymous, LoginValidateErrorIncorrectPassword)): # This prevent the MSL error: No entity association record found for the user common.send_signal(signal=common.Signals.CLEAR_USER_ID_TOKENS) return self._login() except requests.exceptions.RequestException: import traceback common.warn( 'Failed to refresh session data, request error (RequestException)' ) common.warn(g.py2_decode(traceback.format_exc(), 'latin-1')) if raise_exception: raise except Exception: import traceback common.warn( 'Failed to refresh session data, login expired (Exception)') common.debug(g.py2_decode(traceback.format_exc(), 'latin-1')) self.session.cookies.clear() if raise_exception: raise return False
def play(videoid): """Play an episode or movie as specified by the path""" common.debug('Playing {}'.format(videoid)) metadata = api.metadata(videoid) common.debug('Metadata is {}'.format(metadata)) if not _verify_pin(metadata[0].get('requiresPin', False)): ui.show_notification(common.get_local_string(30106)) xbmcplugin.endOfDirectory(g.PLUGIN_HANDLE, succeeded=False) return list_item = get_inputstream_listitem(videoid) infos, art = infolabels.add_info_for_playback(videoid, list_item) # Workaround for resuming strm files from library resume_position = infos.get('resume', {}).get('position') \ if xbmc.getInfoLabel('Container.PluginName') != g.ADDON.getAddonInfo('id') \ 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 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': get_upnext_info(videoid, (infos, art), metadata), 'resume_position': resume_position }) xbmcplugin.setResolvedUrl(handle=g.PLUGIN_HANDLE, succeeded=True, listitem=list_item)
def logout(self): """Logout of the current account and reset the session""" LOG.debug('Logging out of current account') # Perform the website logout self.get('logout') G.settings_monitor_suspend(True) # Disable and reset auto-update / auto-sync features G.ADDON.setSettingInt('lib_auto_upd_mode', 1) G.ADDON.setSettingBool('lib_sync_mylist', False) G.SHARED_DB.delete_key('sync_mylist_profile_guid') # Disable and reset the profile guid of profile auto-selection G.LOCAL_DB.set_value('autoselect_profile_guid', '') # Disable and reset the selected profile guid for library playback G.LOCAL_DB.set_value('library_playback_profile_guid', '') G.settings_monitor_suspend(False) # Delete cookie and credentials self.session.cookies.clear() cookies.delete() common.purge_credentials() # Reset the ESN obtained from website/generated G.LOCAL_DB.set_value('esn', '', TABLE_SESSION) # Reinitialize the MSL handler (delete msl data file, then reset everything) common.send_signal(signal=common.Signals.REINITIALIZE_MSL_HANDLER, data=True) G.CACHE.clear(clear_database=True) LOG.info('Logout successful') ui.show_notification(common.get_local_string(30113)) self._init_session() common.container_update('path', True) # Go to a fake page to clear screen # Open root page common.container_update(G.BASE_URL, True)
def try_refresh_session_data(self, raise_exception=False): """Refresh session data from the Netflix website""" from requests import exceptions try: self.auth_url = website.extract_session_data( self.get('browse'))['auth_url'] cookies.save(self.session.cookies) LOG.debug('Successfully refreshed session data') return True except MbrStatusError: raise except (WebsiteParsingError, MbrStatusAnonymousError) as exc: import traceback LOG.warn( 'Failed to refresh session data, login can be expired or the password has been changed ({})', type(exc).__name__) LOG.debug(G.py2_decode(traceback.format_exc(), 'latin-1')) self.session.cookies.clear() if isinstance(exc, MbrStatusAnonymousError): # This prevent the MSL error: No entity association record found for the user common.send_signal(signal=common.Signals.CLEAR_USER_ID_TOKENS) # Needed to do a new login common.purge_credentials() ui.show_notification(common.get_local_string(30008)) raise_from(NotLoggedInError, exc) except exceptions.RequestException: import traceback LOG.warn( 'Failed to refresh session data, request error (RequestException)' ) LOG.warn(G.py2_decode(traceback.format_exc(), 'latin-1')) if raise_exception: raise except Exception: # pylint: disable=broad-except import traceback LOG.warn( 'Failed to refresh session data, login expired (Exception)') LOG.debug(G.py2_decode(traceback.format_exc(), 'latin-1')) self.session.cookies.clear() if raise_exception: raise return False
def try_refresh_session_data(self, raise_exception=False): """Refresh session_data from the Netflix website""" from requests import exceptions try: self.auth_url = website.extract_session_data( self._get('browse'))['auth_url'] cookies.save(self.account_hash, self.session.cookies) common.debug('Successfully refreshed session data') return True except InvalidMembershipStatusError: raise except (WebsiteParsingError, InvalidMembershipStatusAnonymous, LoginValidateErrorIncorrectPassword) as exc: import traceback common.warn( 'Failed to refresh session data, login can be expired or the password has been changed ({})', type(exc).__name__) common.debug(g.py2_decode(traceback.format_exc(), 'latin-1')) self.session.cookies.clear() if isinstance(exc, (InvalidMembershipStatusAnonymous, LoginValidateErrorIncorrectPassword)): # This prevent the MSL error: No entity association record found for the user common.send_signal(signal=common.Signals.CLEAR_USER_ID_TOKENS) return self._login() except exceptions.RequestException: import traceback common.warn( 'Failed to refresh session data, request error (RequestException)' ) common.warn(g.py2_decode(traceback.format_exc(), 'latin-1')) if raise_exception: raise except Exception: # pylint: disable=broad-except import traceback common.warn( 'Failed to refresh session data, login expired (Exception)') common.debug(g.py2_decode(traceback.format_exc(), 'latin-1')) self.session.cookies.clear() if raise_exception: raise return False
def _play(videoid, is_played_from_strm=False): """Play an episode or movie as specified by the path""" is_upnext_enabled = G.ADDON.getSettingBool('UpNextNotifier_enabled') LOG.info('Playing {}{}{}', videoid, ' [STRM file]' if is_played_from_strm else '', ' [external call]' if G.IS_ADDON_EXTERNAL_CALL else '') # Profile switch when playing from a STRM file (library) if is_played_from_strm and not _profile_switch(): xbmcplugin.endOfDirectory(G.PLUGIN_HANDLE, succeeded=False) return # Generate the xbmcgui.ListItem to be played list_item = get_inputstream_listitem(videoid) # STRM file resume workaround (Kodi library) resume_position = _strm_resume_workaroud(is_played_from_strm, videoid) if resume_position == '': xbmcplugin.setResolvedUrl(handle=G.PLUGIN_HANDLE, succeeded=False, listitem=list_item) return # When a video is played from Kodi library or Up Next add-on is needed set infoLabels and art info to list_item if is_played_from_strm or is_upnext_enabled or G.IS_ADDON_EXTERNAL_CALL: info, arts = common.make_call('get_videoid_info', videoid) list_item.setInfo('video', info) list_item.setArt(arts) # Start and initialize the action controller (see action_controller.py) LOG.debug('Sending initialization signal') # Do not use send_signal as threaded slow devices are not powerful to send in faster way and arrive late to service common.send_signal( common.Signals.PLAYBACK_INITIATED, { 'videoid': videoid, 'is_played_from_strm': is_played_from_strm, 'resume_position': resume_position }) xbmcplugin.setResolvedUrl(handle=G.PLUGIN_HANDLE, succeeded=True, listitem=list_item)
def export_all_new_episodes(): """ Update the local Kodi library with new episodes of every exported shows """ if not _export_all_new_episodes_running(): common.log('Starting to export new episodes for all tv shows') g.SHARED_DB.set_value('library_export_new_episodes_running', True) g.SHARED_DB.set_value('library_export_new_episode_start_time', datetime.now()) for videoid_value in g.SHARED_DB.get_tvshows_id_list( VidLibProp.exclude_update, False): videoid = common.VideoId.from_path( [common.VideoId.SHOW, videoid_value]) export_new_episodes(videoid, True) # add some randomness between show analysis to limit servers load and ban risks xbmc.sleep(random.randint(1000, 5001)) g.SHARED_DB.set_value('library_export_new_episodes_running', False) common.debug('Notify service to update the library') common.send_signal(common.Signals.LIBRARY_UPDATE_REQUESTED)
def reset_esn(self, pathitems=None): # pylint: disable=unused-argument """Reset the ESN stored (retrieved from website and manual)""" if not ui.ask_for_confirmation(common.get_local_string(30217), common.get_local_string(30218)): return # Generate a new ESN generated_esn = self._get_new_esn() # Reset the ESN obtained from website/generated G.LOCAL_DB.set_value('esn', '', TABLE_SESSION) # Reset the custom ESN (manual ESN from settings) G.settings_monitor_suspend(at_first_change=True) G.ADDON.setSetting('esn', '') # Reset the custom ESN (backup of manual ESN from settings, used in settings_monitor.py) G.LOCAL_DB.set_value('custom_esn', '', TABLE_SETTINGS_MONITOR) # Save the new ESN G.LOCAL_DB.set_value('esn', generated_esn, TABLE_SESSION) # Reinitialize the MSL handler (delete msl data file, then reset everything) common.send_signal(signal=common.Signals.REINITIALIZE_MSL_HANDLER, data=True) # Show login notification ui.show_notification(common.get_local_string(30109)) # Open root page common.container_update(G.BASE_URL, True)
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() else: # A hack way to full load requests module without blocking the service startup common.send_signal(signal='startup_requests_module', non_blocking=True) self.is_prefetch_login = True except requests.exceptions.RequestException as exc: # It was not possible to connect to the web service, no connection, network problem, etc import traceback common.error('Login prefetch: request exception {}', exc) common.debug(traceback.format_exc()) except MissingCredentialsError: common.info('Login prefetch: No stored credentials are available') except (LoginFailedError, LoginValidateError): ui.show_notification(common.get_local_string(30009)) except InvalidMembershipStatusError: ui.show_notification(common.get_local_string(30180), time=10000)
def on_playback_started(self, player_state): # pylint: disable=unused-argument LOG.debug('Sending initialization signal to Up Next Add-on') common.send_signal(common.Signals.UPNEXT_ADDON_INIT, self.upnext_info, non_blocking=True)
def _update_esn(old_esn): """Perform key handshake if the esn has changed on Session initialization""" current_esn = g.get_esn() if old_esn != current_esn: common.send_signal(signal=common.Signals.ESN_CHANGED, data=current_esn)
def _on_playback_started(self, player_state): # pylint: disable=unused-argument common.debug('Sending initialization signal to Up Next') common.send_signal('upnext_data', self.upnext_info)
def _play(videoid, is_played_from_strm=False): """Play an episode or movie as specified by the path""" is_upnext_enabled = G.ADDON.getSettingBool('UpNextNotifier_enabled') LOG.info('Playing {}{}{}', videoid, ' [STRM file]' if is_played_from_strm else '', ' [external call]' if G.IS_ADDON_EXTERNAL_CALL else '') # Profile switch when playing from a STRM file (library) if is_played_from_strm: if not _profile_switch(): xbmcplugin.endOfDirectory(G.PLUGIN_HANDLE, succeeded=False) return # Get metadata of videoid try: metadata = api.get_metadata(videoid) LOG.debug('Metadata is {}', json.dumps(metadata)) except MetadataNotAvailable: LOG.warn('Metadata not available for {}', videoid) metadata = [{}, {}] # Check 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=8000) xbmcplugin.endOfDirectory(G.PLUGIN_HANDLE, succeeded=False) return # Generate the xbmcgui.ListItem to be played list_item = get_inputstream_listitem(videoid) # STRM file resume workaround (Kodi library) resume_position = _strm_resume_workaroud(is_played_from_strm, videoid) if resume_position == '': xbmcplugin.setResolvedUrl(handle=G.PLUGIN_HANDLE, succeeded=False, listitem=list_item) return info_data = None event_data = {} videoid_next_episode = None # Get Infolabels and Arts for the videoid to be played, and for the next video if it is an episode (for UpNext) if is_played_from_strm or is_upnext_enabled or G.IS_ADDON_EXTERNAL_CALL: if is_upnext_enabled and videoid.mediatype == common.VideoId.EPISODE: # When UpNext is enabled, get the next episode to play videoid_next_episode = _upnext_get_next_episode_videoid( videoid, metadata) info_data = infolabels.get_info_from_netflix( [videoid, videoid_next_episode] if videoid_next_episode else [videoid]) info, arts = info_data[videoid.value] # When a item is played from Kodi library or Up Next add-on is needed set info and art to list_item list_item.setInfo('video', info) list_item.setArt(arts) # Get event data for videoid to be played (needed for sync of watched status with Netflix) if (G.ADDON.getSettingBool('ProgressManager_enabled') and videoid.mediatype in [common.VideoId.MOVIE, common.VideoId.EPISODE]): if not is_played_from_strm or is_played_from_strm and G.ADDON.getSettingBool( 'sync_watched_status_library'): event_data = _get_event_data(videoid) event_data['videoid'] = videoid.to_dict() event_data['is_played_by_library'] = is_played_from_strm # Start and initialize the action controller (see action_controller.py) LOG.debug('Sending initialization signal') common.send_signal(common.Signals.PLAYBACK_INITIATED, { 'videoid': videoid.to_dict(), 'videoid_next_episode': videoid_next_episode.to_dict() if videoid_next_episode else None, 'metadata': metadata, 'info_data': info_data, 'is_played_from_strm': is_played_from_strm, 'resume_position': resume_position, 'event_data': event_data }, non_blocking=True) xbmcplugin.setResolvedUrl(handle=G.PLUGIN_HANDLE, succeeded=True, listitem=list_item)
def _on_change(self): common.reset_log_level_global_var() common.debug( 'SettingsMonitor: settings have been changed, started checks') reboot_addon = False clean_cache = False use_mysql = g.ADDON.getSettingBool('use_mysql') use_mysql_old = g.LOCAL_DB.get_value('use_mysql', False, TABLE_SETTINGS_MONITOR) use_mysql_turned_on = use_mysql and not use_mysql_old common.debug( 'SettingsMonitor: Reinitialization of service global settings') g.init_globals(sys.argv, use_mysql != use_mysql_old) # Check the MySQL connection status after reinitialization of service global settings use_mysql_after = g.ADDON.getSettingBool('use_mysql') if use_mysql_turned_on and use_mysql_after: g.LOCAL_DB.set_value('use_mysql', True, TABLE_SETTINGS_MONITOR) ui.show_notification(g.ADDON.getLocalizedString(30202)) if not use_mysql_after and use_mysql_old: g.LOCAL_DB.set_value('use_mysql', False, TABLE_SETTINGS_MONITOR) _esn_checks() # Check menu settings changes for menu_id, menu_data in iteritems(g.MAIN_MENU_ITEMS): # Check settings changes in show/hide menu if menu_data.get('has_show_setting', True): show_menu_new_setting = bool( g.ADDON.getSettingBool('_'.join(('show_menu', menu_id)))) show_menu_old_setting = g.LOCAL_DB.get_value( 'menu_{}_show'.format(menu_id), True, TABLE_SETTINGS_MONITOR) if show_menu_new_setting != show_menu_old_setting: g.LOCAL_DB.set_value('menu_{}_show'.format(menu_id), show_menu_new_setting, TABLE_SETTINGS_MONITOR) reboot_addon = True # Check settings changes in sort order of menu if menu_data.get('has_sort_setting'): menu_sortorder_new_setting = int( g.ADDON.getSettingInt('menu_sortorder_' + menu_data['path'][1])) menu_sortorder_old_setting = g.LOCAL_DB.get_value( 'menu_{}_sortorder'.format(menu_id), 0, TABLE_SETTINGS_MONITOR) if menu_sortorder_new_setting != menu_sortorder_old_setting: g.LOCAL_DB.set_value('menu_{}_sortorder'.format(menu_id), menu_sortorder_new_setting, TABLE_SETTINGS_MONITOR) # We remove the cache to allow get the new results in the chosen order g.CACHE.clear([CACHE_COMMON, CACHE_MYLIST, CACHE_SEARCH]) # Check changes on content profiles # This is necessary because it is possible that some manifests # could be cached using the previous settings (see msl_handler - load_manifest) menu_keys = [ 'enable_dolby_sound', 'enable_vp9_profiles', 'enable_hevc_profiles', 'enable_hdr_profiles', 'enable_dolbyvision_profiles', 'enable_force_hdcp', 'disable_webvtt_subtitle' ] collect_int = '' for menu_key in menu_keys: collect_int += unicode(int(g.ADDON.getSettingBool(menu_key))) collect_int_old = g.LOCAL_DB.get_value('content_profiles_int', '', TABLE_SETTINGS_MONITOR) if collect_int != collect_int_old: g.LOCAL_DB.set_value('content_profiles_int', collect_int, TABLE_SETTINGS_MONITOR) g.CACHE.clear([CACHE_MANIFESTS]) # Check if Progress Manager settings is changed progress_manager_enabled = g.ADDON.getSettingBool( 'ProgressManager_enabled') progress_manager_enabled_old = g.LOCAL_DB.get_value( 'progress_manager_enabled', False, TABLE_SETTINGS_MONITOR) if progress_manager_enabled != progress_manager_enabled_old: g.LOCAL_DB.set_value('progress_manager_enabled', progress_manager_enabled, TABLE_SETTINGS_MONITOR) common.send_signal(signal=common.Signals.SWITCH_EVENTS_HANDLER, data=progress_manager_enabled) # Avoid perform these operations when the add-on is installed from scratch and there are no credentials if (clean_cache or reboot_addon) and not common.check_credentials(): reboot_addon = False if reboot_addon: common.debug('SettingsMonitor: addon will be rebooted') # Open root page common.container_update( common.build_url(['root'], mode=g.MODE_DIRECTORY))
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, skip_set_progress_status=True) resume_position = {} event_data = {} if g.IS_SKIN_CALL: # Workaround for resuming strm files from library resume_position = infos.get('resume', {}).get('position') \ if 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 elif (g.ADDON.getSettingBool('ProgressManager_enabled') and videoid.mediatype in [common.VideoId.MOVIE, common.VideoId.EPISODE]): # To now we have this limits: # - enabled only with items played inside the addon then not Kodi library, need impl. JSON-RPC lib update code event_data = _get_event_data(videoid) event_data['videoid'] = videoid.to_dict() event_data['is_played_by_library'] = g.IS_SKIN_CALL # Todo: UpNext addon is incompatible with netflix watched status sync feature # Problems: # - Need to modify the cache (to update the watched status) on every played item # - Modifying the cache after the stop, is wrong due to below problems # - The new fast play, 'play_url' method, cause problems with the Player callbacks, after press play next is missing the Stop event in the controller! # - To verify possibility problems of data mixing of controller._get_player_state() # - The call of next video from UpNext is recognized as Skin call, because it's an external addon call, so it causes several operating problems is_up_next_enabled = False if 'raspberrypi' in common.get_system_platform( ) and '18' in common.GetKodiVersion().version: # OMX Player is not compatible with netflix video streams # Only Kodi 18 has this property, on Kodi 19 Omx Player has been removed value = common.json_rpc('Settings.GetSettingValue', {'setting': 'videoplayer.useomxplayer'}) if value.get('value'): common.json_rpc('Settings.SetSettingValue', { 'setting': 'videoplayer.useomxplayer', 'value': False }) 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 g.LOCAL_DB.set_value('last_videoid_played', videoid.to_dict(), table=TABLE_SESSION) 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, 'event_data': event_data }, non_blocking=True)