Enable context menu playback

This commit is contained in:
croneter 2018-02-03 12:45:48 +01:00
parent a6a8c18711
commit bd85bb445e
6 changed files with 94 additions and 120 deletions

View file

@ -1,19 +1,20 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################### ###############################################################################
import logging from logging import getLogger
import xbmc from xbmc import getInfoLabel, sleep, executebuiltin, getCondVisibility
import xbmcaddon from xbmcaddon import Addon
import plexdb_functions as plexdb 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 dialogs import context
from PlexFunctions import delete_item_from_pms from PlexFunctions import delete_item_from_pms
import variables as v import variables as v
import state
############################################################################### ###############################################################################
LOG = logging.getLogger("PLEX." + __name__) LOG = getLogger("PLEX." + __name__)
OPTIONS = { OPTIONS = {
'Refresh': lang(30410), 'Refresh': lang(30410),
@ -30,69 +31,74 @@ OPTIONS = {
class ContextMenu(object): class ContextMenu(object):
"""
Class initiated if user opens "Plex options" on a PLEX item using the Kodi
context menu
"""
_selected_option = None _selected_option = None
def __init__(self): def __init__(self):
self.kodi_id = xbmc.getInfoLabel('ListItem.DBID').decode('utf-8') """
self.item_type = self._get_item_type() Simply instantiate with ContextMenu() - no need to call any methods
self.item_id = self._get_item_id(self.kodi_id, self.item_type) """
self.kodi_id = getInfoLabel('ListItem.DBID').decode('utf-8')
LOG.info("Found item_id: %s item_type: %s", self.kodi_type = self._get_kodi_type()
self.item_id, self.item_type) self.plex_id = self._get_plex_id(self.kodi_id, self.kodi_type)
if self.kodi_type:
if not self.item_id: 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 return
if self._select_menu(): if self._select_menu():
self._action_menu() self._action_menu()
if self._selected_option in (OPTIONS['Delete'], if self._selected_option in (OPTIONS['Delete'],
OPTIONS['Refresh']): OPTIONS['Refresh']):
LOG.info("refreshing container") LOG.info("refreshing container")
xbmc.sleep(500) sleep(500)
xbmc.executebuiltin('Container.Refresh') executebuiltin('Container.Refresh')
@classmethod @staticmethod
def _get_item_type(cls): def _get_kodi_type():
item_type = xbmc.getInfoLabel('ListItem.DBTYPE').decode('utf-8') kodi_type = getInfoLabel('ListItem.DBTYPE').decode('utf-8')
if not item_type: if not kodi_type:
if xbmc.getCondVisibility('Container.Content(albums)'): if getCondVisibility('Container.Content(albums)'):
item_type = "album" kodi_type = v.KODI_TYPE_ALBUM
elif xbmc.getCondVisibility('Container.Content(artists)'): elif getCondVisibility('Container.Content(artists)'):
item_type = "artist" kodi_type = v.KODI_TYPE_ARTIST
elif xbmc.getCondVisibility('Container.Content(songs)'): elif getCondVisibility('Container.Content(songs)'):
item_type = "song" kodi_type = v.KODI_TYPE_SONG
elif xbmc.getCondVisibility('Container.Content(pictures)'): elif getCondVisibility('Container.Content(pictures)'):
item_type = "picture" kodi_type = v.KODI_TYPE_PHOTO
else: else:
LOG.info("item_type is unknown") LOG.info("kodi_type is unknown")
return item_type kodi_type = None
return kodi_type
@classmethod @staticmethod
def _get_item_id(cls, kodi_id, item_type): def _get_plex_id(kodi_id, kodi_type):
item_id = xbmc.getInfoLabel('ListItem.Property(plexid)') plex_id = getInfoLabel('ListItem.Property(plexid)') or None
if not item_id and kodi_id and item_type: if not plex_id and kodi_id and kodi_type:
with plexdb.Get_Plex_DB() as plexcursor: with plexdb.Get_Plex_DB() as plexcursor:
item = plexcursor.getItem_byKodiId(kodi_id, item_type) item = plexcursor.getItem_byKodiId(kodi_id, kodi_type)
try: try:
item_id = item[0] plex_id = item[0]
except TypeError: except TypeError:
LOG.error('Could not get the Plex id for context menu') LOG.info('Could not get the Plex id for context menu')
return item_id return plex_id
def _select_menu(self): def _select_menu(self):
# Display select dialog """
Display select dialog
"""
options = [] options = []
# if user uses direct paths, give option to initiate playback via PMS # if user uses direct paths, give option to initiate playback via PMS
if (window('useDirectPaths') == 'true' and if state.DIRECT_PATHS and self.kodi_type in v.KODI_VIDEOTYPES:
self.item_type in v.KODI_VIDEOTYPES):
options.append(OPTIONS['PMS_Play']) options.append(OPTIONS['PMS_Play'])
if self.kodi_type in v.KODI_VIDEOTYPES:
if self.item_type in v.KODI_VIDEOTYPES:
options.append(OPTIONS['Transcode']) options.append(OPTIONS['Transcode'])
# userdata = self.api.getUserData() # userdata = self.api.getUserData()
# if userdata['Favorite']: # if userdata['Favorite']:
# # Remove from emby favourites # # Remove from emby favourites
@ -100,11 +106,9 @@ class ContextMenu(object):
# else: # else:
# # Add to emby favourites # # Add to emby favourites
# options.append(OPTIONS['AddFav']) # options.append(OPTIONS['AddFav'])
# if self.kodi_type == "song":
# if self.item_type == "song":
# # Set custom song rating # # Set custom song rating
# options.append(OPTIONS['RateSong']) # options.append(OPTIONS['RateSong'])
# Refresh item # Refresh item
# options.append(OPTIONS['Refresh']) # options.append(OPTIONS['Refresh'])
# Delete item, only if the Plex Home main user is logged in # Delete item, only if the Plex Home main user is logged in
@ -113,103 +117,65 @@ class ContextMenu(object):
options.append(OPTIONS['Delete']) options.append(OPTIONS['Delete'])
# Addon settings # Addon settings
options.append(OPTIONS['Addon']) options.append(OPTIONS['Addon'])
context_menu = context.ContextMenu( context_menu = context.ContextMenu(
"script-emby-context.xml", "script-emby-context.xml",
xbmcaddon.Addon( Addon('plugin.video.plexkodiconnect').getAddonInfo('path'),
'plugin.video.plexkodiconnect').getAddonInfo('path'), "default",
"default", "1080i") "1080i")
context_menu.set_options(options) context_menu.set_options(options)
context_menu.doModal() context_menu.doModal()
if context_menu.is_selected(): if context_menu.is_selected():
self._selected_option = context_menu.get_selected() self._selected_option = context_menu.get_selected()
return self._selected_option return self._selected_option
def _action_menu(self): def _action_menu(self):
"""
Do whatever the user selected to do
"""
selected = self._selected_option selected = self._selected_option
if selected == OPTIONS['Transcode']: if selected == OPTIONS['Transcode']:
window('plex_forcetranscode', value='true') state.FORCE_TRANSCODE = True
self._PMS_play() self._PMS_play()
elif selected == OPTIONS['PMS_Play']: elif selected == OPTIONS['PMS_Play']:
self._PMS_play() self._PMS_play()
# elif selected == OPTIONS['Refresh']: # elif selected == OPTIONS['Refresh']:
# self.emby.refreshItem(self.item_id) # self.emby.refreshItem(self.item_id)
# elif selected == OPTIONS['AddFav']: # elif selected == OPTIONS['AddFav']:
# self.emby.updateUserRating(self.item_id, favourite=True) # self.emby.updateUserRating(self.item_id, favourite=True)
# elif selected == OPTIONS['RemoveFav']: # elif selected == OPTIONS['RemoveFav']:
# self.emby.updateUserRating(self.item_id, favourite=False) # self.emby.updateUserRating(self.item_id, favourite=False)
# elif selected == OPTIONS['RateSong']: # elif selected == OPTIONS['RateSong']:
# self._rate_song() # self._rate_song()
elif selected == OPTIONS['Addon']: elif selected == OPTIONS['Addon']:
xbmc.executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)') executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)')
elif selected == OPTIONS['Delete']: elif selected == OPTIONS['Delete']:
self._delete_item() 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): def _delete_item(self):
"""
Delete item on PMS
"""
delete = True delete = True
if settings('skipContextMenu') != "true": if settings('skipContextMenu') != "true":
if not dialog("yesno", heading="{plex}", line1=lang(33041)):
if not dialog("yesno", heading=lang(29999), line1=lang(33041)): LOG.info("User skipped deletion for: %s", self.plex_id)
LOG.info("User skipped deletion for: %s", self.item_id)
delete = False delete = False
if delete: if delete:
LOG.info("Deleting Plex item with id %s", self.item_id) LOG.info("Deleting Plex item with id %s", self.plex_id)
if delete_item_from_pms(self.item_id) is False: if delete_item_from_pms(self.plex_id) is False:
dialog("ok", heading="{plex}", line1=lang(30414)) dialog("ok", heading="{plex}", line1=lang(30414))
def _PMS_play(self): def _PMS_play(self):
""" """
For using direct paths: Initiates playback using the PMS For using direct paths: Initiates playback using the PMS
""" """
window('plex_contextplay', value='true') state.CONTEXT_MENU_PLAY = True
params = { params = {
'filename': '/library/metadata/%s' % self.item_id, 'mode': 'play',
'id': self.item_id, 'plex_id': self.plex_id,
'dbid': self.kodi_id, 'plex_type': self.plex_type
'mode': "play"
} }
from urllib import urlencode from urllib import urlencode
handle = ("plugin://plugin.video.plexkodiconnect/movies?%s" handle = ("plugin://plugin.video.plexkodiconnect/movies?%s"
% urlencode(params)) % urlencode(params))
xbmc.executebuiltin('RunPlugin(%s)' % handle) executebuiltin('RunPlugin(%s)' % handle)

View file

@ -17,7 +17,7 @@ from playutils import PlayUtils
from PKC_listitem import PKC_ListItem from PKC_listitem import PKC_ListItem
from pickler import pickle_me, Playback_Successful from pickler import pickle_me, Playback_Successful
import json_rpc as js 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 from plexbmchelper.subscribers import LOCKER
import variables as v import variables as v
import state 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. Playback setup if Kodi starts playing an item for the first time.
""" """
LOG.info('Initializing PKC playback') LOG.info('Initializing PKC playback')
contextmenu_play = window('plex_contextplay') == 'true'
window('plex_contextplay', clear=True)
xml = GetPlexMetadata(plex_id) xml = GetPlexMetadata(plex_id)
try: try:
xml[0].attrib 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 a bit to let setResolvedUrl do its thing - bit ugly
sleep(200) sleep(200)
_process_stack(playqueue, stack) _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) # New thread to release this one sooner (e.g. harddisk spinning up)
thread = Thread(target=Player().play, thread = Thread(target=Player().play,
args=(playqueue.kodi_pl, )) args=(playqueue.kodi_pl, ))
@ -176,7 +177,10 @@ def _prep_playlist_stack(xml):
stack = [] stack = []
for item in xml: for item in xml:
api = API(item) 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: with plexdb.Get_Plex_DB() as plex_db:
plex_dbitem = plex_db.getItem_byId(api.getRatingKey()) plex_dbitem = plex_db.getItem_byId(api.getRatingKey())
kodi_id = plex_dbitem[0] if plex_dbitem else None 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.offset = item['offset']
playlist_item.part = item['part'] playlist_item.part = item['part']
playlist_item.id = item['id'] playlist_item.id = item['id']
playlist_item.force_transcode = state.FORCE_TRANSCODE
playlist_item.init_done = True playlist_item.init_done = True
pos += 1 pos += 1
if fill_queue: if fill_queue:
@ -356,7 +361,8 @@ def process_indirect(key, offset, resolve=True):
pickle_me(result) pickle_me(result)
else: else:
thread = Thread(target=Player().play, thread = Thread(target=Player().play,
args={'item': tryEncode(playurl), 'listitem': listitem}) args={'item': tryEncode(playurl),
'listitem': listitem})
thread.setDaemon(True) thread.setDaemon(True)
LOG.info('Done initializing PKC playback, starting Kodi player') LOG.info('Done initializing PKC playback, starting Kodi player')
thread.start() thread.start()

View file

@ -5,7 +5,6 @@ from logging import getLogger
from xbmc import Player from xbmc import Player
from utils import window
from downloadutils import DownloadUtils as DU from downloadutils import DownloadUtils as DU
from plexbmchelper.subscribers import LOCKER from plexbmchelper.subscribers import LOCKER
import variables as v import variables as v
@ -26,10 +25,6 @@ def playback_cleanup():
# We might have saved a transient token from a user flinging media via # 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) # Companion (if we could not use the playqueue to store the token)
state.PLEX_TRANSIENT_TOKEN = None 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: for playerid in state.ACTIVE_PLAYERS:
status = state.PLAYER_STATES[playerid] status = state.PLAYER_STATES[playerid]
# Remember the last played item later # Remember the last played item later

View file

@ -197,6 +197,7 @@ class Playlist_Item(object):
playcount = None [int] how many times the item has already been played 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 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 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 init_done = False Set to True only if run through playback init
""" """
def __init__(self): def __init__(self):
@ -215,6 +216,7 @@ class Playlist_Item(object):
self.offset = None self.offset = None
# If Plex video consists of several parts; part number # If Plex video consists of several parts; part number
self.part = 0 self.part = 0
self.force_transcode = False
self.init_done = False self.init_done = False
def __repr__(self): def __repr__(self):

View file

@ -87,7 +87,8 @@ class PlayUtils():
- codec is in h265 - codec is in h265
- 10bit video codec - 10bit video codec
- HEVC 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.) (excepting trailers etc.)
- video bitrate above specified settings bitrate - video bitrate above specified settings bitrate
if the corresponding file settings are set to 'true' if the corresponding file settings are set to 'true'
@ -97,7 +98,7 @@ class PlayUtils():
return False return False
videoCodec = self.api.getVideoCodec() videoCodec = self.api.getVideoCodec()
LOG.info("videoCodec: %s" % videoCodec) LOG.info("videoCodec: %s" % videoCodec)
if window('plex_forcetranscode') == 'true': if self.item.force_transcode is True:
LOG.info('User chose to force-transcode') LOG.info('User chose to force-transcode')
return True return True
codec = videoCodec['videocodec'] codec = videoCodec['videocodec']

View file

@ -143,6 +143,10 @@ RESUMABLE = False
# Set by SpecialMonitor - did user choose to resume playback or start from the # Set by SpecialMonitor - did user choose to resume playback or start from the
# beginning? # beginning?
RESUME_PLAYBACK = False 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 # Kodi webserver details
WEBSERVER_PORT = 8080 WEBSERVER_PORT = 8080