예제 #1
0
    def recommendations(self, pathitems):
        """Show video lists for a genre"""
        menu_data = G.MAIN_MENU_ITEMS.get(pathitems[1])
        call_args = {
            'menu_data': menu_data,
            'genre_id': None,
            'force_use_videolist_id': True,
        }
        list_data, extra_data = common.make_call('get_genres', call_args)

        finalize_directory(convert_list_to_dir_items(list_data),
                           G.CONTENT_FOLDER,
                           title=get_title(menu_data, extra_data),
                           sort_type='sort_label')
        end_of_directory(False)
        return menu_data.get('view')
예제 #2
0
 def home(self, pathitems=None):  # pylint: disable=unused-argument
     """Show home listing"""
     if 'switch_profile_guid' in self.params and G.CURRENT_LOADED_DIRECTORY in [
             None, 'root', 'profiles'
     ]:
         if not activate_profile(self.params['switch_profile_guid']):
             xbmcplugin.endOfDirectory(G.PLUGIN_HANDLE, succeeded=False)
             return
     LOG.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(True)
def chunked_custom_video_list(chunked_video_list):
    """Retrieve a video list which contains the videos specified by video_ids"""
    chunked_video_ids = chunked_video_list['video_ids']
    common.debug('Requesting custom video list with {} chunked video list',
                 len(chunked_video_ids))
    merged_response = {}
    for video_ids in chunked_video_ids:
        path_response = common.make_call(
            'path_request',
            build_paths(['videos', video_ids], VIDEO_LIST_PARTIAL_PATHS))
        common.merge_dicts(path_response, merged_response)

    perpetual_range_selector = chunked_video_list['perpetual_range_selector']
    if perpetual_range_selector:
        merged_response.update(perpetual_range_selector)
    return CustomVideoList(merged_response)
    def supplemental(self, pathitems):  # pylint: disable=unused-argument
        """Show supplemental video list (eg. trailers) of a tv show / movie"""
        menu_data = {'path': ['is_context_menu_item', 'is_context_menu_item'],  # Menu item do not exists
                     'title': common.get_local_string(30179)}
        from json import loads
        call_args = {
            'menu_data': menu_data,
            'video_id_dict': loads(self.params['video_id_dict']),
            'supplemental_type': self.params['supplemental_type']
        }
        list_data, extra_data = common.make_call('get_video_list_supplemental', call_args)

        finalize_directory(convert_list_to_dir_items(list_data), menu_data.get('content_type', g.CONTENT_SHOW),
                           title=get_title(menu_data, extra_data))
        end_of_directory(self.dir_update_listing)
        return menu_data.get('view')
 def force_update_list(self, pathitems=None):  # pylint: disable=unused-argument
     """Clear the cache of my list to force the update"""
     if self.params['menu_id'] == 'myList':
         G.CACHE.clear([cache_utils.CACHE_MYLIST], clear_database=False)
     if self.params['menu_id'] == 'continueWatching':
         # Delete the cache of continueWatching list
         # pylint: disable=unused-variable
         is_exists, list_id = common.make_call(
             'get_continuewatching_videoid_exists', {'video_id': ''})
         if list_id:
             G.CACHE.delete(cache_utils.CACHE_COMMON,
                            list_id,
                            including_suffixes=True)
         # When the continueWatching context is invalidated from a refreshListByContext call
         # the LoCo need to be updated to obtain the new list id, so we delete the cache to get new data
         G.CACHE.delete(cache_utils.CACHE_COMMON, 'loco_list')
예제 #6
0
    def home(self, pathitems=None, cache_to_disc=True):  # pylint: disable=unused-argument
        """Show home listing"""
        if 'switch_profile_guid' in self.params:
            # This is executed only when you have selected a profile from the profile list
            if not self._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(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 video_list(self, pathitems):
        """Show a video list of a list ID"""
        menu_data = g.MAIN_MENU_ITEMS.get(pathitems[1])
        if not menu_data:  # Dynamic menus
            menu_data = g.LOCAL_DB.get_value(pathitems[1], table=TABLE_MENU_DATA, data_type=dict)
        call_args = {
            'list_id': pathitems[2],
            'menu_data': menu_data,
            'is_dynamic_id': not g.is_known_menu_context(pathitems[2])
        }
        list_data, extra_data = common.make_call('get_video_list', call_args)

        finalize_directory(convert_list_to_dir_items(list_data), menu_data.get('content_type', g.CONTENT_SHOW),
                           title=get_title(menu_data, extra_data))
        end_of_directory(False)
        return menu_data.get('view')
 def home(self, pathitems=None, is_exec_profile_switch=True):  # pylint: disable=unused-argument
     """Show home listing"""
     if is_exec_profile_switch and 'switch_profile_guid' in self.params and is_parent_root_path(
     ):
         # This must be 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
     LOG.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(True)
예제 #9
0
def episodes(videoid, videoid_value, perpetual_range_start=None):  # pylint: disable=unused-argument
    """Retrieve episodes of a season"""
    if videoid.mediatype != common.VideoId.SEASON:
        raise common.InvalidVideoId('Cannot request episode list for {}'
                                    .format(videoid))
    common.debug('Requesting episode list for {}', videoid)
    paths = [['seasons', videoid.seasonid, 'summary']]
    paths.extend(build_paths(['seasons', videoid.seasonid, 'episodes', RANGE_SELECTOR],
                             EPISODES_PARTIAL_PATHS))
    paths.extend(build_paths(['videos', videoid.tvshowid],
                             ART_PARTIAL_PATHS + [['title']]))
    callargs = {
        'paths': paths,
        'length_params': ['stdlist_wid', ['seasons', videoid.seasonid, 'episodes']],
        'perpetual_range_start': perpetual_range_start
    }
    return EpisodeList(videoid, common.make_call('perpetual_path_request', callargs))
예제 #10
0
def video_list(list_id):
    """Retrieve a single video list
    this type of request seems to have results fixed at ~40 from netflix
    and the 'length' tag never return to the actual total count of the elements
    """
    common.debug('Requesting video list {}'.format(list_id))
    return VideoList(
        common.make_call(
            'perpetual_path_request', {
                'path_type':
                'videolist',
                'paths':
                build_paths(['lists', list_id, RANGE_SELECTOR, 'reference'],
                            VIDEO_LIST_PARTIAL_PATHS),
                'length_params1':
                list_id
            }))
예제 #11
0
def remove_watched_status(videoid):
    """Request to Netflix service to delete the watched status (delete also the item from "continue watching" list)"""
    # WARNING: THE NF SERVICE MAY TAKE UNTIL TO 24 HOURS TO REMOVE IT
    try:
        data = common.make_call(
            'post_safe', {
                'endpoint': 'viewing_activity',
                'data': {
                    'movieID': videoid.value,
                    'seriesAll': videoid.mediatype == common.VideoId.SHOW,
                    'guid': G.LOCAL_DB.get_active_profile_guid()
                }
            })
        return data.get('status', False)
    except Exception as exc:  # pylint: disable=broad-except
        LOG.error('remove_watched_status raised this error: {}', exc)
        return False
예제 #12
0
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)
예제 #13
0
def seasons(videoid):
    """Retrieve seasons of a TV show"""
    if videoid.mediatype != common.VideoId.SHOW:
        raise common.InvalidVideoId(
            'Cannot request season list for {}'.format(videoid))
    common.debug('Requesting season list for show {}'.format(videoid))
    return SeasonList(
        videoid,
        common.make_call(
            'perpetual_path_request', {
                'path_type':
                'seasonlist',
                'length_params': [videoid.tvshowid],
                'paths':
                build_paths(['videos', videoid.tvshowid],
                            SEASONS_PARTIAL_PATHS)
            }))
예제 #14
0
def search(search_term, perpetual_range_start=None):
    """Retrieve a video list of search results"""
    common.debug('Searching for {}'.format(search_term))
    base_path = [
        'search', 'byTerm', '|' + search_term, 'titles', MAX_PATH_REQUEST_SIZE
    ]
    paths = [base_path + [['id', 'name', 'requestId']]]
    paths.extend(
        build_paths(base_path + [RANGE_SELECTOR, 'reference'],
                    VIDEO_LIST_PARTIAL_PATHS))
    callargs = {
        'paths': paths,
        'length_params': ['searchlist', ['search', 'byReference']],
        'perpetual_range_start': perpetual_range_start
    }
    return SearchVideoList(common.make_call('perpetual_path_request',
                                            callargs))
예제 #15
0
    def genres(self, pathitems):
        """Show loco list of a genre or from loco root the list of contexts specified in the menu data"""
        menu_data = G.MAIN_MENU_ITEMS.get(pathitems[1])
        if not menu_data:  # Dynamic menus
            menu_data = G.LOCAL_DB.get_value(pathitems[1], table=TABLE_MENU_DATA, data_type=dict)
        call_args = {
            'menu_data': menu_data,
            # When genre_id is None is loaded the loco root the list of contexts specified in the menu data
            'genre_id': None if len(pathitems) < 3 else int(pathitems[2]),
            'force_use_videolist_id': False,
        }
        list_data, extra_data = common.make_call('get_genres', call_args)

        finalize_directory(convert_list_to_dir_items(list_data), G.CONTENT_FOLDER,
                           title=get_title(menu_data, extra_data), sort_type='sort_label')
        end_of_directory(False)
        return menu_data.get('view')
예제 #16
0
def login(ask_credentials=True):
    """Perform a login"""
    try:
        credentials = {
            'credentials': ui.ask_credentials()
        } if ask_credentials else None
        if not common.make_call('login', credentials):
            return False
        return True
    except MissingCredentialsError:
        # Aborted from user or leave an empty field
        ui.show_notification(common.get_local_string(30112))
        raise
    except LoginError as exc:
        # Login not valid
        ui.show_ok_dialog(common.get_local_string(30008), unicode(exc))
        return False
예제 #17
0
    def _exported_directory(self, pathitems, chunked_video_list,
                            perpetual_range_selector):
        menu_data = G.MAIN_MENU_ITEMS['exported']
        call_args = {
            'pathitems': pathitems,
            'menu_data': menu_data,
            'chunked_video_list': chunked_video_list,
            'perpetual_range_selector': perpetual_range_selector
        }
        list_data, extra_data = common.make_call('get_video_list_chunked',
                                                 call_args)

        finalize_directory(convert_list_to_dir_items(list_data),
                           menu_data.get('content_type', G.CONTENT_SHOW),
                           title=get_title(menu_data, extra_data))
        end_of_directory(self.dir_update_listing)
        return menu_data.get('view')
예제 #18
0
def video_list(list_id):
    """Retrieve a single video list"""
    common.debug('Requesting video list {}'.format(list_id))
    return VideoList(
        common.make_call(
            'perpetual_path_request',
            {
                'path_type':
                'videolist',
                'paths': [[
                    'lists',
                    [list_id],
                    # The length attribute MUST be present!
                    ['displayName', 'context', 'genreId', 'length']
                ]] +
                build_paths(['lists', [list_id], RANGE_SELECTOR, 'reference'],
                            VIDEO_LIST_PARTIAL_PATHS)
            }))
예제 #19
0
 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)
예제 #20
0
def root_lists():
    """Retrieve initial video lists to display on homepage"""
    common.debug('Requesting root lists from API')
    return LoLoMo(common.make_call(
        'path_request',
        [['lolomo',
          {'from': 0, 'to': 40},
          ['displayName', 'context', 'id', 'index', 'length', 'genreId']]] +
        # Titles of first 4 videos in each video list
        [['lolomo',
          {'from': 0, 'to': 40},
          {'from': 0, 'to': 3}, 'reference', ['title', 'summary']]] +
        # Art for first video in each video list
        # (will be displayed as video list art)
        build_paths(['lolomo',
                     {'from': 0, 'to': 40},
                     {'from': 0, 'to': 0}, 'reference'],
                    ART_PARTIAL_PATHS)))
예제 #21
0
def update_lolomo_context(context_name, video_id=None):
    """Update the lolomo list by context"""
    # Should update the context list but it doesn't, what is missing?
    # The remaining requests made on the website that are missing here are of logging type,
    # it seems strange that they use log data to finish the operations are almost impossible to reproduce here:
    # pbo_logblobs /logblob
    # personalization/cl2

    lolomo_data = common.make_call('path_request', [["lolomo", [context_name], ['context', 'id', 'index']]])
    # Note: lolomo root seem differs according to the profile in use
    lolomo_root = lolomo_data['lolomo'][1]
    context_index = lolomo_data['lolomos'][lolomo_root][context_name][2]
    context_id = lolomo_data['lolomos'][lolomo_root][context_index][1]

    path = [['lolomos', lolomo_root, 'refreshListByContext']]
    params = [common.enclose_quotes(context_id),
              context_index,
              common.enclose_quotes(context_name),
              common.enclose_quotes(g.LOCAL_DB.get_value('request_id', table=TABLE_SESSION))]
    # 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
    }
    response = common.make_http_call('callpath_request', callargs)
    common.debug('refreshListByContext response: {}', response)

    callargs = {
        'callpaths': [['refreshVideoCurrentPositions']],
        'params': ['[' + video_id + ']', ''],
    }
    response = common.make_http_call('callpath_request', callargs)
    common.debug('refreshVideoCurrentPositions response: {}', response)
예제 #22
0
def show_profiles_dialog(title=None, title_prefix=None, preselect_guid=None):
    """
    Show a dialog to select a profile

    :return guid of selected profile or None
    """
    if not title:
        title = g.ADDON.getLocalizedString(30128)
    if title_prefix:
        title = title_prefix + ' - ' + title
    # Get profiles data
    list_data, extra_data = make_call('get_profiles', {'request_update': True})  # pylint: disable=unused-variable
    return show_modal_dialog(False,
                             Profiles,
                             'plugin-video-netflix-Profiles.xml',
                             g.ADDON.getAddonInfo('path'),
                             title=title,
                             list_data=list_data,
                             preselect_guid=preselect_guid)
예제 #23
0
 def _seasons(self, videoid, pathitems):
     """Show the seasons list of a tv show"""
     call_args = {
         'pathitems': pathitems,
         'tvshowid_dict': videoid.to_dict(),
         'perpetual_range_start': self.perpetual_range_start,
     }
     list_data, extra_data = common.make_call('get_seasons', call_args)
     if len(list_data) == 1:
         # Check if Kodi setting "Flatten TV show seasons" is enabled
         value = common.json_rpc('Settings.GetSettingValue',
                                 {'setting': 'videolibrary.flattentvshows'}).get('value')
         if value != 0:  # Values: 0=never, 1=if one season, 2=always
             # If there is only one season, load and show the episodes now
             pathitems = list_data[0]['url'].replace(G.BASE_URL, '').strip('/').split('/')[1:]
             videoid = common.VideoId.from_path(pathitems)
             self._episodes(videoid, pathitems)
             return
     self._seasons_directory(list_data, extra_data)
예제 #24
0
def custom_video_list_basicinfo(context_name, switch_profiles=False):
    """
    Retrieve a single video list
    used only to know which videos are in my list without requesting additional information
    """
    common.debug('Requesting custom video list basic info for {}', context_name)
    paths = build_paths([context_name, 'az', RANGE_SELECTOR],
                        VIDEO_LIST_BASIC_PARTIAL_PATHS)
    callargs = {
        'paths': paths,
        'length_params': ['stdlist', [context_name, 'az']],
        'perpetual_range_start': None,
        'no_limit_req': True
    }
    # When the list is empty the server returns an empty response
    callname = 'perpetual_path_request_switch_profiles'\
        if switch_profiles else 'perpetual_path_request'
    path_response = common.make_call(callname, callargs)
    return {} if not path_response else VideoListSorted(path_response, context_name, None, 'az')
예제 #25
0
def video_list(list_id):
    """Retrieve a single video list"""
    common.debug('Requesting video list {}'.format(list_id))
    return VideoList(
        common.make_call(
            'perpetual_path_request',
            {
                'path_type':
                'videolist',
                'paths':
                build_paths(['lists', list_id, RANGE_SELECTOR, 'reference'],
                            VIDEO_LIST_PARTIAL_PATHS),
                'length_params1':
                list_id
                # Note: using this format: ["lists", "20c4b91b-2a3d-4fd9-ba82-4c7c63c8ccfe_31272600X19XX1551626066006", {"to": 47, "from": 0}, "reference", ...
                # the 'length' parameter never return to the actual total count of the elements
                # because the limit seems fixed at 40 from netflix
                # to pass it, we have to use 'az' or 'su' request by using a context name see video_list_az()
            }))
예제 #26
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)
예제 #27
0
def episodes(videoid):
    """Retrieve episodes of a season"""
    if videoid.mediatype != common.VideoId.SEASON:
        raise common.InvalidVideoId(
            'Cannot request episode list for {}'.format(videoid))
    common.debug('Requesting episode list for {}'.format(videoid))
    return EpisodeList(
        videoid,
        common.make_call(
            'perpetual_path_request', {
                'path_type':
                'episodelist',
                'length_params': [videoid.seasonid],
                'paths':
                [['seasons', videoid.seasonid, 'summary']] + build_paths(
                    ['seasons', videoid.seasonid, 'episodes', RANGE_SELECTOR],
                    EPISODES_PARTIAL_PATHS) +
                build_paths(['videos', videoid.tvshowid],
                            ART_PARTIAL_PATHS + [['title']])
            }))
예제 #28
0
 def trailer(self, videoid):
     """Get the trailer list"""
     from json import dumps
     menu_data = {'path': ['is_context_menu_item', 'is_context_menu_item'],  # Menu item do not exists
                  'title': common.get_local_string(30179)}
     video_id_dict = videoid.to_dict()
     list_data, extra_data = common.make_call('get_video_list_supplemental',  # pylint: disable=unused-variable
                                              {
                                                  'menu_data': menu_data,
                                                  'video_id_dict': video_id_dict,
                                                  'supplemental_type': SUPPLEMENTAL_TYPE_TRAILERS
                                              })
     if list_data:
         url = common.build_url(['supplemental'],
                                params={'video_id_dict': dumps(video_id_dict),
                                        'supplemental_type': SUPPLEMENTAL_TYPE_TRAILERS},
                                mode=G.MODE_DIRECTORY)
         common.container_update(url)
     else:
         ui.show_notification(common.get_local_string(30111))
예제 #29
0
def _metadata(video_id):
    """Retrieve additional metadata for a video.This is a separate method from
    metadata(videoid) to work around caching issues when new episodes are added
    to a show by Netflix."""
    common.debug('Requesting metadata for {}', video_id)
    # Always use params 'movieid' to all videoid identifier
    metadata_data = common.make_call(
        'get',
        {
            'component': 'metadata',
            'req_type': 'api',
            'params': {'movieid': video_id.value}
        })
    if not metadata_data:
        # This return empty
        # - if the metadata is no longer available
        # - if it has been exported a tv show/movie from a specific language profile that is not
        #   available using profiles with other languages
        raise MetadataNotAvailable
    return metadata_data['video']
예제 #30
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)