def __init__(self, callback): self.mgr = callback self.doUtils = DownloadUtils().downloadUrl self.xbmcplayer = Player() self.playqueue = self.mgr.playqueue Monitor.__init__(self) log.info("Kodi monitor started.")
def __init__(self): """KodiPlayer initialisation""" Player.__init__(self) self.listen = False self.path = None self.av_started = False self.stream_path = None
def __init__(self): """ Initialises a custom Player object """ self.__listen = False self.__av_started = False self.__path = None self.__subtitle_paths = None Player.__init__(self)
def onControl(self, control): if control == self.pause: Player().pause() elif control == self.play: Player().pause() elif control == self.next: page = int(ADDON.getSetting('page')) if page < len(self.data) / config['list']['per_page']: ADDON.setSetting('page', str(page + 1)) self.draw_channels() self.update_pagination() self.setFocus(self.controls['buttons'][0]) elif control == self.prev: page = int(ADDON.getSetting('page')) if page > 0: ADDON.setSetting('page', str(page - 1)) self.draw_channels() self.update_pagination() self.setFocus( self.controls['buttons'][len(self.controls['buttons']) - 1]) else: ADDON.setSetting('channel', control.getLabel()) ADDON.setSetting( 'current', self.data[int(ADDON.getSetting('channel'))]['meta']) self.setProperty( 'current', self.data[int(ADDON.getSetting('channel'))]['meta']) Player().play(self.data[int( ADDON.getSetting('channel'))]['stream']) self.update_channel()
def __run(self): """ Do the work """ log.debug('Show sync info thread started') # cache local variables because it's faster total = self.total dialog = DialogProgressBG('dialoglogProgressBG') thread_stopped = self.thread_stopped dialog.create("%s %s: %s %s" % (lang(39714), self.item_type, str(total), lang(39715))) player = Player() total = 2 * total totalProgress = 0 while thread_stopped() is False and not player.isPlaying(): with LOCK: get_progress = GET_METADATA_COUNT process_progress = PROCESS_METADATA_COUNT viewName = PROCESSING_VIEW_NAME totalProgress = get_progress + process_progress try: percentage = int(float(totalProgress) / float(total) * 100.0) except ZeroDivisionError: percentage = 0 dialog.update(percentage, message="%s %s. %s %s: %s" % (get_progress, lang(39712), process_progress, lang(39713), viewName)) # Sleep for x milliseconds sleep(200) dialog.close() log.debug('Show sync info thread terminated')
def __init__(self, kodi=None): """ Initialises a custom Player object :type kodi: resources.lib.kodiwrapper.KodiWrapper """ self._kodi = kodi self.__listen = False self.__av_started = False self.__path = None self.__subtitle_path = None Player.__init__(self)
def __init__(self): Player.__init__(self) # self.sharedMem = SharedMemory('myfunkyname', True, 1) self.buttonLock = threading.Lock() self.preloadLock = threading.Lock() self.buttons = [] self.preload = {} self.id2buttons = {} self.id2preload = {} _thread.start_new_thread(self.setupListener, ()) self.providers = setupProviders()
def __init__(self, **kwargs): ''' PlayerInfo initialisation ''' self._info = kwargs['info'] self._path = None self._paused = False self._last_pos = None self._total = None self._stop = Event() from random import randint self._id = randint(1, 10001) log(2, '[PlayerInfo] %d initialized' % self._id) Player.__init__(self)
def PLAY(url, name, sub_files=[]): link = get_html(url) match = re.compile('\'(.+?.m3u8)\'').findall(link) for u in match: listitem = xbmcgui.ListItem(name) listitem.setInfo('video', {'Title': name, 'Genre': 'Humor'}) Player().play(unquote(u), listitem) while not Player().isPlaying(): sleep(10) # wait until video is being played for s in save_subs(path=addon.getAddonInfo('path'), subs_href=sub_files, url=url): Player().setSubtitles(s)
def __init__(self): LOG.info("----===## Starting PlexCompanion ##===----") # Init Plex Companion queue # Start GDM for server/client discovery self.client = plexgdm.plexgdm() self.client.clientDetails() LOG.debug("Registration string is:\n%s", self.client.getClientDetails()) # kodi player instance self.player = Player() self.httpd = False self.subscription_manager = None Thread.__init__(self)
def threaded_playback(kodi_playlist, startpos, offset): """ Seek immediately after kicking off playback is not reliable. """ player = Player() player.play(kodi_playlist, None, False, startpos) if offset and offset != '0': i = 0 while not player.isPlaying(): sleep(100) i += 1 if i > 100: LOG.error('Could not seek to %s', offset) return js.seek_to(int(offset))
def skip_to(params): """ Skip to a specific playlist position. Does not seem to be implemented yet by Plex! """ playqueue_item_id = params.get('playQueueItemID') _, plex_id = GetPlexKeyNumber(params.get('key')) LOG.debug('Skipping to playQueueItemID %s, plex_id %s', playqueue_item_id, plex_id) found = True for player in js.get_players().values(): playqueue = PQ.PLAYQUEUES[player['playerid']] for i, item in enumerate(playqueue.items): if item.id == playqueue_item_id: found = True break else: for i, item in enumerate(playqueue.items): if item.plex_id == plex_id: found = True break if found is True: Player().play(playqueue.kodi_pl, None, False, i) else: LOG.error('Item not found to skip to')
def __init__(self): self.xbmcplayer = Player() Monitor.__init__(self) for playerid in state.PLAYER_STATES: state.PLAYER_STATES[playerid] = dict(state.PLAYSTATE) state.OLD_PLAYER_STATES[playerid] = dict(state.PLAYSTATE) LOG.info("Kodi monitor started.")
def __init__(self, callback): self.mgr = callback self.doUtils = downloadutils.DownloadUtils().downloadUrl self.xbmcplayer = Player() self.playqueue = self.mgr.playqueue Monitor.__init__(self) log.info("Kodi monitor started.")
def play_channel(category_id, channel_id): """ Plays selected song :param category_id: Selected category ID :param channel_id: Selected channel ID :return: """ api = API() user = User() is_authenticated = user.authenticate() recent_tracks_url = '' channel = [ item for item in api.get_channels(int(category_id)) if item['id'] == int(channel_id) ][0] url = api.get_streaming_url(channel['streams'], user.username, user.token, user.is_authenticated()) if is_authenticated: recent_tracks_url = channel['recent_tracks']['vip'] elif 'free' in channel['recent_tracks']: recent_tracks_url = channel['recent_tracks']['free'] # is there a valid URL for channel? if url: url = quote(url, safe=':/?=@') li = ListItem(channel['title'], channel['description'], channel['image']) li.setArt({ 'thumb': '{0}/{1}'.format(config['urls']['calm_arts_host'], channel['image']), 'fanart': '{0}{1}'.format(config['urls']['calm_blurred_arts_host'], channel['image']) }) li.setInfo( 'music', { 'Title': channel['title'].replace('CALM RADIO -', '').title(), 'Artist': channel['description'] }) li.setProperty('mimetype', 'audio/mpeg') li.setProperty('IsPlayable', 'true') li.setInfo( 'music', {'Title': channel['title'].replace('CALM RADIO -', '').title()}) Player().play(item=url, listitem=li) log('Playing url: {0}'.format(url)) update_artwork(channel, recent_tracks_url) else: # members only access dialog = Dialog() ret = dialog.yesno(ADDON.getLocalizedString(32200), ADDON.getLocalizedString(32201)) if ret == 1: ADDON.openSettings()
def __init__(self, jsonClass, RequestMgr): self.serverlist = [] self.subscribers = {} self.info = {} self.lastkey = "" self.containerKey = "" self.lastratingkey = "" self.volume = 0 self.mute = '0' self.server = "" self.protocol = "http" self.port = "" self.playerprops = {} self.doUtils = downloadutils.DownloadUtils().downloadUrl self.xbmcplayer = Player() self.js = jsonClass self.RequestMgr = RequestMgr
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') xml = GetPlexMetadata(plex_id) try: xml[0].attrib except (IndexError, TypeError, AttributeError): LOG.error('Could not get a PMS xml for plex id %s', plex_id) # "Play error" dialog('notification', lang(29999), lang(30128), icon='{error}') return trailers = False api = API(xml[0]) if (plex_type == v.PLEX_TYPE_MOVIE and not api.getResume() and settings('enableCinema') == "true"): if settings('askCinema') == "true": # "Play trailers?" trailers = dialog('yesno', lang(29999), lang(33016)) trailers = True if trailers else False else: trailers = True LOG.debug('Playing trailers: %s', trailers) playqueue.clear() if plex_type != v.PLEX_TYPE_CLIP: # Post to the PMS to create a playqueue - in any case due to Companion xml = init_plex_playqueue(plex_id, xml.attrib.get('librarySectionUUID'), mediatype=plex_type, trailers=trailers) if xml is None: LOG.error('Could not get a playqueue xml for plex id %s, UUID %s', plex_id, xml.attrib.get('librarySectionUUID')) # "Play error" dialog('notification', lang(29999), lang(30128), icon='{error}') return # Should already be empty, but just in case PL.get_playlist_details_from_xml(playqueue, xml) stack = _prep_playlist_stack(xml) # 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, )) thread.setDaemon(True) LOG.info('Done initializing PKC playback, starting Kodi player') # By design, PKC will start Kodi playback using Player().play(). Kodi # caches paths like our plugin://pkc. If we use Player().play() between # 2 consecutive startups of exactly the same Kodi library item, Kodi's # cache will have been flushed for some reason. Hence the 2nd call for # plugin://pkc will be lost; Kodi will try to startup playback for an empty # path: log entry is "CGUIWindowVideoBase::OnPlayMedia <missing path>" thread.start()
def onClick(self, controlId): # pylint: disable=invalid-name if controlId == 3012: # Watch now self.set_watch_now(True) self.close() elif controlId == 3013: # Close / Stop self.set_cancel(True) if get_setting_bool('stopAfterClose'): Player().stop() self.close()
def play_video(videoId): video = repository.get_video(videoId) streams = repository.get_video_streams(video.streamId) item = ListItem(path=streams[0].url) item.setProperty(u'IsPlayable', u'true') item.setInfo( type='Video', infoLabels={'Title': video.title, 'Plot': video.description} ) analytics.trackVideoPlayed(video.slug) playlist = PlayList(PLAYLIST_VIDEO) playlist.clear() playlist.add(streams[0].url, item) player = Player() player.play(playlist)
def process_plex_node(self, url, viewOffset, directplay=False, node=True): """ Called for Plex directories or redirect for playback (e.g. trailers, clips, watchlater) """ log.info('process_plex_node called with url: %s, viewOffset: %s' % (url, viewOffset)) # Plex redirect, e.g. watch later. Need to get actual URLs if url.startswith('http') or url.startswith('{server}'): xml = DownloadUtils().downloadUrl(url) else: xml = DownloadUtils().downloadUrl('{server}%s' % url) try: xml[0].attrib except: log.error('Could not download PMS metadata') return if viewOffset != '0': try: viewOffset = int(v.PLEX_TO_KODI_TIMEFACTOR * float(viewOffset)) except: pass else: window('plex_customplaylist.seektime', value=str(viewOffset)) log.info('Set resume point to %s' % str(viewOffset)) api = API(xml[0]) typus = v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()] if node is True: plex_id = None kodi_id = 'plexnode' else: plex_id = api.getRatingKey() kodi_id = None with plexdb.Get_Plex_DB() as plex_db: plexdb_item = plex_db.getItem_byId(plex_id) try: kodi_id = plexdb_item[0] except TypeError: log.info('Couldnt find item %s in Kodi db' % api.getRatingKey()) playqueue = self.playqueue.get_playqueue_from_type(typus) with lock: result = PlaybackUtils(xml, playqueue).play( plex_id, kodi_id=kodi_id, plex_lib_UUID=xml.attrib.get('librarySectionUUID')) if directplay: if result.listitem: listitem = convert_PKC_to_listitem(result.listitem) Player().play(listitem.getfilename(), listitem) return Playback_Successful() else: return result
def __init__(self): self.subscribers = {} self.info = {} self.lastkey = "" self.containerKey = "" self.lastratingkey = "" self.volume = 0 self.mute = '0' self.server = "" self.protocol = "http" self.port = "" self.playerprops = {} self.download = downloadutils.DownloadUtils() self.xbmcplayer = Player()
def skipTo(params): # Does not seem to be implemented yet playQueueItemID = params.get('playQueueItemID', 'not available') library, plex_id = GetPlexKeyNumber(params.get('key')) log.debug('Skipping to playQueueItemID %s, plex_id %s' % (playQueueItemID, plex_id)) found = True playqueues = Playqueue() for (player, ID) in getPlayers().iteritems(): playqueue = playqueues.get_playqueue_from_type(player) for i, item in enumerate(playqueue.items): if item.ID == playQueueItemID or item.plex_id == plex_id: break else: log.debug('Item not found to skip to') found = False if found: Player().play(playqueue.kodi_pl, None, False, i)
def init_playqueue_from_plex_children(self, plex_id): """ Init a new playqueue e.g. from an album. Alexa does this """ xml = GetAllPlexChildren(plex_id) try: xml[0].attrib except (TypeError, IndexError, AttributeError): log.error('Could not download the PMS xml for %s' % plex_id) return playqueue = self.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[xml[0].attrib['type']]) playqueue.clear() for i, child in enumerate(xml): api = API(child) PL.add_item_to_playlist(playqueue, i, plex_id=api.getRatingKey()) log.debug('Firing up Kodi player') Player().play(playqueue.kodi_pl, None, False, 0)
def update_playqueue_from_PMS(self, playqueue, playqueue_id=None, repeat=None, offset=None): """ Completely updates the Kodi playqueue with the new Plex playqueue. Pass in playqueue_id if we need to fetch a new playqueue repeat = 0, 1, 2 offset = time offset in Plextime (milliseconds) """ log.info('New playqueue %s received from Plex companion with offset ' '%s, repeat %s' % (playqueue_id, offset, repeat)) with lock: xml = PL.get_PMS_playlist(playqueue, playqueue_id) playqueue.clear() try: PL.get_playlist_details_from_xml(playqueue, xml) except KeyError: log.error('Could not get playqueue ID %s' % playqueue_id) return PlaybackUtils(xml, playqueue).play_all() playqueue.repeat = 0 if not repeat else int(repeat) window('plex_customplaylist', value="true") if offset not in (None, "0"): window('plex_customplaylist.seektime', str(ConvertPlexToKodiTime(offset))) for startpos, item in enumerate(playqueue.items): if item.ID == playqueue.selectedItemID: break else: startpos = 0 # Start playback. Player does not return in time log.debug('Playqueues after Plex Companion update are now: %s' % self.playqueues) thread = Thread(target=Player().play, args=(playqueue.kodi_pl, None, False, startpos)) thread.setDaemon(True) thread.start()
def update_progress_control(self, remaining=None, runtime=None): self.current_progress_percent = self.current_progress_percent - self.progress_step_size try: self.progress_control = self.getControl(3014) except RuntimeError: # Occurs when skin does not include progress control pass else: self.progress_control.setPercent(self.current_progress_percent) # pylint: disable=no-member,useless-suppression if remaining: self.setProperty('remaining', from_unicode('%02d' % remaining)) if runtime: self.setProperty( 'endtime', from_unicode( localize_time(datetime.now() + timedelta(seconds=runtime)))) if self.current_progress_percent <= 0: if get_setting_int('autoPlayMode') == 0: self.set_watch_now(True) else: Player().stop()
def init_playqueue_from_plex_children(plex_id, transient_token=None): """ Init a new playqueue e.g. from an album. Alexa does this Returns the Playlist_Object """ xml = GetAllPlexChildren(plex_id) try: xml[0].attrib except (TypeError, IndexError, AttributeError): LOG.error('Could not download the PMS xml for %s', plex_id) return playqueue = get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[xml[0].attrib['type']]) playqueue.clear() for i, child in enumerate(xml): api = API(child) PL.add_item_to_playlist(playqueue, i, plex_id=api.plex_id()) playqueue.plex_transient_token = transient_token LOG.debug('Firing up Kodi player') Player().play(playqueue.kodi_pl, None, False, 0) return playqueue
def Plex_Node(url, viewOffset, plex_type, playdirectly=False): """ Called only for a SINGLE element for Plex.tv watch later Always to return with a "setResolvedUrl" """ log.info('Plex_Node called with url: %s, viewOffset: %s' % (url, viewOffset)) # Plex redirect, e.g. watch later. Need to get actual URLs xml = downloadutils.DownloadUtils().downloadUrl(url) try: xml[0].attrib except: log.error('Could not download PMS metadata') return if viewOffset != '0': try: viewOffset = int(v.PLEX_TO_KODI_TIMEFACTOR * float(viewOffset)) except: pass else: window('plex_customplaylist.seektime', value=str(viewOffset)) log.info('Set resume point to %s' % str(viewOffset)) typus = v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[plex_type] playqueue = Playqueue().get_playqueue_from_type(typus) result = pbutils.PlaybackUtils(xml, playqueue).play( None, kodi_id='plexnode', plex_lib_UUID=xml.attrib.get('librarySectionUUID')) if result.listitem: listitem = convert_PKC_to_listitem(result.listitem) else: return if playdirectly: Player().play(listitem.getfilename(), listitem) else: xbmcplugin.setResolvedUrl(HANDLE, True, listitem)
def __init__(self): self.api = Api() self.state = State() self.monitor = Monitor() Player.__init__(self)
addon_handle = int(sys.argv[1]) xbmcplugin.setContent(addon_handle, 'movies') dialog = xbmcgui.Dialog() input_data = dialog.input('Ingresar nombre, temporada y capitulo', type=xbmcgui.INPUT_ALPHANUM) input_data = input_data.split('/') name, season, episode = input_data args = {'name': name, 'season': season, 'episode': episode, 'serve': True } t = Thread(target=watch, kwargs=args) t.start() dialog = xbmcgui.Dialog() dialog.notification('touchandgo', 'touchandgo is starting waith for it.', xbmcgui.NOTIFICATION_INFO, 60000) video_url = "http://localhost:8888/" player = Player(xbmc.PLAYER_CORE_AUTO) player.play(video_url)
class KodiMonitor(Monitor): def __init__(self, callback): self.mgr = callback self.doUtils = downloadutils.DownloadUtils().downloadUrl self.xbmcplayer = Player() self.playqueue = self.mgr.playqueue Monitor.__init__(self) log.info("Kodi monitor started.") def onScanStarted(self, library): log.debug("Kodi library scan %s running." % library) if library == "video": window('plex_kodiScan', value="true") def onScanFinished(self, library): log.debug("Kodi library scan %s finished." % library) if library == "video": window('plex_kodiScan', clear=True) def onSettingsChanged(self): """ Monitor the PKC settings for changes made by the user """ # settings: window-variable items = { 'logLevel': 'plex_logLevel', 'enableContext': 'plex_context', 'plex_restricteduser': '******', 'dbSyncIndicator': 'dbSyncIndicator', 'remapSMB': 'remapSMB', 'replaceSMB': 'replaceSMB', 'force_transcode_pix': 'plex_force_transcode_pix', 'fetch_pms_item_number': 'fetch_pms_item_number' } # Path replacement for typus in REMAP_TYPE_FROM_PLEXTYPE.values(): for arg in ('Org', 'New'): key = 'remapSMB%s%s' % (typus, arg) items[key] = key # Reset the window variables from the settings variables for settings_value, window_value in items.iteritems(): if window(window_value) != settings(settings_value): log.debug('PKC settings changed: %s is now %s' % (settings_value, settings(settings_value))) window(window_value, value=settings(settings_value)) if settings_value == 'fetch_pms_item_number': log.info('Requesting playlist/nodes refresh') window('plex_runLibScan', value="views") @CatchExceptions(warnuser=False) def onNotification(self, sender, method, data): if data: data = loads(data, 'utf-8') log.debug("Method: %s Data: %s" % (method, data)) if method == "Player.OnPlay": self.PlayBackStart(data) elif method == "Player.OnStop": # Should refresh our video nodes, e.g. on deck # xbmc.executebuiltin('ReloadSkin()') pass elif method == "VideoLibrary.OnUpdate": # Manually marking as watched/unwatched playcount = data.get('playcount') item = data.get('item') try: kodiid = item['id'] item_type = item['type'] except (KeyError, TypeError): log.info("Item is invalid for playstate update.") else: # Send notification to the server. with plexdb.Get_Plex_DB() as plexcur: plex_dbitem = plexcur.getItem_byKodiId(kodiid, item_type) try: itemid = plex_dbitem[0] except TypeError: log.error("Could not find itemid in plex database for a " "video library update") else: # Stop from manually marking as watched unwatched, with actual playback. if window('plex_skipWatched%s' % itemid) == "true": # property is set in player.py window('plex_skipWatched%s' % itemid, clear=True) else: # notify the server if playcount != 0: scrobble(itemid, 'watched') else: scrobble(itemid, 'unwatched') elif method == "VideoLibrary.OnRemove": pass elif method == "System.OnSleep": # Connection is going to sleep log.info("Marking the server as offline. SystemOnSleep activated.") window('plex_online', value="sleep") elif method == "System.OnWake": # Allow network to wake up sleep(10000) window('plex_onWake', value="true") window('plex_online', value="false") elif method == "GUI.OnScreensaverDeactivated": if settings('dbSyncScreensaver') == "true": sleep(5000) window('plex_runLibScan', value="full") def PlayBackStart(self, data): """ Called whenever a playback is started """ # Get currently playing file - can take a while. Will be utf-8! try: currentFile = self.xbmcplayer.getPlayingFile() except: currentFile = None count = 0 while currentFile is None: sleep(100) try: currentFile = self.xbmcplayer.getPlayingFile() except: pass if count == 50: log.info("No current File, cancel OnPlayBackStart...") return else: count += 1 # Just to be on the safe side currentFile = tryDecode(currentFile) log.debug("Currently playing file is: %s" % currentFile) # Get the type of media we're playing try: typus = data['item']['type'] except (TypeError, KeyError): log.info("Item is invalid for PMS playstate update.") return log.debug("Playing itemtype is (or appears to be): %s" % typus) # Try to get a Kodi ID # If PKC was used - native paths, not direct paths plexid = window('plex_%s.itemid' % tryEncode(currentFile)) # Get rid of the '' if the window property was not set plexid = None if not plexid else plexid kodiid = None if plexid is None: log.debug('Did not get Plex id from window properties') try: kodiid = data['item']['id'] except (TypeError, KeyError): log.debug('Did not get a Kodi id from Kodi, darn') # For direct paths, if we're not streaming something # When using Widgets, Kodi doesn't tell us shit so we need this hack if (kodiid is None and plexid is None and typus != 'song' and not currentFile.startswith('http')): (kodiid, typus) = get_kodiid_from_filename(currentFile) if kodiid is None: return if plexid is None: # Get Plex' item id with plexdb.Get_Plex_DB() as plexcursor: plex_dbitem = plexcursor.getItem_byKodiId(kodiid, typus) try: plexid = plex_dbitem[0] except TypeError: log.info("No Plex id returned for kodiid %s. Aborting playback" " report" % kodiid) return log.debug("Found Plex id %s for Kodi id %s for type %s" % (plexid, kodiid, typus)) # Set some stuff if Kodi initiated playback if ((settings('useDirectPaths') == "1" and not typus == "song") or (typus == "song" and settings('enableMusic') == "true")): if self.StartDirectPath(plexid, typus, tryEncode(currentFile)) is False: log.error('Could not initiate monitoring; aborting') return # Save currentFile for cleanup later and to be able to access refs window('plex_lastPlayedFiled', value=currentFile) window('plex_currently_playing_itemid', value=plexid) window("plex_%s.itemid" % tryEncode(currentFile), value=plexid) log.info('Finish playback startup') def StartDirectPath(self, plexid, type, currentFile): """ Set some additional stuff if playback was initiated by Kodi, not PKC """ xml = self.doUtils('{server}/library/metadata/%s' % plexid) try: xml[0].attrib except: log.error('Did not receive a valid XML for plexid %s.' % plexid) return False # Setup stuff, because playback was started by Kodi, not PKC api = API(xml[0]) listitem = api.CreateListItemFromPlexItem() api.set_playback_win_props(currentFile, listitem) if type == "song" and settings('streamMusic') == "true": window('plex_%s.playmethod' % currentFile, value="DirectStream") else: window('plex_%s.playmethod' % currentFile, value="DirectPlay") log.debug('Window properties set for direct paths!')
class SubscriptionManager: def __init__(self, jsonClass, RequestMgr): self.serverlist = [] self.subscribers = {} self.info = {} self.lastkey = "" self.containerKey = "" self.lastratingkey = "" self.volume = 0 self.mute = '0' self.server = "" self.protocol = "http" self.port = "" self.playerprops = {} self.doUtils = downloadutils.DownloadUtils().downloadUrl self.xbmcplayer = Player() self.js = jsonClass self.RequestMgr = RequestMgr def getServerByHost(self, host): if len(self.serverlist) == 1: return self.serverlist[0] for server in self.serverlist: if (server.get('serverName') in host or server.get('server') in host): return server return {} def getVolume(self): self.volume, self.mute = self.js.getVolume() def msg(self, players): msg = getXMLHeader() msg += '<MediaContainer commandID="INSERTCOMMANDID"' if players: self.getVolume() maintype = plex_audio() for p in players.values(): if p.get('type') == xbmc_video(): maintype = plex_video() elif p.get('type') == xbmc_photo(): maintype = plex_photo() self.mainlocation = "fullScreen" + maintype[0:1].upper() + maintype[1:].lower() else: self.mainlocation = "navigation" msg += ' location="%s">' % self.mainlocation msg += self.getTimelineXML(self.js.getAudioPlayerId(players), plex_audio()) msg += self.getTimelineXML(self.js.getPhotoPlayerId(players), plex_photo()) msg += self.getTimelineXML(self.js.getVideoPlayerId(players), plex_video()) msg += "\r\n</MediaContainer>" return msg def getTimelineXML(self, playerid, ptype): if playerid is not None: info = self.getPlayerProperties(playerid) # save this info off so the server update can use it too self.playerprops[playerid] = info; state = info['state'] time = info['time'] else: state = "stopped" time = 0 ret = "\r\n"+' <Timeline state="%s" time="%s" type="%s"' % (state, time, ptype) if playerid is None: ret += ' seekRange="0-0"' ret += ' />' return ret # pbmc_server = str(WINDOW.getProperty('plexbmc.nowplaying.server')) # userId = str(WINDOW.getProperty('currUserId')) pbmc_server = window('pms_server') if pbmc_server: (self.protocol, self.server, self.port) = \ pbmc_server.split(':') self.server = self.server.replace('/', '') keyid = None count = 0 while not keyid: if count > 300: break keyid = window('Plex_currently_playing_itemid') xbmc.sleep(100) count += 1 if keyid: self.lastkey = "/library/metadata/%s"%keyid self.lastratingkey = keyid ret += ' location="%s"' % (self.mainlocation) ret += ' key="%s"' % (self.lastkey) ret += ' ratingKey="%s"' % (self.lastratingkey) serv = self.getServerByHost(self.server) if info.get('playQueueID'): self.containerKey = "/playQueues/%s" % info.get('playQueueID') ret += ' playQueueID="%s"' % info.get('playQueueID') ret += ' playQueueVersion="%s"' % info.get('playQueueVersion') ret += ' playQueueItemID="%s"' % (info.get('playQueueItemID')) ret += ' containerKey="%s"' % self.containerKey elif keyid: self.containerKey = self.lastkey ret += ' containerKey="%s"' % (self.containerKey) ret += ' duration="%s"' % info['duration'] ret += ' seekRange="0-%s"' % info['duration'] ret += ' controllable="%s"' % self.controllable() ret += ' machineIdentifier="%s"' % serv.get('uuid', "") ret += ' protocol="%s"' % serv.get('protocol', "http") ret += ' address="%s"' % serv.get('server', self.server) ret += ' port="%s"' % serv.get('port', self.port) ret += ' guid="%s"' % info['guid'] ret += ' volume="%s"' % info['volume'] ret += ' shuffle="%s"' % info['shuffle'] ret += ' mute="%s"' % self.mute ret += ' repeat="%s"' % info['repeat'] # Might need an update in the future ret += ' subtitleStreamID="-1"' ret += ' audioStreamID="-1"' ret += ' />' return ret def updateCommandID(self, uuid, commandID): if commandID and self.subscribers.get(uuid, False): self.subscribers[uuid].commandID = int(commandID) def notify(self, event = False): self.cleanup() # Don't tell anyone if we don't know a Plex ID and are still playing # (e.g. no stop called). Used for e.g. PVR/TV without PKC usage if not window('Plex_currently_playing_itemid'): return True players = self.js.getPlayers() # fetch the message, subscribers or not, since the server # will need the info anyway msg = self.msg(players) if self.subscribers: with threading.RLock(): for sub in self.subscribers.values(): sub.send_update(msg, len(players)==0) self.notifyServer(players) return True def notifyServer(self, players): if not players: return True for p in players.values(): info = self.playerprops[p.get('playerid')] params = {'state': 'stopped'} params['containerKey'] = (self.containerKey or "/library/metadata/900000") if info.get('playQueueID'): params['containerKey'] = '/playQueues/' + info['playQueueID'] params['playQueueVersion'] = info['playQueueVersion'] params['playQueueItemID'] = info['playQueueItemID'] params['key'] = (self.lastkey or "/library/metadata/900000") params['ratingKey'] = (self.lastratingkey or "900000") params['state'] = info['state'] params['time'] = info['time'] params['duration'] = info['duration'] serv = self.getServerByHost(self.server) url = serv.get('protocol', 'http') + '://' \ + serv.get('server', 'localhost') + ':' \ + serv.get('port', '32400') + "/:/timeline" self.doUtils(url, type="GET", parameters=params) # requests.getwithparams(serv.get('server', 'localhost'), serv.get('port', 32400), "/:/timeline", params, getPlexHeaders(), serv.get('protocol', 'http')) self.logMsg("sent server notification with state = %s" % params['state'], 2) def controllable(self): return "volume,shuffle,repeat,audioStream,videoStream,subtitleStream,skipPrevious,skipNext,seekTo,stepBack,stepForward,stop,playPause" def addSubscriber(self, protocol, host, port, uuid, commandID): sub = Subscriber(protocol, host, port, uuid, commandID, self, self.RequestMgr) with threading.RLock(): self.subscribers[sub.uuid] = sub return sub def removeSubscriber(self, uuid): with threading.RLock(): for sub in self.subscribers.values(): if sub.uuid == uuid or sub.host == uuid: sub.cleanup() del self.subscribers[sub.uuid] def cleanup(self): with threading.RLock(): for sub in self.subscribers.values(): if sub.age > 30: sub.cleanup() del self.subscribers[sub.uuid] def getPlayerProperties(self, playerid): info = {} try: # get info from the player props = self.js.jsonrpc("Player.GetProperties", {"playerid": playerid, "properties": ["time", "totaltime", "speed", "shuffled", "repeat"]}) self.logMsg(self.js.jsonrpc("Player.GetItem", {"playerid": playerid, "properties": ["file", "showlink", "episode", "season"]}), 2) info['time'] = timeToMillis(props['time']) info['duration'] = timeToMillis(props['totaltime']) info['state'] = ("paused", "playing")[int(props['speed'])] info['shuffle'] = ("0","1")[props.get('shuffled', False)] info['repeat'] = pf.getPlexRepeat(props.get('repeat')) # New PMS playQueue attributes cf = self.xbmcplayer.getPlayingFile() info['playQueueID'] = window('playQueueID') info['playQueueVersion'] = window('playQueueVersion') info['playQueueItemID'] = window('plex_%s.playQueueItemID' % cf) info['guid'] = window('plex_%s.guid' % cf) except: info['time'] = 0 info['duration'] = 0 info['state'] = "stopped" info['shuffle'] = False # get the volume from the application info['volume'] = self.volume info['mute'] = self.mute return info
def __new__(cls, lights=[]): obj = Player.__new__(cls) obj.lights = dict((light, None) for light in lights) obj.lights_lock = RLock() return obj
def __init__(self): Player.__init__(self) self.ended = False self.started = False
def play(self, plex_id, kodi_id=None, plex_lib_UUID=None): """ plex_lib_UUID: xml attribute 'librarySectionUUID', needed for posting to the PMS """ log.info("Playbackutils called") item = self.xml[0] api = API(item) playqueue = self.playqueue xml = None result = Playback_Successful() listitem = ListItem() playutils = putils.PlayUtils(item) playurl = playutils.getPlayUrl() if not playurl: log.error('No playurl found, aborting') return if kodi_id in (None, 'plextrailer', 'plexnode'): # Item is not in Kodi database, is a trailer/clip or plex redirect # e.g. plex.tv watch later api.CreateListItemFromPlexItem(listitem) api.set_listitem_artwork(listitem) if kodi_id == 'plexnode': # Need to get yet another xml to get final url window('plex_%s.playmethod' % playurl, clear=True) xml = downloadutils.DownloadUtils().downloadUrl( '{server}%s' % item[0][0].attrib.get('key')) try: xml[0].attrib except (TypeError, AttributeError): log.error('Could not download %s' % item[0][0].attrib.get('key')) return playurl = tryEncode(xml[0].attrib.get('key')) window('plex_%s.playmethod' % playurl, value='DirectStream') playmethod = window('plex_%s.playmethod' % playurl) if playmethod == "Transcode": playutils.audioSubsPref(listitem, tryDecode(playurl)) listitem.setPath(playurl) api.set_playback_win_props(playurl, listitem) result.listitem = listitem return result kodi_type = v.KODITYPE_FROM_PLEXTYPE[api.getType()] kodi_id = int(kodi_id) # ORGANIZE CURRENT PLAYLIST ################ contextmenu_play = window('plex_contextplay') == 'true' window('plex_contextplay', clear=True) homeScreen = getCondVisibility('Window.IsActive(home)') sizePlaylist = len(playqueue.items) if contextmenu_play: # Need to start with the items we're inserting here startPos = sizePlaylist else: # Can return -1 startPos = max(playqueue.kodi_pl.getposition(), 0) self.currentPosition = startPos propertiesPlayback = window('plex_playbackProps') == "true" introsPlaylist = False dummyPlaylist = False log.info("Playing from contextmenu: %s" % contextmenu_play) log.info("Playlist start position: %s" % startPos) log.info("Playlist plugin position: %s" % self.currentPosition) log.info("Playlist size: %s" % sizePlaylist) # RESUME POINT ################ seektime, runtime = api.getRuntime() if window('plex_customplaylist.seektime'): # Already got seektime, e.g. from playqueue & Plex companion seektime = int(window('plex_customplaylist.seektime')) # We need to ensure we add the intro and additional parts only once. # Otherwise we get a loop. if not propertiesPlayback: window('plex_playbackProps', value="true") log.info("Setting up properties in playlist.") # Where will the player need to start? # Do we need to get trailers? trailers = False if (api.getType() == v.PLEX_TYPE_MOVIE and not seektime and sizePlaylist < 2 and settings('enableCinema') == "true"): if settings('askCinema') == "true": trailers = xbmcgui.Dialog().yesno(lang(29999), "Play trailers?") else: trailers = True # Post to the PMS. REUSE THE PLAYQUEUE! xml = init_plex_playqueue(plex_id, plex_lib_UUID, mediatype=api.getType(), trailers=trailers) try: get_playlist_details_from_xml(playqueue, xml=xml) except KeyError: return if (not homeScreen and not seektime and sizePlaylist < 2 and window('plex_customplaylist') != "true" and not contextmenu_play): # Need to add a dummy file because the first item will fail log.debug("Adding dummy file to playlist.") dummyPlaylist = True add_listitem_to_Kodi_playlist(playqueue, startPos, xbmcgui.ListItem(), playurl, xml[0]) # Remove the original item from playlist remove_from_Kodi_playlist(playqueue, startPos + 1) # Readd the original item to playlist - via jsonrpc so we have # full metadata add_item_to_kodi_playlist(playqueue, self.currentPosition + 1, kodi_id=kodi_id, kodi_type=kodi_type, file=playurl) self.currentPosition += 1 # -- ADD TRAILERS ################ if trailers: for i, item in enumerate(xml): if i == len(xml) - 1: # Don't add the main movie itself break self.add_trailer(item) introsPlaylist = True # -- ADD MAIN ITEM ONLY FOR HOMESCREEN ############## if homeScreen and not seektime and not sizePlaylist: # Extend our current playlist with the actual item to play # only if there's no playlist first log.info("Adding main item to playlist.") add_item_to_kodi_playlist(playqueue, self.currentPosition, kodi_id, kodi_type) elif contextmenu_play: if state.DIRECT_PATHS: # Cannot add via JSON with full metadata because then we # Would be using the direct path log.debug("Adding contextmenu item for direct paths") if window('plex_%s.playmethod' % playurl) == "Transcode": playutils.audioSubsPref(listitem, tryDecode(playurl)) api.CreateListItemFromPlexItem(listitem) api.set_playback_win_props(playurl, listitem) api.set_listitem_artwork(listitem) add_listitem_to_Kodi_playlist( playqueue, self.currentPosition + 1, convert_PKC_to_listitem(listitem), file=playurl, kodi_item={ 'id': kodi_id, 'type': kodi_type }) else: # Full metadata$ add_item_to_kodi_playlist(playqueue, self.currentPosition + 1, kodi_id, kodi_type) self.currentPosition += 1 if seektime: window('plex_customplaylist.seektime', value=str(seektime)) # Ensure that additional parts are played after the main item self.currentPosition += 1 # -- CHECK FOR ADDITIONAL PARTS ################ if len(item[0]) > 1: self.add_part(item, api, kodi_id, kodi_type) if dummyPlaylist: # Added a dummy file to the playlist, # because the first item is going to fail automatically. log.info("Processed as a playlist. First item is skipped.") # Delete the item that's gonna fail! del playqueue.items[startPos] # Don't attach listitem return result # We just skipped adding properties. Reset flag for next time. elif propertiesPlayback: log.debug("Resetting properties playback flag.") window('plex_playbackProps', clear=True) # SETUP MAIN ITEM ########## # For transcoding only, ask for audio/subs pref if (window('plex_%s.playmethod' % playurl) == "Transcode" and not contextmenu_play): playutils.audioSubsPref(listitem, tryDecode(playurl)) listitem.setPath(playurl) api.set_playback_win_props(playurl, listitem) api.set_listitem_artwork(listitem) # PLAYBACK ################ if (homeScreen and seektime and window('plex_customplaylist') != "true" and not contextmenu_play): log.info("Play as a widget item") api.CreateListItemFromPlexItem(listitem) result.listitem = listitem return result elif ((introsPlaylist and window('plex_customplaylist') == "true") or (homeScreen and not sizePlaylist) or contextmenu_play): # Playlist was created just now, play it. # Contextmenu plays always need this log.info("Play playlist from starting position %s" % startPos) # Need a separate thread because Player won't return in time thread = Thread(target=Player().play, args=(playqueue.kodi_pl, None, False, startPos)) thread.setDaemon(True) thread.start() # Don't attach listitem return result else: log.info("Play as a regular item") result.listitem = listitem return result
class PlexCompanion(Thread): """ Plex Companion monitoring class. Invoke only once """ def __init__(self): LOG.info("----===## Starting PlexCompanion ##===----") # Init Plex Companion queue # Start GDM for server/client discovery self.client = plexgdm.plexgdm() self.client.clientDetails() LOG.debug("Registration string is:\n%s", self.client.getClientDetails()) # kodi player instance self.player = Player() self.httpd = False self.subscription_manager = None Thread.__init__(self) def _process_alexa(self, data): xml = PF.GetPlexMetadata(data['key']) try: xml[0].attrib except (AttributeError, IndexError, TypeError): LOG.error('Could not download Plex metadata for: %s', data) return api = API(xml[0]) if api.plex_type() == v.PLEX_TYPE_ALBUM: LOG.debug('Plex music album detected') PQ.init_playqueue_from_plex_children( api.plex_id(), transient_token=data.get('token')) elif data['containerKey'].startswith('/playQueues/'): _, container_key, _ = PF.ParseContainerKey(data['containerKey']) xml = PF.DownloadChunks('{server}/playQueues/%s?' % container_key) if xml is None: # "Play error" utils.dialog('notification', utils.lang(29999), utils.lang(30128), icon='{error}') return playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.plex_type()]) playqueue.clear() PL.get_playlist_details_from_xml(playqueue, xml) playqueue.plex_transient_token = data.get('token') if data.get('offset') != '0': offset = float(data['offset']) / 1000.0 else: offset = None playback.play_xml(playqueue, xml, offset) else: state.PLEX_TRANSIENT_TOKEN = data.get('token') if data.get('offset') != '0': state.RESUMABLE = True state.RESUME_PLAYBACK = True playback.playback_triage(api.plex_id(), api.plex_type(), resolve=False) @staticmethod def _process_node(data): """ E.g. watch later initiated by Companion. Basically navigating Plex """ state.PLEX_TRANSIENT_TOKEN = data.get('key') params = { 'mode': 'plex_node', 'key': '{server}%s' % data.get('key'), 'offset': data.get('offset') } executebuiltin('RunPlugin(plugin://%s?%s)' % (v.ADDON_ID, urlencode(params))) def _process_playlist(self, data): # Get the playqueue ID _, container_key, query = PF.ParseContainerKey(data['containerKey']) try: playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']]) except KeyError: # E.g. Plex web does not supply the media type # Still need to figure out the type (video vs. music vs. pix) xml = PF.GetPlexMetadata(data['key']) try: xml[0].attrib except (AttributeError, IndexError, TypeError): LOG.error('Could not download Plex metadata') return api = API(xml[0]) playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.plex_type()]) update_playqueue_from_PMS(playqueue, playqueue_id=container_key, repeat=query.get('repeat'), offset=data.get('offset'), transient_token=data.get('token')) def _process_streams(self, data): """ Plex Companion client adjusted audio or subtitle stream """ playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']]) pos = js.get_position(playqueue.playlistid) if 'audioStreamID' in data: index = playqueue.items[pos].kodi_stream_index( data['audioStreamID'], 'audio') self.player.setAudioStream(index) elif 'subtitleStreamID' in data: if data['subtitleStreamID'] == '0': self.player.showSubtitles(False) else: index = playqueue.items[pos].kodi_stream_index( data['subtitleStreamID'], 'subtitle') self.player.setSubtitleStream(index) else: LOG.error('Unknown setStreams command: %s', data) def _process_refresh(self, data): """ example data: {'playQueueID': '8475', 'commandID': '11'} """ xml = PL.get_pms_playqueue(data['playQueueID']) if xml is None: return if len(xml) == 0: LOG.debug('Empty playqueue received - clearing playqueue') plex_type = PL.get_plextype_from_xml(xml) if plex_type is None: return playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[plex_type]) playqueue.clear() return playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[xml[0].attrib['type']]) update_playqueue_from_PMS(playqueue, data['playQueueID']) def _process_tasks(self, task): """ Processes tasks picked up e.g. by Companion listener, e.g. {'action': 'playlist', 'data': {'address': 'xyz.plex.direct', 'commandID': '7', 'containerKey': '/playQueues/6669?own=1&repeat=0&window=200', 'key': '/library/metadata/220493', 'machineIdentifier': 'xyz', 'offset': '0', 'port': '32400', 'protocol': 'https', 'token': 'transient-cd2527d1-0484-48e0-a5f7-f5caa7d591bd', 'type': 'video'}} """ LOG.debug('Processing: %s', task) data = task['data'] if task['action'] == 'alexa': with state.LOCK_PLAYQUEUES: self._process_alexa(data) elif (task['action'] == 'playlist' and data.get('address') == 'node.plexapp.com'): self._process_node(data) elif task['action'] == 'playlist': with state.LOCK_PLAYQUEUES: self._process_playlist(data) elif task['action'] == 'refreshPlayQueue': with state.LOCK_PLAYQUEUES: self._process_refresh(data) elif task['action'] == 'setStreams': try: self._process_streams(data) except KeyError: pass def run(self): """ Ensure that sockets will be closed no matter what """ try: self._run() finally: try: self.httpd.socket.shutdown(SHUT_RDWR) except AttributeError: pass finally: try: self.httpd.socket.close() except AttributeError: pass LOG.info("----===## Plex Companion stopped ##===----") def _run(self): httpd = self.httpd # Cache for quicker while loops client = self.client stopped = self.stopped suspended = self.suspended # Start up instances request_mgr = httppersist.RequestMgr() subscription_manager = subscribers.SubscriptionMgr( request_mgr, self.player) self.subscription_manager = subscription_manager if utils.settings('plexCompanion') == 'true': # Start up httpd start_count = 0 while True: try: httpd = listener.ThreadedHTTPServer( client, subscription_manager, ('', v.COMPANION_PORT), listener.MyHandler) httpd.timeout = 0.95 break except: LOG.error("Unable to start PlexCompanion. Traceback:") import traceback LOG.error(traceback.print_exc()) sleep(3000) if start_count == 3: LOG.error("Error: Unable to start web helper.") httpd = False break start_count += 1 else: LOG.info('User deactivated Plex Companion') client.start_all() message_count = 0 if httpd: thread = Thread(target=httpd.handle_request) while not stopped(): # If we are not authorized, sleep # Otherwise, we trigger a download which leads to a # re-authorizations while suspended(): if stopped(): break sleep(1000) try: message_count += 1 if httpd: if not thread.isAlive(): # Use threads cause the method will stall thread = Thread(target=httpd.handle_request) thread.start() if message_count == 3000: message_count = 0 if client.check_client_registration(): LOG.debug('Client is still registered') else: LOG.debug( 'Client is no longer registered. Plex ' 'Companion still running on port %s', v.COMPANION_PORT) client.register_as_client() # Get and set servers if message_count % 30 == 0: subscription_manager.serverlist = client.getServerList() subscription_manager.notify() if not httpd: message_count = 0 except: LOG.warn("Error in loop, continuing anyway. Traceback:") import traceback LOG.warn(traceback.format_exc()) # See if there's anything we need to process try: task = state.COMPANION_QUEUE.get(block=False) except Empty: pass else: # Got instructions, process them self._process_tasks(task) state.COMPANION_QUEUE.task_done() # Don't sleep continue sleep(50) subscription_manager.signal_stop() client.stop_all()
# authenticate with leafs TV ltv = authenticate() if ltv == None: return # get the archived game URL try: url = ltv.getArchivedGame(game_id) except LeafsTVError, ltvErr: dialog = xbmcgui.Dialog() dialog.ok(__language__(30008),__language__(30010)) return # play the archived game player = Player(xbmc.PLAYER_CORE_AUTO) player.play(url) return # if the second argument is empty this is the main menu if (len(sys.argv[2]) == 0): createMainMenu() elif sys.argv[2] == "?url=archived": createArchivedMenu() elif sys.argv[2] == '?url=live': createLiveMenu() else: match = re.match('\?archive\=(.*)', sys.argv[2]) if match != None:
if not exists(MyTmp): mkdirs(MyTmp) action = None if len(sys.argv) >= 2: params = getParams(sys.argv[2]) action = getParam("action", params) MyLog("Version:%s" % MyVersion) MyLog("Action:%s" % action) if action == 'search': item = {} MyLog("isPlaying:%s" % Player().isPlaying()) if Player().isPlaying(): item['year'] = getInfoLabel("VideoPlayer.Year") # Year item['season'] = str(getInfoLabel("VideoPlayer.Season")) # Season if item['season'] == '' or item['season'] < 1: item['season'] = 0 item['episode'] = str(getInfoLabel("VideoPlayer.Episode")) # Episode if item['episode'] == '' or item['episode'] < 1: item['episode'] = 0 if item['episode'] == 0: item['title'] = lowercase_with_underscores( getInfoLabel( "VideoPlayer.Title")) # no original title, get just Title else: