def add_info_list_item(list_item: ListItemW, videoid, item, raw_data, is_in_mylist, common_data, art_item=None): """Add infolabels and art to a ListItem""" infos, quality_infos = get_info(videoid, item, raw_data, delayed_db_op=True) list_item.addStreamInfoFromDict(quality_infos) # Use a deepcopy of dict to not reflect future changes to the dictionary also to the cache infos_copy = copy.deepcopy(infos) if 'Plot' not in infos_copy and 'PlotOutline' in infos_copy: # Not all skins support read value from PlotOutline infos_copy['Plot'] = infos_copy['PlotOutline'] _add_supplemental_plot_info(infos_copy, item, common_data) if is_in_mylist and common_data.get('mylist_titles_color'): # Highlight ListItem title when the videoid is contained in my-list list_item.setLabel( _colorize_text(common_data['mylist_titles_color'], list_item.getLabel())) infos_copy['title'] = list_item.getLabel() list_item.setInfo('video', infos_copy) list_item.setArt( get_art(videoid, art_item or item or {}, common_data['profile_language_code'], delayed_db_op=True))
def _create_video_item(videoid_value, video, video_list, perpetual_range_start, common_data): # pylint: disable=unused-argument videoid = common.VideoId.from_videolist_item(video) is_folder = videoid.mediatype == common.VideoId.SHOW is_playable = video['availability']['isPlayable'] is_video_playable = not is_folder and is_playable is_in_mylist = videoid in common_data['mylist_items'] list_item = ListItemW(label=video['title']) list_item.setProperties({ 'isPlayable': str(is_video_playable).lower(), 'nf_videoid': videoid.to_string(), 'nf_is_in_mylist': str(is_in_mylist), 'nf_perpetual_range_start': str(perpetual_range_start) }) add_info_list_item(list_item, videoid, video, video_list.data, is_in_mylist, common_data) if not is_folder: set_watched_status(list_item, video, common_data) if is_playable: # The movie or tvshow (episodes) is playable url = common.build_url( videoid=videoid, mode=G.MODE_DIRECTORY if is_folder else G.MODE_PLAY, params=None if is_folder else common_data['params']) list_item.addContextMenuItems( generate_context_menu_items( videoid, is_in_mylist, perpetual_range_start, common_data['ctxmenu_remove_watched_status'])) else: # The movie or tvshow (episodes) is not available # Try check if there is a availability date list_item.setProperty('nf_availability_message', get_availability_message(video)) # Check if the user has set "Remind Me" feature, try: # Due to the add-on cache we can not change in easy way the value stored in database cache, # then we temporary override the value (see 'remind_me' in navigation/actions.py) is_in_remind_me = G.CACHE.get(CACHE_BOOKMARKS, f'is_in_remind_me_{videoid}') except CacheMiss: # The website check the "Remind Me" value on key "inRemindMeList" and also "queue"/"inQueue" is_in_remind_me = video['inRemindMeList'] or video['queue'][ 'inQueue'] trackid = video['trackIds']['trackId_jaw'] list_item.addContextMenuItems( generate_context_menu_remind_me(videoid, is_in_remind_me, trackid)) url = common.build_url(['show_availability_message'], videoid=videoid, mode=G.MODE_ACTION) return url, list_item, is_folder and is_playable
def _create_episode_item(seasonid, episodeid_value, episode, episodes_list, common_data): is_playable = episode['summary']['isPlayable'] episodeid = seasonid.derive_episode(episodeid_value) list_item = ListItemW(label=episode['title']) list_item.setProperties({ 'isPlayable': str(is_playable).lower(), 'nf_videoid': episodeid.to_string() }) add_info_list_item(list_item, episodeid, episode, episodes_list.data, False, common_data) set_watched_status(list_item, episode, common_data) if is_playable: url = common.build_url(videoid=episodeid, mode=G.MODE_PLAY, params=common_data['params']) list_item.addContextMenuItems( generate_context_menu_items(episodeid, False, None)) else: # The video is not playable, try check if there is a date list_item.setProperty('nf_availability_message', get_availability_message(episode)) url = common.build_url(['show_availability_message'], mode=G.MODE_ACTION) return url, list_item, False
def _create_videolist_item(list_id, video_list, menu_data, common_data, static_lists=False): if static_lists and G.is_known_menu_context(video_list['context']): pathitems = list(menu_data['path']) # Make a copy pathitems.append(video_list['context']) else: # It is a dynamic video list / menu context if menu_data.get('force_use_videolist_id', False): path = 'video_list' else: path = 'video_list_sorted' pathitems = [path, menu_data['path'][1], list_id] list_item = ListItemW(label=video_list['displayName']) add_info_list_item(list_item, video_list.videoid, video_list, video_list.data, False, common_data, art_item=video_list.artitem) # Add possibility to browse the sub-genres (see build_video_listing) sub_genre_id = video_list.get('genreId') params = {'sub_genre_id': str(sub_genre_id)} if sub_genre_id else None return common.build_url(pathitems, params=params, mode=G.MODE_DIRECTORY), list_item, True
def _create_category_item(list_id, video_list, menu_data, common_data, summary_data): pathitems = ['video_list', menu_data['path'][1], list_id] list_item = ListItemW(label=summary_data['displayName']) add_info_list_item(list_item, video_list.videoid, video_list, video_list.data, False, common_data, art_item=video_list.artitem) return common.build_url(pathitems, mode=G.MODE_DIRECTORY), list_item, True
def _create_season_item(tvshowid, seasonid_value, season, season_list, common_data): seasonid = tvshowid.derive_season(seasonid_value) list_item = ListItemW(label=season['summary']['name']) list_item.setProperty('nf_videoid', seasonid.to_string()) add_info_list_item(list_item, seasonid, season, season_list.data, False, common_data, art_item=season_list.artitem) list_item.addContextMenuItems(generate_context_menu_items(seasonid, False, None)) return common.build_url(videoid=seasonid, mode=G.MODE_DIRECTORY), list_item, True
def _create_video_item(videoid_value, video, video_list, perpetual_range_start, common_data): # pylint: disable=unused-argument videoid = common.VideoId.from_videolist_item(video) is_folder = videoid.mediatype == common.VideoId.SHOW is_playable = video['availability']['isPlayable'] is_video_playable = not is_folder and is_playable is_in_mylist = videoid in common_data['mylist_items'] list_item = ListItemW(label=video['title']) list_item.setProperties({ 'isPlayable': str(is_video_playable).lower(), 'nf_videoid': videoid.to_string(), 'nf_is_in_mylist': str(is_in_mylist), 'nf_perpetual_range_start': str(perpetual_range_start) }) add_info_list_item(list_item, videoid, video, video_list.data, is_in_mylist, common_data) if not is_folder: set_watched_status(list_item, video, common_data) if is_playable: url = common.build_url( videoid=videoid, mode=G.MODE_DIRECTORY if is_folder else G.MODE_PLAY, params=None if is_folder else common_data['params']) list_item.addContextMenuItems( generate_context_menu_items( videoid, is_in_mylist, perpetual_range_start, common_data['ctxmenu_remove_watched_status'])) else: # The video is not playable, try check if there is a date list_item.setProperty('nf_availability_message', get_availability_message(video)) url = common.build_url(['show_availability_message'], mode=G.MODE_ACTION) return url, list_item, is_folder and is_playable
def set_watched_status(list_item: ListItemW, video_data, common_data): """Check and set progress status (watched and resume)""" if not common_data['set_watched_status']: return video_id = str(video_data['summary']['id']) # Check from db if user has manually changed the watched status is_watched_user_overrided = G.SHARED_DB.get_watched_status( common_data['active_profile_guid'], video_id, None, bool) resume_time = 0 if is_watched_user_overrided is None: # Note to shakti properties: # 'watched': unlike the name this value is used to other purposes, so not to set a video as watched # 'watchedToEndOffset': this value is used to determine if a video is watched but # is available only with the metadata api and only for "episode" video type # 'creditsOffset' : this value is used as position where to show the (play) "Next" (episode) button # on the website, but it may not be always available with the "movie" video type if 'creditsOffset' in video_data: # To better ensure that a video is marked as watched also when a user do not reach the ending credits # we generally lower the watched threshold by 50 seconds for 50 minutes of video (3000 secs) lower_value = video_data['runtime'] / 3000 * 50 watched_threshold = video_data['creditsOffset'] - lower_value else: # When missing the value should be only a video of movie type, # then we simulate the default Kodi playcount behaviour (playcountminimumpercent) watched_threshold = video_data['runtime'] / 100 * 90 # To avoid asking to the server again the entire list of titles (after watched a video) # to get the updated value, we override the value with the value saved in memory (see am_video_events.py) try: bookmark_position = G.CACHE.get(CACHE_BOOKMARKS, video_id) except CacheMiss: # NOTE shakti 'bookmarkPosition' tag when it is not set have -1 value bookmark_position = video_data['bookmarkPosition'] playcount = '1' if bookmark_position >= watched_threshold else '0' if playcount == '0' and bookmark_position > 0: resume_time = bookmark_position else: playcount = '1' if is_watched_user_overrided else '0' # We have to set playcount with setInfo(), because the setProperty('PlayCount', ) have a bug # when a item is already watched and you force to set again watched, the override do not work list_item.updateInfo({'PlayCount': playcount}) list_item.setProperty('TotalTime', str(video_data['runtime'])) list_item.setProperty('ResumeTime', str(resume_time))
def build_video_listing(video_list, menu_data, sub_genre_id=None, pathitems=None, perpetual_range_start=None, mylist_items=None): """Build a video listing""" common_data = { 'params': get_param_watched_status_by_profile(), 'mylist_items': mylist_items, 'set_watched_status': G.ADDON.getSettingBool('sync_watched_status'), 'supplemental_info_color': get_color_name(G.ADDON.getSettingInt('supplemental_info_color')), 'mylist_titles_color': (get_color_name(G.ADDON.getSettingInt('mylist_titles_color')) if menu_data['path'][1] != 'myList' else None), 'profile_language_code': G.LOCAL_DB.get_profile_config('language', ''), 'ctxmenu_remove_watched_status': menu_data['path'][1] == 'continueWatching', 'active_profile_guid': G.LOCAL_DB.get_active_profile_guid() } directory_items = [_create_video_item(videoid_value, video, video_list, perpetual_range_start, common_data) for videoid_value, video in video_list.videos.items()] # If genre_id exists add possibility to browse LoCo sub-genres if sub_genre_id and sub_genre_id != 'None': # Create dynamic sub-menu info in MAIN_MENU_ITEMS menu_id = 'subgenre_' + sub_genre_id sub_menu_data = menu_data.copy() sub_menu_data['path'] = [menu_data['path'][0], menu_id, sub_genre_id] sub_menu_data['loco_known'] = False sub_menu_data['loco_contexts'] = None sub_menu_data['content_type'] = menu_data.get('content_type', G.CONTENT_SHOW) sub_menu_data.update({'title': common.get_local_string(30089)}) sub_menu_data['initial_menu_id'] = menu_data.get('initial_menu_id', menu_data['path'][1]) G.LOCAL_DB.set_value(menu_id, sub_menu_data, TABLE_MENU_DATA) # Create the folder for the access to sub-genre folder_list_item = ListItemW(label=common.get_local_string(30089)) folder_list_item.setArt({'icon': 'DefaultVideoPlaylists.png'}) folder_list_item.setInfo('video', {'plot': common.get_local_string(30088)}) # The description directory_items.insert(0, (common.build_url(['genres', menu_id, sub_genre_id], mode=G.MODE_DIRECTORY), folder_list_item, True)) # add_items_previous_next_page use the new value of perpetual_range_selector add_items_previous_next_page(directory_items, pathitems, video_list.perpetual_range_selector, sub_genre_id) G.CACHE_MANAGEMENT.execute_pending_db_ops() return directory_items, {}
def _create_profile_item(profile_guid, is_selected, is_autoselect, is_library_playback, detailed_info): profile_name = G.LOCAL_DB.get_profile_config('profileName', '???', guid=profile_guid) profile_attributes = [] if G.LOCAL_DB.get_profile_config('isPinLocked', False, guid=profile_guid): profile_attributes.append('[COLOR red]' + common.get_local_string(20068) + '[/COLOR]') if G.LOCAL_DB.get_profile_config('isAccountOwner', False, guid=profile_guid): profile_attributes.append(common.get_local_string(30221)) if G.LOCAL_DB.get_profile_config('isKids', False, guid=profile_guid): profile_attributes.append(common.get_local_string(30222)) if is_autoselect and detailed_info: profile_attributes.append(common.get_local_string(30054)) if is_library_playback and detailed_info: profile_attributes.append(common.get_local_string(30051)) attributes_desc = '[CR]'.join( profile_attributes) + '[CR]' if profile_attributes else '' description = attributes_desc + '[' + G.LOCAL_DB.get_profile_config( 'language_desc', '', guid=profile_guid) + ']' if detailed_info: menu_items = generate_context_menu_profile(profile_guid, is_autoselect, is_library_playback) else: menu_items = [] list_item = ListItemW(label=profile_name) list_item.setProperties({ 'nf_guid': profile_guid, 'nf_description': description.replace('[CR]', ' - ') }) list_item.setArt({ 'icon': G.LOCAL_DB.get_profile_config('avatar', '', guid=profile_guid) }) list_item.setInfo('video', {'plot': description}) list_item.select(is_selected) list_item.addContextMenuItems(menu_items) return (common.build_url(pathitems=['home'], params={'switch_profile_guid': profile_guid}, mode=G.MODE_DIRECTORY), list_item, True)
def _create_subgenre_item(video_list_id, subgenre_data, menu_data): pathitems = ['video_list_sorted', menu_data['path'][1], video_list_id] list_item = ListItemW(label=subgenre_data['name']) return common.build_url(pathitems, mode=G.MODE_DIRECTORY), list_item, True
def build_mainmenu_listing(loco_list): """Builds the main menu listing (my list, continue watching, etc.)""" from resources.lib.kodi.context_menu import generate_context_menu_mainmenu directory_items = [] common_data = { 'profile_language_code': G.LOCAL_DB.get_profile_config('language', ''), 'supplemental_info_color': get_color_name(G.ADDON.getSettingInt('supplemental_info_color')) } for menu_id, data in G.MAIN_MENU_ITEMS.items(): if data.get('has_show_setting', True) and not G.ADDON.getSettingBool('_'.join( ('show_menu', menu_id))): continue if data['loco_known']: list_id, video_list = loco_list.find_by_context( data['loco_contexts'][0]) if not list_id: continue menu_title = video_list['displayName'] directory_item = _create_videolist_item(list_id, video_list, data, common_data, static_lists=True) directory_item[1].addContextMenuItems( generate_context_menu_mainmenu(menu_id)) directory_items.append(directory_item) else: menu_title = common.get_local_string(data['label_id']) if data.get( 'label_id') else 'Missing menu title' menu_description = (common.get_local_string(data['description_id']) if data['description_id'] is not None else '') list_item = ListItemW(label=menu_title) list_item.setArt({'icon': data['icon']}) list_item.setInfo('video', {'plot': menu_description}) list_item.addContextMenuItems( generate_context_menu_mainmenu(menu_id)) directory_items.append( (common.build_url(data['path'], mode=G.MODE_DIRECTORY), list_item, True)) # Save the menu titles, to reuse it when will be open the content of menus G.LOCAL_DB.set_value(menu_id, {'title': menu_title}, TABLE_MENU_DATA) # Add "Profiles" menu pfl_list_item = ListItemW(label=common.get_local_string(13200)) pfl_list_item.setArt({'icon': 'DefaultUser.png'}) directory_items.append( (common.build_url(['profiles'], mode=G.MODE_DIRECTORY), pfl_list_item, True)) G.CACHE_MANAGEMENT.execute_pending_db_ops() return directory_items, {}
def add_items_previous_next_page(directory_items, pathitems, perpetual_range_selector, sub_genre_id=None, path_params=None): if pathitems and perpetual_range_selector: if 'previous_start' in perpetual_range_selector: params = {'perpetual_range_start': perpetual_range_selector.get('previous_start'), 'sub_genre_id': sub_genre_id if perpetual_range_selector.get('previous_start') == 0 else None} if path_params: params.update(path_params) previous_page_item = ListItemW(label=common.get_local_string(30148)) previous_page_item.setProperty('specialsort', 'top') # Force an item to stay on top previous_page_item.setArt({'thumb': _get_custom_thumb_path('FolderPagePrevious.png')}) directory_items.insert(0, (common.build_url(pathitems=pathitems, params=params, mode=G.MODE_DIRECTORY), previous_page_item, True)) if 'next_start' in perpetual_range_selector: params = {'perpetual_range_start': perpetual_range_selector.get('next_start')} if path_params: params.update(path_params) next_page_item = ListItemW(label=common.get_local_string(30147)) next_page_item.setProperty('specialsort', 'bottom') # Force an item to stay on bottom next_page_item.setArt({'thumb': _get_custom_thumb_path('FolderPageNext.png')}) directory_items.append((common.build_url(pathitems=pathitems, params=params, mode=G.MODE_DIRECTORY), next_page_item, True))