diff --git a/resources/lib/context_entry.py b/resources/lib/context_entry.py index 2428847f..c96a469e 100644 --- a/resources/lib/context_entry.py +++ b/resources/lib/context_entry.py @@ -1,19 +1,20 @@ # -*- coding: utf-8 -*- ############################################################################### -import logging +from logging import getLogger -import xbmc -import xbmcaddon +from xbmc import getInfoLabel, sleep, executebuiltin, getCondVisibility +from xbmcaddon import Addon import plexdb_functions as plexdb -from utils import window, settings, dialog, language as lang, kodiSQL +from utils import window, settings, dialog, language as lang from dialogs import context from PlexFunctions import delete_item_from_pms import variables as v +import state ############################################################################### -LOG = logging.getLogger("PLEX." + __name__) +LOG = getLogger("PLEX." + __name__) OPTIONS = { 'Refresh': lang(30410), @@ -30,69 +31,74 @@ OPTIONS = { class ContextMenu(object): - + """ + Class initiated if user opens "Plex options" on a PLEX item using the Kodi + context menu + """ _selected_option = None def __init__(self): - self.kodi_id = xbmc.getInfoLabel('ListItem.DBID').decode('utf-8') - self.item_type = self._get_item_type() - self.item_id = self._get_item_id(self.kodi_id, self.item_type) - - LOG.info("Found item_id: %s item_type: %s", - self.item_id, self.item_type) - - if not self.item_id: + """ + Simply instantiate with ContextMenu() - no need to call any methods + """ + self.kodi_id = getInfoLabel('ListItem.DBID').decode('utf-8') + self.kodi_type = self._get_kodi_type() + self.plex_id = self._get_plex_id(self.kodi_id, self.kodi_type) + if self.kodi_type: + self.plex_type = v.PLEX_TYPE_FROM_KODI_TYPE[self.kodi_type] + else: + self.plex_type = None + LOG.debug("Found plex_id: %s plex_type: %s", + self.plex_id, self.plex_type) + if not self.plex_id: return - if self._select_menu(): self._action_menu() - if self._selected_option in (OPTIONS['Delete'], OPTIONS['Refresh']): LOG.info("refreshing container") - xbmc.sleep(500) - xbmc.executebuiltin('Container.Refresh') + sleep(500) + executebuiltin('Container.Refresh') - @classmethod - def _get_item_type(cls): - item_type = xbmc.getInfoLabel('ListItem.DBTYPE').decode('utf-8') - if not item_type: - if xbmc.getCondVisibility('Container.Content(albums)'): - item_type = "album" - elif xbmc.getCondVisibility('Container.Content(artists)'): - item_type = "artist" - elif xbmc.getCondVisibility('Container.Content(songs)'): - item_type = "song" - elif xbmc.getCondVisibility('Container.Content(pictures)'): - item_type = "picture" + @staticmethod + def _get_kodi_type(): + kodi_type = getInfoLabel('ListItem.DBTYPE').decode('utf-8') + if not kodi_type: + if getCondVisibility('Container.Content(albums)'): + kodi_type = v.KODI_TYPE_ALBUM + elif getCondVisibility('Container.Content(artists)'): + kodi_type = v.KODI_TYPE_ARTIST + elif getCondVisibility('Container.Content(songs)'): + kodi_type = v.KODI_TYPE_SONG + elif getCondVisibility('Container.Content(pictures)'): + kodi_type = v.KODI_TYPE_PHOTO else: - LOG.info("item_type is unknown") - return item_type + LOG.info("kodi_type is unknown") + kodi_type = None + return kodi_type - @classmethod - def _get_item_id(cls, kodi_id, item_type): - item_id = xbmc.getInfoLabel('ListItem.Property(plexid)') - if not item_id and kodi_id and item_type: + @staticmethod + def _get_plex_id(kodi_id, kodi_type): + plex_id = getInfoLabel('ListItem.Property(plexid)') or None + if not plex_id and kodi_id and kodi_type: with plexdb.Get_Plex_DB() as plexcursor: - item = plexcursor.getItem_byKodiId(kodi_id, item_type) + item = plexcursor.getItem_byKodiId(kodi_id, kodi_type) try: - item_id = item[0] + plex_id = item[0] except TypeError: - LOG.error('Could not get the Plex id for context menu') - return item_id + LOG.info('Could not get the Plex id for context menu') + return plex_id def _select_menu(self): - # Display select dialog + """ + Display select dialog + """ options = [] - # if user uses direct paths, give option to initiate playback via PMS - if (window('useDirectPaths') == 'true' and - self.item_type in v.KODI_VIDEOTYPES): + if state.DIRECT_PATHS and self.kodi_type in v.KODI_VIDEOTYPES: options.append(OPTIONS['PMS_Play']) - - if self.item_type in v.KODI_VIDEOTYPES: + if self.kodi_type in v.KODI_VIDEOTYPES: options.append(OPTIONS['Transcode']) - # userdata = self.api.getUserData() # if userdata['Favorite']: # # Remove from emby favourites @@ -100,11 +106,9 @@ class ContextMenu(object): # else: # # Add to emby favourites # options.append(OPTIONS['AddFav']) - - # if self.item_type == "song": + # if self.kodi_type == "song": # # Set custom song rating # options.append(OPTIONS['RateSong']) - # Refresh item # options.append(OPTIONS['Refresh']) # Delete item, only if the Plex Home main user is logged in @@ -113,103 +117,65 @@ class ContextMenu(object): options.append(OPTIONS['Delete']) # Addon settings options.append(OPTIONS['Addon']) - context_menu = context.ContextMenu( "script-emby-context.xml", - xbmcaddon.Addon( - 'plugin.video.plexkodiconnect').getAddonInfo('path'), - "default", "1080i") + Addon('plugin.video.plexkodiconnect').getAddonInfo('path'), + "default", + "1080i") context_menu.set_options(options) context_menu.doModal() - if context_menu.is_selected(): self._selected_option = context_menu.get_selected() - return self._selected_option def _action_menu(self): - + """ + Do whatever the user selected to do + """ selected = self._selected_option - if selected == OPTIONS['Transcode']: - window('plex_forcetranscode', value='true') + state.FORCE_TRANSCODE = True self._PMS_play() - elif selected == OPTIONS['PMS_Play']: self._PMS_play() - # elif selected == OPTIONS['Refresh']: # self.emby.refreshItem(self.item_id) - # elif selected == OPTIONS['AddFav']: # self.emby.updateUserRating(self.item_id, favourite=True) - # elif selected == OPTIONS['RemoveFav']: # self.emby.updateUserRating(self.item_id, favourite=False) - # elif selected == OPTIONS['RateSong']: # self._rate_song() - elif selected == OPTIONS['Addon']: - xbmc.executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)') - + executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)') elif selected == OPTIONS['Delete']: self._delete_item() - def _rate_song(self): - - conn = kodiSQL('music') - cursor = conn.cursor() - query = "SELECT rating FROM song WHERE idSong = ?" - cursor.execute(query, (self.kodi_id,)) - try: - value = cursor.fetchone()[0] - current_value = int(round(float(value), 0)) - except TypeError: - pass - else: - new_value = dialog("numeric", 0, lang(30411), str(current_value)) - if new_value > -1: - - new_value = int(new_value) - if new_value > 5: - new_value = 5 - - if settings('enableUpdateSongRating') == "true": - musicutils.updateRatingToFile(new_value, self.api.get_file_path()) - - query = "UPDATE song SET rating = ? WHERE idSong = ?" - cursor.execute(query, (new_value, self.kodi_id,)) - conn.commit() - finally: - cursor.close() - def _delete_item(self): - + """ + Delete item on PMS + """ delete = True if settings('skipContextMenu') != "true": - - if not dialog("yesno", heading=lang(29999), line1=lang(33041)): - LOG.info("User skipped deletion for: %s", self.item_id) + if not dialog("yesno", heading="{plex}", line1=lang(33041)): + LOG.info("User skipped deletion for: %s", self.plex_id) delete = False - if delete: - LOG.info("Deleting Plex item with id %s", self.item_id) - if delete_item_from_pms(self.item_id) is False: + LOG.info("Deleting Plex item with id %s", self.plex_id) + if delete_item_from_pms(self.plex_id) is False: dialog("ok", heading="{plex}", line1=lang(30414)) def _PMS_play(self): """ For using direct paths: Initiates playback using the PMS """ - window('plex_contextplay', value='true') + state.CONTEXT_MENU_PLAY = True params = { - 'filename': '/library/metadata/%s' % self.item_id, - 'id': self.item_id, - 'dbid': self.kodi_id, - 'mode': "play" + 'mode': 'play', + 'plex_id': self.plex_id, + 'plex_type': self.plex_type } from urllib import urlencode handle = ("plugin://plugin.video.plexkodiconnect/movies?%s" % urlencode(params)) - xbmc.executebuiltin('RunPlugin(%s)' % handle) + executebuiltin('RunPlugin(%s)' % handle) diff --git a/resources/lib/playback.py b/resources/lib/playback.py index b00904bf..8934ccd9 100644 --- a/resources/lib/playback.py +++ b/resources/lib/playback.py @@ -17,7 +17,7 @@ from playutils import PlayUtils from PKC_listitem import PKC_ListItem from pickler import pickle_me, Playback_Successful import json_rpc as js -from utils import window, settings, dialog, language as lang, tryEncode +from utils import settings, dialog, language as lang, tryEncode from plexbmchelper.subscribers import LOCKER import variables as v import state @@ -120,8 +120,6 @@ def playback_init(plex_id, plex_type, playqueue): Playback setup if Kodi starts playing an item for the first time. """ LOG.info('Initializing PKC playback') - contextmenu_play = window('plex_contextplay') == 'true' - window('plex_contextplay', clear=True) xml = GetPlexMetadata(plex_id) try: xml[0].attrib @@ -158,6 +156,9 @@ def playback_init(plex_id, plex_type, playqueue): # Sleep a bit to let setResolvedUrl do its thing - bit ugly sleep(200) _process_stack(playqueue, stack) + # Reset some playback variables + state.CONTEXT_MENU_PLAY = False + state.FORCE_TRANSCODE = False # New thread to release this one sooner (e.g. harddisk spinning up) thread = Thread(target=Player().play, args=(playqueue.kodi_pl, )) @@ -176,7 +177,10 @@ def _prep_playlist_stack(xml): stack = [] for item in xml: api = API(item) - if api.getType() != v.PLEX_TYPE_CLIP: + if (state.CONTEXT_MENU_PLAY is False or + api.getType() != v.PLEX_TYPE_CLIP): + # If user chose to play via PMS or force transcode, do not + # use the item path stored in the Kodi DB with plexdb.Get_Plex_DB() as plex_db: plex_dbitem = plex_db.getItem_byId(api.getRatingKey()) kodi_id = plex_dbitem[0] if plex_dbitem else None @@ -243,6 +247,7 @@ def _process_stack(playqueue, stack, fill_queue=False): playlist_item.offset = item['offset'] playlist_item.part = item['part'] playlist_item.id = item['id'] + playlist_item.force_transcode = state.FORCE_TRANSCODE playlist_item.init_done = True pos += 1 if fill_queue: @@ -356,7 +361,8 @@ def process_indirect(key, offset, resolve=True): pickle_me(result) else: thread = Thread(target=Player().play, - args={'item': tryEncode(playurl), 'listitem': listitem}) + args={'item': tryEncode(playurl), + 'listitem': listitem}) thread.setDaemon(True) LOG.info('Done initializing PKC playback, starting Kodi player') thread.start() diff --git a/resources/lib/player.py b/resources/lib/player.py index 49436094..6e1363de 100644 --- a/resources/lib/player.py +++ b/resources/lib/player.py @@ -5,7 +5,6 @@ from logging import getLogger from xbmc import Player -from utils import window from downloadutils import DownloadUtils as DU from plexbmchelper.subscribers import LOCKER import variables as v @@ -26,10 +25,6 @@ def playback_cleanup(): # We might have saved a transient token from a user flinging media via # Companion (if we could not use the playqueue to store the token) state.PLEX_TRANSIENT_TOKEN = None - for item in ('plex_customplaylist', - 'plex_customplaylist.seektime', - 'plex_forcetranscode'): - window(item, clear=True) for playerid in state.ACTIVE_PLAYERS: status = state.PLAYER_STATES[playerid] # Remember the last played item later diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index e9bd004d..5c2d67ba 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -197,6 +197,7 @@ class Playlist_Item(object): playcount = None [int] how many times the item has already been played offset = None [int] the item's view offset UPON START in Plex time part = 0 [int] part number if Plex video consists of mult. parts + force_transcode [bool] defaults to False init_done = False Set to True only if run through playback init """ def __init__(self): @@ -215,6 +216,7 @@ class Playlist_Item(object): self.offset = None # If Plex video consists of several parts; part number self.part = 0 + self.force_transcode = False self.init_done = False def __repr__(self): diff --git a/resources/lib/playutils.py b/resources/lib/playutils.py index 89da5e70..11cfe418 100644 --- a/resources/lib/playutils.py +++ b/resources/lib/playutils.py @@ -87,7 +87,8 @@ class PlayUtils(): - codec is in h265 - 10bit video codec - HEVC codec - - window variable 'plex_forcetranscode' set to 'true' + - playqueue_item force_transcode is set to True + - state variable FORCE_TRANSCODE set to True (excepting trailers etc.) - video bitrate above specified settings bitrate if the corresponding file settings are set to 'true' @@ -97,7 +98,7 @@ class PlayUtils(): return False videoCodec = self.api.getVideoCodec() LOG.info("videoCodec: %s" % videoCodec) - if window('plex_forcetranscode') == 'true': + if self.item.force_transcode is True: LOG.info('User chose to force-transcode') return True codec = videoCodec['videocodec'] diff --git a/resources/lib/state.py b/resources/lib/state.py index 6ac85f40..1da14c2d 100644 --- a/resources/lib/state.py +++ b/resources/lib/state.py @@ -143,6 +143,10 @@ RESUMABLE = False # Set by SpecialMonitor - did user choose to resume playback or start from the # beginning? RESUME_PLAYBACK = False +# Was the playback initiated by the user using the Kodi context menu? +CONTEXT_MENU_PLAY = False +# Set by context menu - shall we force-transcode the next playing item? +FORCE_TRANSCODE = False # Kodi webserver details WEBSERVER_PORT = 8080