def logout(self):
        """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()
        common.container_update('path',
                                True)  # Go to a fake page to clear screen
        # Open root page
        common.container_update(G.BASE_URL, True)
def change_watched_status_locally(videoid):
    """Change the watched status locally"""
    # Todo: how get resumetime/playcount of selected item for calculate current watched status?
    profile_guid = G.LOCAL_DB.get_active_profile_guid()
    current_value = G.SHARED_DB.get_watched_status(profile_guid, videoid.value,
                                                   None, bool)
    if current_value:
        txt_index = 1
        G.SHARED_DB.set_watched_status(profile_guid, videoid.value, False)
    elif current_value is not None and not current_value:
        txt_index = 2
        G.SHARED_DB.delete_watched_status(profile_guid, videoid.value)
    else:
        txt_index = 0
        G.SHARED_DB.set_watched_status(profile_guid, videoid.value, True)
    ui.show_notification(common.get_local_string(30237).split('|')[txt_index])
    common.container_refresh()
Beispiel #3
0
def _create_dictitem_from_row(row):
    row_id = str(row['ID'])
    search_desc = common.get_local_string(
        30401) + ': ' + SEARCH_TYPES_DESC.get(row['Type'], 'Unknown')
    return {
        'url':
        common.build_url(['search', 'search', row_id], mode=g.MODE_DIRECTORY),
        'label':
        row['Value'],
        'info': {
            'plot': search_desc
        },  # The description
        'menu_items':
        generate_context_menu_searchitem(row_id, row['Type']),
        'is_folder':
        True
    }
def _display_search_results(pathitems, perpetual_range_start,
                            dir_update_listing):
    menu_data = g.MAIN_MENU_ITEMS['search']
    call_args = {
        'menu_data': menu_data,
        'search_term': pathitems[2],
        'pathitems': pathitems,
        'perpetual_range_start': perpetual_range_start
    }
    list_data, extra_data = common.make_call('get_video_list_search',
                                             call_args)
    if list_data:
        _search_results_directory(pathitems, menu_data, list_data, extra_data,
                                  dir_update_listing)
    else:
        ui.show_notification(common.get_local_string(30013))
        xbmcplugin.endOfDirectory(g.PLUGIN_HANDLE, succeeded=False)
Beispiel #5
0
 def update_videoid_bookmark(self, video_id):
     """Update the videoid bookmark position"""
     # You can check if this function works through the official android app
     # by checking if the red status bar of watched time position appears and will be updated, or also
     # if continueWatching list will be updated (e.g. try to play a new tvshow not contained in the "my list")
     call_paths = [['refreshVideoCurrentPositions']]
     params = [f'[{video_id}]', '[]']
     try:
         response = self.callpath_request(call_paths, params)
         LOG.debug('refreshVideoCurrentPositions response: {}', response)
     except Exception as exc:  # pylint: disable=broad-except
         LOG.warn('refreshVideoCurrentPositions failed: {}', exc)
         ui.show_notification(
             title=common.get_local_string(30105),
             msg=
             'An error prevented the update the status watched on Netflix',
             time=10000)
Beispiel #6
0
def play(videoid):
    """Play an episode or movie as specified by the path"""
    common.info('Playing {}', videoid)
    metadata = api.metadata(videoid)
    common.debug('Metadata is {}', metadata)

    # 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)

    # 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 update_videoid_bookmark(video_id):
    """Update the videoid bookmark position"""
    # You can check if this function works through the official android app
    # by checking if the status bar watched of the video will be updated
    callargs = {
        'callpaths': [['refreshVideoCurrentPositions']],
        'params': ['[' + video_id + ']', '[]'],
    }
    try:
        response = common.make_http_call('callpath_request', callargs)
        common.debug('refreshVideoCurrentPositions response: {}', response)
    except Exception:  # pylint: disable=broad-except
        # I do not know the reason yet, but sometimes continues to return error 401,
        # making it impossible to update the bookmark position
        ui.show_notification(title=common.get_local_string(30105),
                             msg='An error prevented the update the status watched on netflix',
                             time=10000)
    def update_loco_context(self, context_name):
        """Update a loco list by context"""
        # Call this api seem no more needed to update the continueWatching loco list
        # Get current loco root data
        loco_data = self.path_request(
            [['loco', [context_name], ['context', 'id', 'index']]])
        loco_root = loco_data['loco'][1]
        if 'continueWatching' in loco_data['locos'][loco_root]:
            context_index = loco_data['locos'][loco_root]['continueWatching'][
                2]
            context_id = loco_data['locos'][loco_root][context_index][1]
        else:
            # In the new profiles, there is no 'continueWatching' list and no list is returned
            LOG.warn(
                'update_loco_context: Update skipped due to missing context {}',
                context_name)
            return

        path = [['locos', loco_root, 'refreshListByContext']]
        # After the introduction of LoCo, the following notes are to be reviewed (refers to old LoLoMo):
        #   The fourth parameter is like a request-id, but it does not seem to match to
        #   serverDefs/date/requestId of reactContext nor to request_id of the video event request,
        #   seem to have some kind of relationship with renoMessageId suspect with the logblob but i am not sure.
        #   I noticed also that this request can also be made with the fourth parameter empty.
        params = [
            common.enclose_quotes(context_id), context_index,
            common.enclose_quotes(context_name), ''
        ]
        # path_suffixs = [
        #    [{'from': 0, 'to': 100}, 'itemSummary'],
        #    [['componentSummary']]
        # ]
        try:
            response = self.callpath_request(path, params)
            LOG.debug('refreshListByContext response: {}', response)
            # The call response return the new context id of the previous invalidated loco context_id
            # and if path_suffixs is added return also the new video list data
        except Exception as exc:  # pylint: disable=broad-except
            LOG.warn('refreshListByContext failed: {}', exc)
            if not LOG.level == LOG.LEVEL_VERBOSE:
                return
            ui.show_notification(
                title=common.get_local_string(30105),
                msg='An error prevented the update the loco context on Netflix',
                time=10000)
def get_inputstream_listitem(videoid):
    """Return a listitem that has all inputstream relevant properties set for playback of the given video_id"""
    service_url = SERVICE_URL_FORMAT.format(
        port=G.LOCAL_DB.get_value('msl_service_port', 8000))
    manifest_path = MANIFEST_PATH_FORMAT.format(videoid=videoid.value)
    list_item = xbmcgui.ListItem(path=service_url + manifest_path,
                                 offscreen=True)
    list_item.setContentLookup(False)
    list_item.setMimeType('application/xml+dash')
    list_item.setProperty('isFolder', 'false')
    list_item.setProperty('IsPlayable', 'true')
    try:
        import inputstreamhelper
        is_helper = inputstreamhelper.Helper('mpd', drm='widevine')
        inputstream_ready = is_helper.check_inputstream()
        if not inputstream_ready:
            raise Exception(common.get_local_string(30046))

        list_item.setProperty(key=is_helper.inputstream_addon +
                              '.stream_headers',
                              value='user-agent=' + common.get_user_agent())
        list_item.setProperty(key=is_helper.inputstream_addon +
                              '.license_type',
                              value='com.widevine.alpha')
        list_item.setProperty(key=is_helper.inputstream_addon +
                              '.manifest_type',
                              value='mpd')
        list_item.setProperty(
            key=is_helper.inputstream_addon + '.license_key',
            value=service_url +
            LICENSE_PATH_FORMAT.format(videoid=videoid.value) +
            '||b{SSM}!b{SID}|')
        list_item.setProperty(key=is_helper.inputstream_addon +
                              '.server_certificate',
                              value=INPUTSTREAM_SERVER_CERTIFICATE)
        list_item.setProperty(
            key='inputstreamaddon'
            if G.KODI_VERSION.is_major_ver('18') else 'inputstream',
            value=is_helper.inputstream_addon)
        return list_item
    except Exception as exc:  # pylint: disable=broad-except
        # Captures all types of ISH internal errors
        import traceback
        LOG.error(G.py2_decode(traceback.format_exc(), 'latin-1'))
        raise_from(InputStreamHelperError(str(exc)), exc)
    def _login(self):
        """Perform account login"""
        try:
            auth_url = website.extract_userdata(
                self._get('profiles'))['authURL']
            common.debug('Logging in...')
            login_response = self._post(
                'login',
                data=_login_payload(common.get_credentials(), auth_url))
            session_data = website.extract_session_data(login_response)
        except Exception:
            common.debug(traceback.format_exc())
            self.session.cookies.clear()
            raise LoginFailedError

        common.info('Login successful')
        ui.show_notification(common.get_local_string(30109))
        self.session_data = session_data
 def home(self, pathitems=None):  # pylint: disable=unused-argument
     """Show home listing"""
     if 'switch_profile_guid' in self.params:
         if G.IS_ADDON_EXTERNAL_CALL:
             # Profile switch/ask PIN only once
             ret = not self.params['switch_profile_guid'] == G.LOCAL_DB.get_active_profile_guid()
         else:
             # Profile switch/ask PIN every time you come from ...
             ret = common.WndHomeProps[common.WndHomeProps.CURRENT_DIRECTORY] in ['', 'root', 'profiles']
         if ret and not activate_profile(self.params['switch_profile_guid']):
             xbmcplugin.endOfDirectory(G.PLUGIN_HANDLE, succeeded=False)
             return
     LOG.debug('Showing home listing')
     dir_items, extra_data = common.make_call('get_mainmenu')  # pylint: disable=unused-variable
     finalize_directory(dir_items, G.CONTENT_FOLDER,
                        title=(G.LOCAL_DB.get_profile_config('profileName', '???') +
                               ' - ' + common.get_local_string(30097)))
     end_of_directory(True)
Beispiel #12
0
 def export_to_library_new_episodes(self, videoid, show_prg_dialog=True):
     """
     Export new episodes for a tv show by it's videoid
     :param videoid: The videoid of the tv show to process
     :param show_prg_dialog: if True show progress dialog, otherwise, a background progress bar
     """
     LOG.info('Start exporting new episodes for {}', videoid)
     if videoid.mediatype != common.VideoId.SHOW:
         LOG.warn('{} is not a tv show, the operation is cancelled',
                  videoid)
         return
     nfo_settings = nfo.NFOSettings()
     nfo_settings.show_export_dialog(videoid.mediatype)
     self.execute_library_task_gui(videoid,
                                   self.export_new_item,
                                   title=common.get_local_string(30198),
                                   nfo_settings=nfo_settings,
                                   show_prg_dialog=show_prg_dialog)
Beispiel #13
0
def _get_dictitem_clear():
    """The "clear" menu item"""
    return {
        'url':
        common.build_url(['search', 'search', 'clear'], mode=G.MODE_DIRECTORY),
        'label':
        common.get_local_string(30404),
        'art': {
            'icon': 'icons\\infodialogs\\uninstall.png'
        },
        'is_folder':
        False,  # Set folder to false to run as command so that clear is not in directory history
        'media_type':
        None,  # Set media type none to avoid setting isplayable flag for non-folder item
        'properties': {
            'specialsort': 'bottom'
        }  # Force an item to stay on bottom (not documented in Kodi)
    }
 def home(self,
          pathitems=None,
          cache_to_disc=True,
          is_autoselect_profile=False):  # pylint: disable=unused-argument
     """Show home listing"""
     if not is_autoselect_profile and 'switch_profile_guid' in self.params:
         # This is executed only when you have selected a profile from the profile list
         if not activate_profile(self.params['switch_profile_guid']):
             xbmcplugin.endOfDirectory(g.PLUGIN_HANDLE, succeeded=False)
             return
     common.debug('Showing home listing')
     list_data, extra_data = common.make_call('get_mainmenu')  # pylint: disable=unused-variable
     finalize_directory(
         convert_list_to_dir_items(list_data),
         g.CONTENT_FOLDER,
         title=(g.LOCAL_DB.get_profile_config('profileName', '???') +
                ' - ' + common.get_local_string(30097)))
     end_of_directory(False, cache_to_disc)
 def __init__(self, path_response):
     self.data = path_response
     have_data = 'search' in path_response
     if have_data:
         self.title = common.get_local_string(30100).format(
             self.data['search']['byTerm'].keys()[0][1:])
         self.videos = OrderedDict(
             resolve_refs(self.data['search']['byReference'].values()[0],
                          self.data))
         self.videoids = _get_videoids(self.videos)
         self.artitem = next(self.videos.itervalues(), None)
         self.contained_titles = _get_titles(self.videos)
     else:
         common.debug('SearchVideoList - No data in path_response')
         self.videos = {}
         self.videoids = None
         self.artitem = None
         self.contained_titles = None
Beispiel #16
0
 def on_playback_started(self, player_state):
     is_enabled = G.ADDON.getSettingBool('StreamContinuityManager_enabled')
     if is_enabled:
         # Get user saved preferences
         self.sc_settings = G.SHARED_DB.get_stream_continuity(G.LOCAL_DB.get_active_profile_guid(),
                                                              self.videoid_parent.value, {})
     else:
         # Disable on_tick activity to check changes of settings
         self.enabled = False
     if (player_state.get(STREAMS['subtitle']['current']) is None and
             player_state.get('currentvideostream') is None):
         # Kodi 19 BUG JSON RPC: "Player.GetProperties" is broken: https://github.com/xbmc/xbmc/issues/17915
         # The first call return wrong data the following calls return OSError, and then _notify_all will be blocked
         self.enabled = False
         LOG.error('Due of Kodi 19 bug has been disabled: '
                   'Ask to skip dialog, remember audio/subtitles preferences and other features')
         ui.show_notification(title=common.get_local_string(30105),
                              msg='Due to Kodi bug has been disabled all Netflix features')
         return
     xbmc.sleep(500)  # Wait for slower systems
     self.player_state = player_state
     # If the user has not changed the subtitle settings
     if self.sc_settings.get('subtitleenabled') is None:
         # Copy player state to restore it after, or the changes will affect the _restore_stream()
         _player_state_copy = copy.deepcopy(player_state)
         # Force selection of the audio/subtitles language with country code
         if G.ADDON.getSettingBool('prefer_alternative_lang'):
             self._select_lang_with_country_code()
         # Ensures the display of forced subtitles only with the audio language set
         if G.ADDON.getSettingBool('show_forced_subtitles_only'):
             self._ensure_forced_subtitle_only()
         # Ensure in any case to show the regular subtitles when the preferred audio language is not available
         if G.ADDON.getSettingBool('show_subtitles_miss_audio'):
             self._ensure_subtitles_no_audio_available()
         player_state = _player_state_copy
     for stype in sorted(STREAMS):
         # Save current stream setting from the Kodi player to the local dict
         self._set_current_stream(stype, player_state)
         # Apply the chosen stream setting to Kodi player and update the local dict
         self._restore_stream(stype)
     if is_enabled:
         # It is mandatory to wait at least 1 second to allow the Kodi system to update the values
         # changed by restore, otherwise when on_tick is executed it will save twice unnecessarily
         xbmc.sleep(1000)
    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', 0)
        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)

        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
Beispiel #18
0
def rate_thumb(videoid, rating, track_id_jaw):
    """Rate a video on Netflix"""
    common.debug('Thumb rating {} as {}', videoid.value, rating)
    event_uuid = common.get_random_uuid()
    response = common.make_call(
        'post',
        {'component': 'set_thumb_rating',
         'data': {
             'eventUuid': event_uuid,
             'titleId': int(videoid.value),
             'trackId': track_id_jaw,
             'rating': rating,
         }})
    if response.get('status', '') == 'success':
        ui.show_notification(common.get_local_string(30045).split('|')[rating])
    else:
        common.error('Rating thumb error, response detail: {}', response)
        ui.show_error_info('Rating error', 'Error type: {}' + response.get('status', '--'),
                           True, True)
Beispiel #19
0
def export_new_episodes(videoid, silent=False, nfo_settings=None):
    """
    Export new episodes for a tv show by it's video id
    :param videoid: The videoid of the tv show to process
    :param silent: don't display user interface while exporting
    :param nfo_settings: the nfo settings
    :return: None
    """

    method = execute_library_tasks_silently if silent else execute_library_tasks

    if videoid.mediatype == common.VideoId.SHOW:
        common.debug('Exporting new episodes for {}', videoid)
        method(videoid, [export_new_item],
               title=common.get_local_string(30198),
               nfo_settings=nfo_settings)
    else:
        common.debug('{} is not a tv show, no new episodes will be exported',
                     videoid)
Beispiel #20
0
def update_loco_context(context_name):
    """Update a loco list by context"""
    # This api seem no more needed to update the continueWatching loco list
    loco_root = g.LOCAL_DB.get_value('loco_root_id', '', TABLE_SESSION)

    context_index = g.LOCAL_DB.get_value('loco_{}_index'.format(context_name.lower()), '', TABLE_SESSION)
    context_id = g.LOCAL_DB.get_value('loco_{}_id'.format(context_name.lower()), '', TABLE_SESSION)

    if not context_index:
        common.warn('Update loco context {} skipped due to missing loco index', context_name)
        return
    path = [['locos', loco_root, 'refreshListByContext']]
    # After the introduction of LoCo, the following notes are to be reviewed (refers to old LoLoMo):
    #   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.
    params = [common.enclose_quotes(context_id),
              context_index,
              common.enclose_quotes(context_name),
              '']
    # path_suffixs = [
    #    [{'from': 0, 'to': 100}, 'itemSummary'],
    #    [['componentSummary']]
    # ]
    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 loco 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 loco context on netflix',
                             time=10000)
Beispiel #21
0
 def clear_library(self, show_prg_dialog=True):
     """
     Delete all exported items to the library
     :param show_prg_dialog: if True, will be show a progress dialog window
     """
     LOG.info('Start deleting exported library items')
     # This will clear all the add-on library data, to prevents possible inconsistencies when for some reason
     # such as improper use of the add-on, unexpected error or other has broken the library database data or files
     with ui.ProgressDialog(show_prg_dialog, common.get_local_string(30245), max_value=3) as progress_dlg:
         progress_dlg.perform_step()
         progress_dlg.set_wait_message()
         G.SHARED_DB.purge_library()
         for folder_name in [FOLDER_NAME_MOVIES, FOLDER_NAME_SHOWS]:
             progress_dlg.perform_step()
             progress_dlg.set_wait_message()
             section_root_dir = common.join_folders_paths(get_library_path(), folder_name)
             common.delete_folder_contents(section_root_dir, delete_subfolders=True)
     # Clean the Kodi library database
     common.clean_library(show_prg_dialog)
def _create_profile_item(profile_guid, profile, html_parser):
    """Create a tuple that can be added to a Kodi directory that represents
    a profile as listed in the profiles listing"""
    profile_name = profile.get('profileName', '')
    unescaped_profile_name = html_parser.unescape(profile_name)
    enc_profile_name = profile_name.encode('utf-8')
    list_item = list_item_skeleton(label=unescaped_profile_name,
                                   icon=profile.get('avatar'))
    list_item.select(profile.get('isActive', False))
    autologin_url = common.build_url(
        pathitems=['save_autologin', profile_guid],
        params={'autologin_user': enc_profile_name},
        mode=g.MODE_ACTION)
    list_item.addContextMenuItems([(common.get_local_string(30053),
                                    'RunPlugin({})'.format(autologin_url))])
    url = common.build_url(pathitems=['home'],
                           params={'profile_id': profile_guid},
                           mode=g.MODE_DIRECTORY)
    return (url, list_item, True)
Beispiel #23
0
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_continue_watching_index', '', TABLE_SESSION)
    context_id = g.LOCAL_DB.get_value('lolomo_continue_watching_id', '', TABLE_SESSION)

    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)
    except Exception:  # pylint: disable=broad-except
        # I do not know the reason yet, but sometimes continues to return error 401,
        # making it impossible to update the bookmark position
        ui.show_notification(title=common.get_local_string(30105),
                             msg='An error prevented the update the lolomo context on netflix',
                             time=10000)
Beispiel #24
0
def search_add():
    """Perform actions to add and execute a new research"""
    # Ask to user the type of research
    search_types_desc = [
        SEARCH_TYPES_DESC.get(stype, 'Unknown') for stype in SEARCH_TYPES
    ]
    type_index = ui.show_dlg_select(common.get_local_string(30401),
                                    search_types_desc)
    if type_index == -1:  # Cancelled
        return False
    # If needed ask to user other info, then save the research to the database
    search_type = SEARCH_TYPES[type_index]
    row_id = None
    if search_type == 'text':
        search_term = ui.ask_for_search_term()
        if search_term and search_term.strip():
            row_id = G.LOCAL_DB.insert_search_item(SEARCH_TYPES[type_index],
                                                   search_term.strip())
    elif search_type == 'audio_lang':
        row_id = _search_add_bylang(SEARCH_TYPES[type_index],
                                    api.get_available_audio_languages())
    elif search_type == 'subtitles_lang':
        row_id = _search_add_bylang(SEARCH_TYPES[type_index],
                                    api.get_available_subtitles_languages())
    elif search_type == 'genre_id':
        genre_id = ui.show_dlg_input_numeric(search_types_desc[type_index],
                                             mask_input=False)
        if genre_id:
            row_id = _search_add_bygenreid(SEARCH_TYPES[type_index], genre_id)
    else:
        raise NotImplementedError(
            'Search type index {} not implemented'.format(type_index))
    # Redirect to "search" endpoint (otherwise no results in JSON-RPC)
    # Rewrite path history using dir_update_listing + container_update
    # (otherwise will retrigger input dialog on Back or Container.Refresh)
    if row_id is not None and search_query(row_id, 0, False):
        url = common.build_url(['search', 'search', row_id],
                               mode=G.MODE_DIRECTORY,
                               params={'dir_update_listing': True})
        common.container_update(url, False)
        return True
    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.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
Beispiel #26
0
 def start_services(self):
     """
     Start the background services
     """
     from resources.lib.services.playback.action_controller import ActionController
     from resources.lib.services.library_updater import LibraryUpdateService
     from resources.lib.services.settings_monitor import SettingsMonitor
     for server in self.SERVERS:
         server['instance'].server_activate()
         server['instance'].timeout = 1
         server['thread'].start()
         info('[{}] Thread started'.format(server['name']))
     self.controller = ActionController()
     self.library_updater = LibraryUpdateService()
     self.settings_monitor = SettingsMonitor()
     # Mark the service as active
     self._set_service_status('running')
     if not g.ADDON.getSettingBool('disable_startup_notification'):
         from resources.lib.kodi.ui import show_notification
         show_notification(get_local_string(30110))
 def search(self, pathitems):
     """Ask for a search term if none is given via path, query API
     and display search results"""
     if len(pathitems) == 2:
         # Show 'search term' window
         search_term = ui.ask_for_search_term()
         pathitems.append(search_term)
     else:
         # Do a research
         search_term = pathitems[2]
     if search_term:
         search_results = api.search(search_term, self.perpetual_range_start)
         if search_results.videos:
             listings.build_video_listing(search_results, g.MAIN_MENU_ITEMS['search'], pathitems)
             _handle_endofdirectory(self.dir_update_listing)
         else:
             ui.show_notification(common.get_local_string(30013))
             xbmcplugin.endOfDirectory(g.PLUGIN_HANDLE, succeeded=False)
     else:
         xbmcplugin.endOfDirectory(g.PLUGIN_HANDLE, succeeded=False)
Beispiel #28
0
def _remove_from_kodi_library(videoid):
    """Remove an item from the Kodi library."""
    common.debug('Removing {} videoid from Kodi library'.format(videoid))
    try:
        kodi_library_item = get_item(videoid)
        rpc_params = {
            'movie': ['VideoLibrary.RemoveMovie', 'movieid'],
            'show': ['VideoLibrary.RemoveTVShow', 'tvshowid'],
            'episode': ['VideoLibrary.RemoveEpisode', 'episodeid']
        }[videoid.mediatype]
        common.json_rpc(rpc_params[0],
                        {rpc_params[1]: kodi_library_item[rpc_params[1]]})
    except ItemNotFound:
        common.debug(
            'Cannot remove {} from Kodi library, item not present'.format(
                videoid))
    except KeyError as exc:
        ui.show_notification(common.get_local_string(30120), time=7500)
        common.warn('Cannot remove {} from Kodi library, '
                    'Kodi does not support this (yet)'.format(exc))
Beispiel #29
0
 def __init__(self, path_response):
     self.perpetual_range_selector = path_response.get(
         '_perpetual_range_selector')
     self.data = path_response
     has_data = 'search' in path_response
     self.videos = OrderedDict()
     self.videoids = None
     self.artitem = None
     self.contained_titles = None
     if has_data:
         self.title = common.get_local_string(30100).format(
             list(self.data['search']['byTerm'])[0][1:])
         self.videos = OrderedDict(
             resolve_refs(
                 list(self.data['search']['byReference'].values())[0],
                 self.data))
         self.videoids = _get_videoids(self.videos)
         # self.artitem = next(itervalues(self.videos), None)
         self.artitem = listvalues(self.videos)[0] if self.videos else None
         self.contained_titles = _get_titles(self.videos)
Beispiel #30
0
def export_new_episodes(videoid, silent=False):
    """
    Export new episodes for a tv show by it's video id
    :param videoid: The videoid of the tv show to process
    :param scan: Whether or not to scan the library after exporting, useful for a single show
    :param silent: don't display user interface while exporting
    :return: None
    """

    method = execute_library_tasks_silently if silent else execute_library_tasks

    if videoid.mediatype == common.VideoId.SHOW:
        common.debug('Exporting new episodes for {}'.format(videoid))
        method(videoid, [export_new_item],
               title=common.get_local_string(30198),
               sync_mylist=False)
    else:
        common.debug(
            '{} is not a tv show, no new episodes will be exported'.format(
                videoid))