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)
Example #2
0
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
Example #5
0
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)
Example #6
0
 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)
Example #9
0
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)
Example #10
0
 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)
Example #11
0
 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)
Example #16
0
 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
Example #17
0
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)
Example #18
0
    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
Example #20
0
 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
Example #21
0
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)
Example #22
0
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)
Example #26
0
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)
Example #27
0
 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)
Example #28
0
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))
Example #30
0
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)