diff --git a/addon.xml b/addon.xml index d6238e74c..1f5947948 100644 --- a/addon.xml +++ b/addon.xml @@ -1,21 +1,20 @@ - + - + + video - + -[fix] Nexus compatibility with InfoTagVideo |contrib: jurialmunkey| -[chg] make httpd /api url case insensitive due to skin choice of uppercase settings text -[chg] use listitem property for Inputstream Adaptive headers instead of url piped on Nexus+ +[chg] add-on is now Nexus+ compatible, removed compatibility with older versions of Kodi [upd] Translations updated from Kodi Weblate diff --git a/resources/lib/youtube_plugin/external/__init__.py b/resources/lib/youtube_plugin/external/__init__.py deleted file mode 100644 index 644de4f82..000000000 --- a/resources/lib/youtube_plugin/external/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -""" - - Copyright (C) 2023 plugin.video.youtube - - SPDX-License-Identifier: GPL-2.0-only - See LICENSES/GPL-2.0-only for more information. -""" - -__all__ = ['listitem'] diff --git a/resources/lib/youtube_plugin/external/listitem.py b/resources/lib/youtube_plugin/external/listitem.py deleted file mode 100644 index f2ea96a3d..000000000 --- a/resources/lib/youtube_plugin/external/listitem.py +++ /dev/null @@ -1,218 +0,0 @@ -# -*- coding: utf-8 -*- -# Module: default -# Author: jurialmunkey -# License: GPL v.3 https://www.gnu.org/copyleft/gpl.html -from xbmc import Actor, VideoStreamDetail, AudioStreamDetail, SubtitleStreamDetail, LOGINFO -from xbmc import log as kodi_log - - -class ListItemInfoTag(): - INFO_TAG_ATTR = { - 'video': { - 'tag_getter': 'getVideoInfoTag', - 'tag_attr': { - 'genre': {'attr': 'setGenres', 'convert': lambda x: [x], 'classinfo': (list, tuple)}, - 'country': {'attr': 'setCountries', 'convert': lambda x: [x], 'classinfo': (list, tuple)}, - 'year': {'attr': 'setYear', 'convert': int, 'classinfo': int}, - 'episode': {'attr': 'setEpisode', 'convert': int, 'classinfo': int}, - 'season': {'attr': 'setSeason', 'convert': int, 'classinfo': int}, - 'sortepisode': {'attr': 'setSortEpisode', 'convert': int, 'classinfo': int}, - 'sortseason': {'attr': 'setSortSeason', 'convert': int, 'classinfo': int}, - 'episodeguide': {'attr': 'setEpisodeGuide', 'convert': str, 'classinfo': str}, - 'showlink': {'attr': 'setShowLinks', 'convert': lambda x: [x], 'classinfo': (list, tuple)}, - 'top250': {'attr': 'setTop250', 'convert': int, 'classinfo': int}, - 'setid': {'attr': 'setSetId', 'convert': int, 'classinfo': int}, - 'tracknumber': {'attr': 'setTrackNumber', 'convert': int, 'classinfo': int}, - 'rating': {'attr': 'setRating', 'convert': float, 'classinfo': float}, - 'userrating': {'attr': 'setUserRating', 'convert': int, 'classinfo': int}, - 'watched': {'skip': True}, # Evaluated internally in Nexus based on playcount so skip - 'playcount': {'attr': 'setPlaycount', 'convert': int, 'classinfo': int}, - 'overlay': {'skip': True}, # Evaluated internally in Nexus based on playcount so skip - 'cast': {'route': 'set_info_cast'}, - 'castandrole': {'route': 'set_info_cast'}, - 'director': {'attr': 'setDirectors', 'convert': lambda x: [x], 'classinfo': (list, tuple)}, - 'mpaa': {'attr': 'setMpaa', 'convert': str, 'classinfo': str}, - 'plot': {'attr': 'setPlot', 'convert': str, 'classinfo': str}, - 'plotoutline': {'attr': 'setPlotOutline', 'convert': str, 'classinfo': str}, - 'title': {'attr': 'setTitle', 'convert': str, 'classinfo': str}, - 'originaltitle': {'attr': 'setOriginalTitle', 'convert': str, 'classinfo': str}, - 'sorttitle': {'attr': 'setSortTitle', 'convert': str, 'classinfo': str}, - 'duration': {'attr': 'setDuration', 'convert': int, 'classinfo': int}, - 'studio': {'attr': 'setStudios', 'convert': lambda x: [x], 'classinfo': (list, tuple)}, - 'tagline': {'attr': 'setTagLine', 'convert': str, 'classinfo': str}, - 'writer': {'attr': 'setWriters', 'convert': lambda x: [x], 'classinfo': (list, tuple)}, - 'tvshowtitle': {'attr': 'setTvShowTitle', 'convert': str, 'classinfo': str}, - 'premiered': {'attr': 'setPremiered', 'convert': str, 'classinfo': str}, - 'status': {'attr': 'setTvShowStatus', 'convert': str, 'classinfo': str}, - 'set': {'attr': 'setSet', 'convert': str, 'classinfo': str}, - 'setoverview': {'attr': 'setSetOverview', 'convert': str, 'classinfo': str}, - 'tag': {'attr': 'setTags', 'convert': lambda x: [x], 'classinfo': (list, tuple)}, - 'imdbnumber': {'attr': 'setIMDBNumber', 'convert': str, 'classinfo': str}, - 'code': {'attr': 'setProductionCode', 'convert': str, 'classinfo': str}, - 'aired': {'attr': 'setFirstAired', 'convert': str, 'classinfo': str}, - 'credits': {'attr': 'setWriters', 'convert': lambda x: [x], 'classinfo': (list, tuple)}, - 'lastplayed': {'attr': 'setLastPlayed', 'convert': str, 'classinfo': str}, - 'album': {'attr': 'setAlbum', 'convert': str, 'classinfo': str}, - 'artist': {'attr': 'setArtists', 'convert': lambda x: [x], 'classinfo': (list, tuple)}, - 'votes': {'attr': 'setVotes', 'convert': int, 'classinfo': int}, - 'path': {'attr': 'setPath', 'convert': str, 'classinfo': str}, - 'trailer': {'attr': 'setTrailer', 'convert': str, 'classinfo': str}, - 'dateadded': {'attr': 'setDateAdded', 'convert': str, 'classinfo': str}, - 'date': {'attr': 'setDateAdded', 'convert': str, 'classinfo': str}, - 'mediatype': {'attr': 'setMediaType', 'convert': str, 'classinfo': str}, - 'dbid': {'attr': 'setDbId', 'convert': int, 'classinfo': int}, - } - }, - 'music': { - 'tag_getter': 'getMusicInfoTag', - 'tag_attr': { - 'tracknumber': {'attr': 'setTrack', 'convert': int, 'classinfo': int}, - 'discnumber': {'attr': 'setDisc', 'convert': int, 'classinfo': int}, - 'duration': {'attr': 'setDuration', 'convert': int, 'classinfo': int}, - 'year': {'attr': 'setYear', 'convert': int, 'classinfo': int}, - 'genre': {'attr': 'setGenres', 'convert': lambda x: [x], 'classinfo': (list, tuple)}, - 'album': {'attr': 'setAlbum', 'convert': str, 'classinfo': str}, - 'artist': {'attr': 'setArtist', 'convert': str, 'classinfo': str}, - 'title': {'attr': 'setTitle', 'convert': str, 'classinfo': str}, - 'rating': {'attr': 'setRating', 'convert': float, 'classinfo': float}, - 'userrating': {'attr': 'setUserRating', 'convert': int, 'classinfo': int}, - 'lyrics': {'attr': 'setLyrics', 'convert': str, 'classinfo': str}, - 'playcount': {'attr': 'setPlayCount', 'convert': int, 'classinfo': int}, - 'lastplayed': {'attr': 'setLastPlayed', 'convert': str, 'classinfo': str}, - 'mediatype': {'attr': 'setMediaType', 'convert': str, 'classinfo': str}, - 'dbid': {'route': 'set_info_music_dbid'}, - 'listeners': {'attr': 'setListeners', 'convert': int, 'classinfo': int}, - 'musicbrainztrackid': {'attr': 'setMusicBrainzTrackID', 'convert': str, 'classinfo': str}, - 'musicbrainzartistid': {'attr': 'setMusicBrainzArtistID', 'convert': lambda x: [x], 'classinfo': (list, tuple)}, - 'musicbrainzalbumid': {'attr': 'setMusicBrainzAlbumID', 'convert': str, 'classinfo': str}, - 'musicbrainzalbumartistid': {'attr': 'setMusicBrainzAlbumArtistID', 'convert': lambda x: [x], 'classinfo': (list, tuple)}, - 'comment': {'attr': 'setComment', 'convert': str, 'classinfo': str}, - 'albumartist': {'attr': 'setAlbumArtist', 'convert': str, 'classinfo': str}, # Not listed in setInfo docs but included for forward compatibility - } - }, - 'game': { - 'tag_getter': 'getGameInfoTag', - 'tag_attr': { - 'title': {'attr': 'setTitle', 'convert': str, 'classinfo': str}, - 'platform': {'attr': 'setPlatform', 'convert': str, 'classinfo': str}, - 'genres': {'attr': 'setGenres', 'convert': lambda x: [x], 'classinfo': (list, tuple)}, - 'publisher': {'attr': 'setPublisher', 'convert': str, 'classinfo': str}, - 'developer': {'attr': 'setDeveloper', 'convert': str, 'classinfo': str}, - 'overview': {'attr': 'setOverview', 'convert': str, 'classinfo': str}, - 'year': {'attr': 'setYear', 'convert': int, 'classinfo': int}, - 'gameclient': {'attr': 'setGameClient', 'convert': str, 'classinfo': str}, - } - } - } - - def __init__(self, listitem, tag_type: str = 'video', type_check=False): - """ - Pass xbmcgui.ListItem() to listitem with tag_type to the library type normally in li.setInfo(type=) - Optional set type_check= - - False: (default) - - Slightly increases performance by avoiding additional internal type checks - - Relys on Kodi Python API raising a TypeError to determine when to force type conversion - - Kodi creates EXCEPTION log spam when infolabels require type conversion - - True: - - Slightly descreases performance by requiring additional internal type checks - - Uses internal isinstance type check to determine when to force type conversion - - Prevents Kodi EXCEPTION log spam when infolabels require type conversion - """ - self._listitem = listitem - self._tag_type = tag_type - self._tag_attr = self.INFO_TAG_ATTR[tag_type]['tag_attr'] - self._info_tag = getattr(self._listitem, self.INFO_TAG_ATTR[tag_type]['tag_getter'])() - self._type_chk = type_check - - def set_info(self, infolabels: dict): - """ Wrapper for compatibility with Matrix ListItem.setInfo() method """ - for k, v in infolabels.items(): - if v is None: - continue - try: - _tag_attr = self._tag_attr[k] - func = getattr(self._info_tag, _tag_attr['attr']) - if self._type_chk and not isinstance(v, _tag_attr['classinfo']): - raise TypeError - func(v) - - except KeyError: - if k not in self._tag_attr: - log_msg = f'[script.module.infotagger] set_info:\nKeyError: {k}' - kodi_log(log_msg, level=LOGINFO) - continue - - if _tag_attr.get('skip'): - continue - - if 'route' in _tag_attr: - getattr(self, _tag_attr['route'])(v, infolabels) - continue - - log_msg = _tag_attr.get('log_msg') or '' - log_msg = f'[script.module.infotagger] set_info:\nKeyError: {log_msg}' - kodi_log(log_msg, level=LOGINFO) - continue - - except TypeError: - func(_tag_attr['convert'](v)) # Attempt to force conversion to correct type - - def set_info_music_dbid(self, dbid: int, infolabels: dict, *args, **kwargs): - """ Wrapper for InfoTagMusic.setDbId to retrieve mediatype """ - try: - mediatype = infolabels['mediatype'] - self._info_tag.setDbId(int(dbid), mediatype) - except (KeyError, TypeError): - return - - def set_info_cast(self, cast: list, *args, **kwargs): - """ Wrapper to convert cast and castandrole from ListItem.setInfo() to InfoTagVideo.setCast() """ - def _set_cast_member(x, i): - if not isinstance(i, tuple): - i = (i, '',) - return {'name': f'{i[0]}', 'role': f'{i[1]}', 'order': x, 'thumbnail': ''} - - self._info_tag.setCast([Actor(**_set_cast_member(x, i)) for x, i in enumerate(cast, start=1)]) - - def set_cast(self, cast: list): - """ Wrapper for compatibility with Matrix ListItem.setCast() method """ - self._info_tag.setCast([Actor(**i) for i in cast]) - - def set_unique_ids(self, unique_ids: dict, default_id: str = None): - """ Wrapper for compatibility with Matrix ListItem.setUniqueIDs() method """ - self._info_tag.setUniqueIDs({k: f'{v}' for k, v in unique_ids.items()}, default_id) - - def set_stream_details(self, stream_details: dict): - """ Wrapper for compatibility with multiple ListItem.addStreamInfo() methods in one call """ - if not stream_details: - return - - try: - for i in stream_details['video']: - try: - self._info_tag.addVideoStream(VideoStreamDetail(**i)) - except TypeError: - # TEMP BANDAID workaround for inconsistent key names prior to Nexus Beta changes - i['hdrType'] = i.pop('hdrtype', '') - i['stereoMode'] = i.pop('stereomode', '') - self._info_tag.addVideoStream(VideoStreamDetail(**i)) - except (KeyError, TypeError): - pass - - try: - for i in stream_details['audio']: - self._info_tag.addAudioStream(AudioStreamDetail(**i)) - except (KeyError, TypeError): - pass - - try: - for i in stream_details['subtitle']: - self._info_tag.addSubtitleStream(SubtitleStreamDetail(**i)) - except (KeyError, TypeError): - pass - - def add_stream_info(self, stream_type, stream_values): - """ Wrapper for compatibility with Matrix ListItem.addStreamInfo() method """ - stream_details = {'video': [], 'audio': [], 'subtitle': []} - stream_details[stream_type] = [stream_values] - self.set_stream_details(stream_details) diff --git a/resources/lib/youtube_plugin/kodion/impl/abstract_settings.py b/resources/lib/youtube_plugin/kodion/impl/abstract_settings.py index 1a6e82c35..b79c3b0c7 100644 --- a/resources/lib/youtube_plugin/kodion/impl/abstract_settings.py +++ b/resources/lib/youtube_plugin/kodion/impl/abstract_settings.py @@ -63,7 +63,7 @@ def get_bool(self, setting_id, default_value): return value == 'true' def get_items_per_page(self): - return self.get_int(constants.setting.ITEMS_PER_PAGE, 50, lambda x: (x + 1) * 5) + return self.get_int(constants.setting.ITEMS_PER_PAGE, 50) def get_video_quality(self, quality_map_override=None): vq_dict = {0: 240, diff --git a/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_items.py b/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_items.py index 011287857..4ae824612 100644 --- a/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_items.py +++ b/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_items.py @@ -10,6 +10,8 @@ import xbmcgui +from infotagger.listitem import ListItemInfoTag + from ...items import VideoItem, AudioItem, UriItem from ... import utils from . import info_labels @@ -33,7 +35,6 @@ def to_play_item(context, play_item): else: list_item = xbmcgui.ListItem(label=utils.to_unicode(title)) if major_version >= 20: - from ....external.listitem import ListItemInfoTag info_tag = ListItemInfoTag(list_item, tag_type='video') if not is_strm: @@ -121,7 +122,6 @@ def to_video_item(context, video_item): else: item = xbmcgui.ListItem(label=utils.to_unicode(title)) if major_version >= 20: - from ....external.listitem import ListItemInfoTag info_tag = ListItemInfoTag(item, tag_type='video') if video_item.get_fanart() and settings.show_fanart(): fanart = video_item.get_fanart() @@ -196,7 +196,6 @@ def to_audio_item(context, audio_item): else: item = xbmcgui.ListItem(label=utils.to_unicode(title)) if major_version >= 20: - from ....external.listitem import ListItemInfoTag info_tag = ListItemInfoTag(item, tag_type='music') if audio_item.get_fanart() and settings.show_fanart(): fanart = audio_item.get_fanart() diff --git a/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_runner.py b/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_runner.py index 9f69f4c16..eaebef290 100644 --- a/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_runner.py +++ b/resources/lib/youtube_plugin/kodion/impl/xbmc/xbmc_runner.py @@ -11,6 +11,8 @@ import xbmcgui import xbmcplugin +from infotagger.listitem import ListItemInfoTag + from ..abstract_provider_runner import AbstractProviderRunner from ...exceptions import KodionException from ...items import * @@ -98,7 +100,6 @@ def _add_directory(self, context, directory_item, item_count=0): else: item = xbmcgui.ListItem(label=directory_item.get_name()) if major_version >= 20: - from ....external.listitem import ListItemInfoTag info_tag = ListItemInfoTag(item, tag_type='video') # only set fanart is enabled diff --git a/resources/settings.xml b/resources/settings.xml index dd1d7696c..240043a94 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -1,151 +1,961 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +
+ + + + 0 + 3 + + + + + + + + + + + + false + + + + + + 0 + false + + + false + + + + + + 0 + false + + + + 0 + RunPlugin(plugin://plugin.video.youtube/config/subtitles/) + + true + + + + true + + + true + + + + true + + + + 0 + 0 + + + false + + + + + + + + 0 + false + + + false + + + + + + 0 + false + + + + 0 + false + + + + 0 + 85 + + 1 + 1 + 99 + + + + true + + + + false + + + + 0 + true + + + + 0 + false + + + + 0 + false + + + true + + + + + + 0 + false + + + + + + 0 + 1 + + + + + + + + + + 0 + true + + + + 0 + true + + + true + + + + + + 0 + 50 + + 5 + 5 + 50 + + + false + + + + 0 + 10 + + 0 + 10 + 200 + + + false + + + + 0 + 0 + + + + + + + + + + + 0 + true + + + + + + + + 0 + false + + + System.HasAddon(inputstream.adaptive) + + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/config/mpd/) + + true + + + + true + + + + true + + + + + + 0 + true + + + true + + + + + + 0 + 8 + + + + + + + + + + + + + + + + + true + + + + + + 0 + false + + + + true + 8 + + + + + + + 0 + false + + + + true + false + 8 + 9 + + + + + + + + + 0 + true + + + true + + + + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/inputstreamhelper/install/) + + true + + + + true + + + + + + + + + + 0 + true + + + + 0 + true + + + + 0 + false + + + + 0 + false + + + true + + + true + + + + + + 0 + + + true + + + + true + + + true + + + + 30585 + + + + 0 + true + + + + 0 + true + + + + 0 + true + + + + 0 + false + + + + 0 + false + + + + 0 + true + + + + 0 + true + + + + 0 + true + + + false + + + + + + 0 + true + + + + 0 + + + true + + + + true + + + true + + + + 30037 + + + + 0 + true + + + + 0 + true + + + + 0 + true + + + + 0 + + + true + + + + true + + + true + + + + 30038 + + + + 0 + true + + + + 0 + true + + + false + + + + + + 0 + true + + + + 0 + true + + + + 0 + true + + + + 0 + true + + + + 0 + true + + + + 0 + true + + + + 0 + true + + + + 0 + true + + + + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/users/add/?refresh=false) + + true + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/users/remove/?refresh=false) + + true + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/users/rename/?refresh=false) + + true + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/users/switch/?refresh=false) + + true + + + + + + + + + 0 + true + + + + 0 + false + + + + 0 + false + + + true + + + + + + 0 + true + + + + + + 0 + false + + + + + + 0 + 10 + + 5 + 1 + 100 + + + false + + + + 0 + true + + + + + + 0 + en-US + + + false + + + + 30523 + + + + 0 + US + + + false + + + + 30550 + + + + 0 + + + true + + + + true + + + + 30651 + + + + 0 + 500 + + 1 + 1 + 1000 + + + + true + + + + false + + + + + + + + 0 + 0.0.0.0 + + + true + + + + 30643 + + + + 0 + RunPlugin(plugin://plugin.video.youtube/config/listen_ip/) + + true + + + true + + + + 0 + 50152 + + + true + + + + 30619 + + + + 0 + + + true + + + + true + + + + 30629 + + + + 0 + RunPlugin(plugin://plugin.video.youtube/show_client_ip/) + + true + + + + + + + + + 0 + + + true + + + 30201 + + + + 0 + + + true + + + 30202 + + + + 0 + + + true + + + 30203 + + + + 0 + true + + + + + + 0 + false + + + + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/function_cache/clear/) + + true + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/data_cache/clear/) + + true + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/search_cache/clear/) + + true + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/playback_history/clear/) + + true + + + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/function_cache/delete/) + + true + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/data_cache/delete/) + + true + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/search_cache/delete/) + + true + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/playback_history/delete/) + + true + + + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/access_manager/reset/) + + true + + + true + + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/settings_xml/delete/) + + true + + + true + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/api_keys/delete/) + + true + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/access_manager/delete/) + + true + + + + + 0 + RunPlugin(plugin://plugin.video.youtube/maintain/temp_files/delete/) + + true + + + + + 0 + + + true + + + + false + + + + + + + + 0 + + + true + + + + false + + + + + + + + 0 + 0 + + + false + + + + + + + + 0 + + + true + + + + false + + + + + + + + +