diff --git a/README.md b/README.md index 0591e8ac..14eb4693 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![stable version](https://img.shields.io/badge/stable_version-2.1.1-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip) -[![beta version](https://img.shields.io/badge/beta_version-2.2.2-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) +[![stable version](https://img.shields.io/badge/stable_version-2.1.2-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip) +[![beta version](https://img.shields.io/badge/beta_version-2.2.3-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) [![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation) [![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq) @@ -53,7 +53,7 @@ Some people argue that PKC is 'hacky' because of the way it directly accesses th ### PKC Features -- Support for Kodi 18 Leia Alpha 1 (nightly versions are NOT supported!) +- Support for Kodi 18 Leia Alpha 2 (nightly versions are NOT supported!) - Support for Kodi 17 Krypton - [Amazon Alexa voice recognition](https://www.plex.tv/apps/streaming-devices/amazon-alexa) - [Cinema Trailers & Extras](https://support.plex.tv/articles/202934883-cinema-trailers-extras/) diff --git a/addon.xml b/addon.xml index b211d57c..19173ed7 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + @@ -67,7 +67,11 @@ Нативная интеграция сервера Plex в Kodi Подключите Kodi к своему серверу Plex. Плагин предполагает что вы управляете своими видео с помощью Plex (а не в Kodi). Вы можете потерять текущие базы данных музыки и видео в Kodi (так как плагин напрямую их изменяет). Используйте на свой страх и риск Используйте на свой страх и риск - version 2.2.2 (beta only): + version 2.2.3 (beta only): +- Compatibility with Kodi Krypton Alpha 2 +- Append tv show and SxxExx to episode playlist entries + +version 2.2.2 (beta only): - Fixes to locking mechanisms which resulted in weird behavior in some cases - Switch to Python __future__ unicode_literals and absolute paths - Fix UnboundLocalError for playlists diff --git a/changelog.txt b/changelog.txt index 20100113..c9ba1a3f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +version 2.2.3 (beta only): +- Compatibility with Kodi Krypton Alpha 2 +- Append tv show and SxxExx to episode playlist entries + version 2.2.2 (beta only): - Fixes to locking mechanisms which resulted in weird behavior in some cases - Switch to Python __future__ unicode_literals and absolute paths diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index fee48e8a..f1f14670 100644 --- a/resources/lib/entrypoint.py +++ b/resources/lib/entrypoint.py @@ -22,11 +22,6 @@ from . import variables as v ############################################################################### LOG = getLogger('PLEX.entrypoint') -try: - HANDLE = int(argv[1]) - ARGV_0 = path_ops.decode_path(argv[0]) -except IndexError: - pass ############################################################################### @@ -119,7 +114,7 @@ def directory_item(label, path, folder=True): {"fanart": "special://home/addons/plugin.video.plexkodiconnect/fanart.jpg"}) listitem.setArt( {"landscape":"special://home/addons/plugin.video.plexkodiconnect/fanart.jpg"}) - xbmcplugin.addDirectoryItem(handle=HANDLE, + xbmcplugin.addDirectoryItem(handle=int(argv[1]), url=path, listitem=listitem, isFolder=folder) @@ -130,7 +125,7 @@ def show_main_menu(content_type=None): Shows the main PKC menu listing with all libraries, Channel, settings, etc. """ LOG.debug('Do main listing with content_type: %s', content_type) - xbmcplugin.setContent(HANDLE, 'files') + xbmcplugin.setContent(int(argv[1]), 'files') # Get emby nodes from the window props plexprops = utils.window('Plex.nodes.total') if plexprops: @@ -169,7 +164,7 @@ def show_main_menu(content_type=None): "plugin://%s?mode=refreshplaylist" % v.ADDON_ID) directory_item(utils.lang(39204), "plugin://%s?mode=manualsync" % v.ADDON_ID) - xbmcplugin.endOfDirectory(HANDLE) + xbmcplugin.endOfDirectory(int(argv[1])) def switch_plex_user(): @@ -268,7 +263,7 @@ def next_up_episodes(tagname, limit): count = 0 # if the addon is called with nextup parameter, # we return the nextepisodes list of the given tagname - xbmcplugin.setContent(HANDLE, 'episodes') + xbmcplugin.setContent(int(argv[1]), 'episodes') # First we get a list of all the TV shows - filtered by tag params = { 'sort': {'order': "descending", 'method': "lastplayed"}, @@ -317,13 +312,13 @@ def next_up_episodes(tagname, limit): 'limits': {"end": 1} } for episode in js.get_episodes(params): - xbmcplugin.addDirectoryItem(handle=HANDLE, + xbmcplugin.addDirectoryItem(handle=int(argv[1]), url=episode['file'], listitem=create_listitem(episode)) count += 1 if count == limit: break - xbmcplugin.endOfDirectory(handle=HANDLE) + xbmcplugin.endOfDirectory(handle=int(argv[1])) def in_progress_episodes(tagname, limit): @@ -333,7 +328,7 @@ def in_progress_episodes(tagname, limit): count = 0 # if the addon is called with inprogressepisodes parameter, # we return the inprogressepisodes list of the given tagname - xbmcplugin.setContent(HANDLE, 'episodes') + xbmcplugin.setContent(int(argv[1]), 'episodes') # First we get a list of all the in-progress TV shows - filtered by tag params = { 'sort': {'order': "descending", 'method': "lastplayed"}, @@ -359,13 +354,13 @@ def in_progress_episodes(tagname, limit): "lastplayed"] } for episode in js.get_episodes(params): - xbmcplugin.addDirectoryItem(handle=HANDLE, + xbmcplugin.addDirectoryItem(handle=int(argv[1]), url=episode['file'], listitem=create_listitem(episode)) count += 1 if count == limit: break - xbmcplugin.endOfDirectory(handle=HANDLE) + xbmcplugin.endOfDirectory(handle=int(argv[1])) def recent_episodes(mediatype, tagname, limit): @@ -375,7 +370,7 @@ def recent_episodes(mediatype, tagname, limit): count = 0 # if the addon is called with recentepisodes parameter, # we return the recentepisodes list of the given tagname - xbmcplugin.setContent(HANDLE, 'episodes') + xbmcplugin.setContent(int(argv[1]), 'episodes') append_show_title = utils.settings('RecentTvAppendShow') == 'true' append_sxxexx = utils.settings('RecentTvAppendSeason') == 'true' # First we get a list of all the TV shows - filtered by tag @@ -405,13 +400,13 @@ def recent_episodes(mediatype, tagname, limit): listitem = create_listitem(episode, append_show_title=append_show_title, append_sxxexx=append_sxxexx) - xbmcplugin.addDirectoryItem(handle=HANDLE, + xbmcplugin.addDirectoryItem(handle=int(argv[1]), url=episode['file'], listitem=listitem) count += 1 if count == limit: break - xbmcplugin.endOfDirectory(handle=HANDLE) + xbmcplugin.endOfDirectory(handle=int(argv[1])) def get_video_files(plex_id, params): @@ -435,14 +430,14 @@ def get_video_files(plex_id, params): if plex_id is None: LOG.info('No Plex ID found, abort getting Extras') - return xbmcplugin.endOfDirectory(HANDLE) + return xbmcplugin.endOfDirectory(int(argv[1])) item = PF.GetPlexMetadata(plex_id) try: path = utils.try_decode(item[0][0][0].attrib['file']) except (TypeError, IndexError, AttributeError, KeyError): LOG.error('Could not get file path for item %s', plex_id) - return xbmcplugin.endOfDirectory(HANDLE) + return xbmcplugin.endOfDirectory(int(argv[1])) # Assign network protocol if path.startswith('\\\\'): path = path.replace('\\\\', 'smb://') @@ -458,20 +453,20 @@ def get_video_files(plex_id, params): item_path = utils.try_encode(path_ops.path.join(root, directory)) listitem = ListItem(item_path, path=item_path) - xbmcplugin.addDirectoryItem(handle=HANDLE, + xbmcplugin.addDirectoryItem(handle=int(argv[1]), url=item_path, listitem=listitem, isFolder=True) for file in files: item_path = utils.try_encode(path_ops.path.join(root, file)) listitem = ListItem(item_path, path=item_path) - xbmcplugin.addDirectoryItem(handle=HANDLE, + xbmcplugin.addDirectoryItem(handle=int(argv[1]), url=file, listitem=listitem) break else: LOG.error('Kodi cannot access folder %s', path) - xbmcplugin.endOfDirectory(HANDLE) + xbmcplugin.endOfDirectory(int(argv[1])) @utils.catch_exceptions(warnuser=False) @@ -487,7 +482,7 @@ def extra_fanart(plex_id, plex_path): plex_id = plex_path.split("/")[-2] if not plex_id: LOG.error('Could not get a plex_id, aborting') - return xbmcplugin.endOfDirectory(HANDLE) + return xbmcplugin.endOfDirectory(int(argv[1])) # We need to store the images locally for this to work # because of the caching system in xbmc @@ -499,7 +494,7 @@ def extra_fanart(plex_id, plex_path): xml = PF.GetPlexMetadata(plex_id) if xml is None: LOG.error('Could not download metadata for %s', plex_id) - return xbmcplugin.endOfDirectory(HANDLE) + return xbmcplugin.endOfDirectory(int(argv[1])) api = API(xml[0]) backdrops = api.artwork()['Backdrop'] @@ -509,7 +504,7 @@ def extra_fanart(plex_id, plex_path): fanart_dir, "fanart%.3d.jpg" % count)) listitem = ListItem("%.3d" % count, path=art_file) xbmcplugin.addDirectoryItem( - handle=HANDLE, + handle=int(argv[1]), url=art_file, listitem=listitem) path_ops.copyfile(backdrop, utils.try_decode(art_file)) @@ -523,10 +518,10 @@ def extra_fanart(plex_id, plex_path): file = utils.decode_path(file) art_file = utils.try_encode(path_ops.path.join(root, file)) listitem = ListItem(file, path=art_file) - xbmcplugin.addDirectoryItem(handle=HANDLE, + xbmcplugin.addDirectoryItem(handle=int(argv[1]), url=art_file, listitem=listitem) - xbmcplugin.endOfDirectory(HANDLE) + xbmcplugin.endOfDirectory(int(argv[1])) def on_deck_episodes(viewid, tagname, limit): @@ -538,7 +533,7 @@ def on_deck_episodes(viewid, tagname, limit): tagname: Name of the Plex library, e.g. "My Movies" limit: Max. number of items to retrieve, e.g. 50 """ - xbmcplugin.setContent(HANDLE, 'episodes') + xbmcplugin.setContent(int(argv[1]), 'episodes') append_show_title = utils.settings('OnDeckTvAppendShow') == 'true' append_sxxexx = utils.settings('OnDeckTvAppendSeason') == 'true' if utils.settings('OnDeckTVextended') == 'false': @@ -550,13 +545,13 @@ def on_deck_episodes(viewid, tagname, limit): if counter == 300: LOG.error('Aborting On Deck view, we were not authenticated ' 'for the PMS') - xbmcplugin.endOfDirectory(HANDLE, False) + xbmcplugin.endOfDirectory(int(argv[1]), False) return sleep(100) xml = DU().downloadUrl('{server}/library/sections/%s/onDeck' % viewid) if xml in (None, 401): LOG.error('Could not download PMS xml for view %s', viewid) - xbmcplugin.endOfDirectory(HANDLE, False) + xbmcplugin.endOfDirectory(int(argv[1]), False) return direct_paths = utils.settings('useDirectPaths') == '1' counter = 0 @@ -569,14 +564,14 @@ def on_deck_episodes(viewid, tagname, limit): listitem.setProperty('resumetime', str(api.resume_point())) path = api.path(force_first_media=False, direct_paths=direct_paths) xbmcplugin.addDirectoryItem( - handle=HANDLE, + handle=int(argv[1]), url=path, listitem=listitem) counter += 1 if counter == limit: break xbmcplugin.endOfDirectory( - handle=HANDLE, + handle=int(argv[1]), cacheToDisc=utils.settings('enableTextureCache') == 'true') return @@ -594,7 +589,7 @@ def on_deck_episodes(viewid, tagname, limit): items = js.get_tv_shows(params) if not items: # Now items retrieved - empty directory - xbmcplugin.endOfDirectory(handle=HANDLE) + xbmcplugin.endOfDirectory(handle=int(argv[1])) return params = { @@ -646,14 +641,14 @@ def on_deck_episodes(viewid, tagname, limit): listitem = create_listitem(episode, append_show_title=append_show_title, append_sxxexx=append_sxxexx) - xbmcplugin.addDirectoryItem(handle=HANDLE, + xbmcplugin.addDirectoryItem(handle=int(argv[1]), url=episode['file'], listitem=listitem, isFolder=False) count += 1 if count >= limit: break - xbmcplugin.endOfDirectory(handle=HANDLE) + xbmcplugin.endOfDirectory(handle=int(argv[1])) def watchlater(): @@ -662,24 +657,25 @@ def watchlater(): """ if utils.window('plex_token') == '': LOG.error('No watch later - not signed in to plex.tv') - return xbmcplugin.endOfDirectory(HANDLE, False) + return xbmcplugin.endOfDirectory(int(argv[1]), False) if utils.window('plex_restricteduser') == 'true': LOG.error('No watch later - restricted user') - return xbmcplugin.endOfDirectory(HANDLE, False) + return xbmcplugin.endOfDirectory(int(argv[1]), False) xml = DU().downloadUrl('https://plex.tv/pms/playlists/queue/all', authenticate=False, headerOptions={'X-Plex-Token': utils.window('plex_token')}) if xml in (None, 401): LOG.error('Could not download watch later list from plex.tv') - return xbmcplugin.endOfDirectory(HANDLE, False) + return xbmcplugin.endOfDirectory(int(argv[1]), False) + LOG.info('Displaying watch later plex.tv items') - xbmcplugin.setContent(HANDLE, 'movies') + xbmcplugin.setContent(int(argv[1]), 'movies') direct_paths = utils.settings('useDirectPaths') == '1' for item in xml: __build_item(item, direct_paths) xbmcplugin.endOfDirectory( - handle=HANDLE, + handle=int(argv[1]), cacheToDisc=utils.settings('enableTextureCache') == 'true') @@ -692,16 +688,16 @@ def channels(): xml[0].attrib except (ValueError, AttributeError, IndexError, TypeError): LOG.error('Could not download Plex Channels') - return xbmcplugin.endOfDirectory(HANDLE, False) + return xbmcplugin.endOfDirectory(int(argv[1]), False) LOG.info('Displaying Plex Channels') - xbmcplugin.setContent(HANDLE, 'files') + xbmcplugin.setContent(int(argv[1]), 'files') for method in v.SORT_METHODS_DIRECTORY: - xbmcplugin.addSortMethod(HANDLE, getattr(xbmcplugin, method)) + xbmcplugin.addSortMethod(int(argv[1]), getattr(xbmcplugin, method)) for item in xml: __build_folder(item) xbmcplugin.endOfDirectory( - handle=HANDLE, + handle=int(argv[1]), cacheToDisc=utils.settings('enableTextureCache') == 'true') @@ -718,7 +714,7 @@ def browse_plex(key=None, plex_section_id=None): xml[0].attrib except (ValueError, AttributeError, IndexError, TypeError): LOG.error('Could not browse to %s', key) - return xbmcplugin.endOfDirectory(HANDLE, False) + return xbmcplugin.endOfDirectory(int(argv[1]), False) photos = False movies = False @@ -757,45 +753,45 @@ def browse_plex(key=None, plex_section_id=None): # Set the correct content type if movies is True: - xbmcplugin.setContent(HANDLE, 'movies') + xbmcplugin.setContent(int(argv[1]), 'movies') sort_methods = v.SORT_METHODS_MOVIES elif clips is True: - xbmcplugin.setContent(HANDLE, 'movies') + xbmcplugin.setContent(int(argv[1]), 'movies') sort_methods = v.SORT_METHODS_CLIPS elif photos is True: - xbmcplugin.setContent(HANDLE, 'images') + xbmcplugin.setContent(int(argv[1]), 'images') sort_methods = v.SORT_METHODS_PHOTOS elif tvshows is True: - xbmcplugin.setContent(HANDLE, 'tvshows') + xbmcplugin.setContent(int(argv[1]), 'tvshows') sort_methods = v.SORT_METHOD_TVSHOWS elif episodes is True: - xbmcplugin.setContent(HANDLE, 'episodes') + xbmcplugin.setContent(int(argv[1]), 'episodes') sort_methods = v.SORT_METHODS_EPISODES elif songs is True: - xbmcplugin.setContent(HANDLE, 'songs') + xbmcplugin.setContent(int(argv[1]), 'songs') sort_methods = v.SORT_METHODS_SONGS elif artists is True: - xbmcplugin.setContent(HANDLE, 'artists') + xbmcplugin.setContent(int(argv[1]), 'artists') sort_methods = v.SORT_METHODS_ARTISTS elif albums is True: - xbmcplugin.setContent(HANDLE, 'albums') + xbmcplugin.setContent(int(argv[1]), 'albums') sort_methods = v.SORT_METHODS_ALBUMS elif musicvideos is True: - xbmcplugin.setContent(HANDLE, 'musicvideos') + xbmcplugin.setContent(int(argv[1]), 'musicvideos') sort_methods = v.SORT_METHODS_MOVIES else: - xbmcplugin.setContent(HANDLE, 'files') + xbmcplugin.setContent(int(argv[1]), 'files') sort_methods = v.SORT_METHODS_DIRECTORY for method in sort_methods: - xbmcplugin.addSortMethod(HANDLE, getattr(xbmcplugin, method)) + xbmcplugin.addSortMethod(int(argv[1]), getattr(xbmcplugin, method)) # Set the Kodi title for this view title = xml.attrib.get('librarySectionTitle', xml.attrib.get('title1')) - xbmcplugin.setPluginCategory(HANDLE, title) + xbmcplugin.setPluginCategory(int(argv[1]), title) xbmcplugin.endOfDirectory( - handle=HANDLE, + handle=int(argv[1]), cacheToDisc=utils.settings('enableTextureCache') == 'true') @@ -812,7 +808,7 @@ def __build_folder(xml_element, plex_section_id=None): listitem = ListItem(xml_element.attrib.get('title')) listitem.setArt({'thumb': xml_element.attrib.get('thumb'), 'poster': xml_element.attrib.get('art')}) - xbmcplugin.addDirectoryItem(handle=HANDLE, + xbmcplugin.addDirectoryItem(handle=int(argv[1]), url="%s?%s" % (url, urlencode(params)), isFolder=True, listitem=listitem) @@ -838,7 +834,7 @@ def __build_item(xml_element, direct_paths): url = api.path(direct_paths=direct_paths) if api.resume_point(): listitem.setProperty('resumetime', str(api.resume_point())) - xbmcplugin.addDirectoryItem(handle=HANDLE, + xbmcplugin.addDirectoryItem(handle=int(argv[1]), url=url, listitem=listitem) @@ -847,20 +843,20 @@ def extras(plex_id): """ Lists all extras for plex_id """ - xbmcplugin.setContent(HANDLE, 'movies') + xbmcplugin.setContent(int(argv[1]), 'movies') xml = PF.GetPlexMetadata(plex_id) try: xml[0].attrib except (TypeError, IndexError, KeyError): - xbmcplugin.endOfDirectory(HANDLE) + xbmcplugin.endOfDirectory(int(argv[1])) return for item in API(xml[0]).extras(): api = API(item) listitem = api.create_listitem() - xbmcplugin.addDirectoryItem(handle=HANDLE, + xbmcplugin.addDirectoryItem(handle=int(argv[1]), url=api.path(), listitem=listitem) - xbmcplugin.endOfDirectory(HANDLE) + xbmcplugin.endOfDirectory(int(argv[1])) def create_new_pms(): diff --git a/resources/lib/playlists.py b/resources/lib/playlists.py index a5c80ce5..5429d591 100644 --- a/resources/lib/playlists.py +++ b/resources/lib/playlists.py @@ -206,8 +206,27 @@ def _write_playlist_to_file(playlist, xml): text = '#EXTCPlayListM3U::M3U\n' for element in xml: api = API(element) - text += ('#EXTINF:%s,%s\n%s\n' - % (api.runtime(), api.title(), api.path())) + append_season_episode = False + if api.plex_type() == v.PLEX_TYPE_EPISODE: + _, show, season_id, episode_id = api.episode_data() + try: + season_id = int(season_id) + episode_id = int(episode_id) + except ValueError: + pass + else: + append_season_episode = True + if append_season_episode: + text += ('#EXTINF:%s,%s S%.2dE%.2d - %s\n%s\n' + % (api.runtime(), show, season_id, episode_id, + api.title(), api.path())) + else: + # Only append the TV show name + text += ('#EXTINF:%s,%s - %s\n%s\n' + % (api.runtime(), show, api.title(), api.path())) + else: + text += ('#EXTINF:%s,%s\n%s\n' + % (api.runtime(), api.title(), api.path())) text += '\n' text = text.encode(v.M3U_ENCODING, 'strict') try: diff --git a/resources/lib/variables.py b/resources/lib/variables.py index faf5e75e..b71a42fa 100644 --- a/resources/lib/variables.py +++ b/resources/lib/variables.py @@ -85,14 +85,14 @@ MIN_DB_VERSION = '2.0.27' # Database paths _DB_VIDEO_VERSION = { 17: 107, # Krypton - 18: 109 # Leia + 18: 110 # Leia } DB_VIDEO_PATH = try_decode(xbmc.translatePath( "special://database/MyVideos%s.db" % _DB_VIDEO_VERSION[KODIVERSION])) _DB_MUSIC_VERSION = { 17: 60, # Krypton - 18: 70 # Leia + 18: 72 # Leia } DB_MUSIC_PATH = try_decode(xbmc.translatePath( "special://database/MyMusic%s.db" % _DB_MUSIC_VERSION[KODIVERSION]))