def queue_next_item(data=None, episode=None): """Function to add next episode to the UpNext queue""" next_item = {} play_url = data.get('play_url') if data else None episodeid = (utils.get_int(episode, 'episodeid') if episode else constants.UNKNOWN_DATA) if play_url: next_item.update(file=play_url) elif episode and episodeid != constants.UNKNOWN_DATA: next_item.update(episodeid=episodeid) if next_item: log('Adding to queue: {0}'.format(next_item)) utils.jsonrpc(method='Playlist.Add', params={ 'playlistid': get_player_id(), 'item': next_item }, no_response=True) else: log('Nothing added to queue') return bool(next_item)
def play_addon_item(self): if self.data.get('play_url'): self.log('Playing the next episode directly: %(play_url)s' % self.data, 2) jsonrpc(method='Player.Open', params=dict(item=dict(file=self.data.get('play_url')))) else: self.log('Sending %(encoding)s data to add-on to play: %(play_info)s' % dict(encoding=self.encoding, **self.data), 2) event(message=self.data.get('id'), data=self.data.get('play_info'), sender='upnextprovider', encoding=self.encoding)
def get_now_playing(self): # Seems to work too fast loop whilst waiting for it to become active result = dict() while not result.get('result'): result = jsonrpc(method='Player.GetActivePlayers') self.log('Got active player %s' % result, 2) if not result.get('result'): return None playerid = result.get('result')[0].get('playerid') # Get details of the playing media self.log('Getting details of now playing media', 2) result = jsonrpc(method='Player.GetItem', params=dict( playerid=playerid, properties=[ 'episode', 'genre', 'playcount', 'plotoutline', 'season', 'showtitle', 'tvshowid' ], )) self.log('Got details of now playing media %s' % result, 2) return result
def play_addon_item(data, encoding, resume=False): """Function to play next addon item, either using JSONRPC Player.Open or by passthrough back to the addon""" play_url = data.get('play_url') if play_url: log('Playing from addon - {0}'.format(play_url)) utils.jsonrpc(method='Player.Open', params={'item': { 'file': play_url }}, options={'resume': resume}, no_response=True) return play_info = data.get('play_info') if play_info: log('Sending as {0} to addon - {1}'.format(encoding, play_info)) utils.event(message=data.get('id'), data=play_info, sender='upnextprovider', encoding=encoding) return log('Error: no addon data available for playback', utils.LOGWARNING)
def get_from_library(episodeid, tvshowid=None): """Function to get show and episode details from Kodi library""" result = utils.jsonrpc(method='VideoLibrary.GetEpisodeDetails', params={ 'episodeid': episodeid, 'properties': EPISODE_PROPERTIES }) result = result.get('result', {}).get('episodedetails') if not result: log('Error: episode info not found in library', utils.LOGWARNING) return None episode = result if not tvshowid: tvshowid = episode.get('tvshowid', constants.UNKNOWN_DATA) result = utils.jsonrpc(method='VideoLibrary.GetTVShowDetails', params={ 'tvshowid': tvshowid, 'properties': TVSHOW_PROPERTIES }) result = result.get('result', {}).get('tvshowdetails') if not result: log('Error: show info not found in library', utils.LOGWARNING) return None result.update(episode) log('Episode from library: {0}'.format(result)) return result
def play_kodi_item(episode, resume=False): """Function to directly play a file from the Kodi library""" log('Playing from library: {0}'.format(episode)) utils.jsonrpc( method='Player.Open', params={'item': { 'episodeid': utils.get_int(episode, 'episodeid') }}, options={'resume': resume}, no_response=True)
def queue_next_item(self, episode): next_item = {} if not self.data: next_item.update(episodeid=episode.get('episodeid')) elif self.data.get('play_url'): next_item.update(file=self.data.get('play_url')) if next_item: jsonrpc(method='Playlist.Add', id=0, params=dict(playlistid=1, item=next_item)) return bool(next_item)
def dequeue_next_item(): """Function to remove the 2nd item from the playlist, used by the UpNext queue for the next video to be played""" log('Removing unplayed next item from queue') utils.jsonrpc(method='Playlist.Remove', params={ 'playlistid': get_player_id(), 'position': 1 }, no_response=True) return False
def reset_queue(): """Function to remove the 1st item from the playlist, used by the UpNext queue for the video that was just played""" log('Removing previously played item from queue') utils.jsonrpc(method='Playlist.Remove', params={ 'playlistid': get_player_id(), 'position': 0 }, no_response=True) return False
def get_next_in_playlist(self, position): result = jsonrpc(method='Playlist.GetItems', params=dict( playlistid=1, limits=dict(start=position + 1, end=position + 2), properties=[ 'art', 'dateadded', 'episode', 'file', 'firstaired', 'lastplayed', 'playcount', 'plot', 'rating', 'resume', 'runtime', 'season', 'showtitle', 'streamdetails', 'title', 'tvshowid', 'writer' ], )) if not result: return None self.log('Got details of next playlist item %s' % result, 2) if result.get('result', {}).get('items') is None: return None item = result.get('result', {}).get('items', [])[0] if item.get('type') != 'episode': return None item['episodeid'] = item.get('id') item['tvshowid'] = item.get('tvshowid', item.get('id')) return item
def get_episodeid(tvshowid, season, episode): """Function to search Kodi library for episodeid by tvshowid, season, and episode""" filters = [{ 'field': 'season', 'operator': 'is', 'value': str(season) }, { 'field': 'episode', 'operator': 'is', 'value': str(episode) }] filters = {'and': filters} result = utils.jsonrpc(method='VideoLibrary.GetEpisodes', params={ 'tvshowid': tvshowid, 'properties': EPISODE_PROPERTIES, 'limits': { 'start': 0, 'end': 1 }, 'filter': filters }) result = result.get('result', {}).get('episodes') if not result: log('Error: episodeid not found in library', utils.LOGWARNING) return constants.UNKNOWN_DATA return utils.get_int(result[0], 'episodeid')
def showtitle_to_id(title): result = jsonrpc(method='VideoLibrary.GetTVShows', id='libTvShows', params=dict(properties=['title'])) for tvshow in result.get('result', {}).get('tvshows', []): if tvshow.get('label') == title: return tvshow.get('tvshowid') return '-1'
def get_player_id(player_type=None, player_id_cache=[None]): # pylint: disable=dangerous-default-value """Function to get active player ID""" # We don't need to actually get playerid everytime, cache and reuse instead if player_id_cache[0] is not None: return player_id_cache[0] # Sometimes Kodi gets confused and uses a music playlist for video content, # so we use the first active player id instead. result = utils.jsonrpc(method='Player.GetActivePlayers') result = result.get('result', [{}]) if player_type: result = [ player for player in result if player.get('type') == player_type ] else: result = [ player for player in result if player.get('type') in PLAYER_TYPES ] if not result: log('Error: no active player', utils.LOGWARNING) return None player_id = result[0].get('playerid') player_id_cache[0] = player_id return player_id
def handle_kodi_lookup_of_current_episode(self, tvshowid, current_episode_id): result = jsonrpc(method='VideoLibrary.GetEpisodes', params=dict( tvshowid=tvshowid, properties=[ 'art', 'dateadded', 'episode', 'file', 'firstaired', 'lastplayed', 'playcount', 'plot', 'rating', 'resume', 'runtime', 'season', 'showtitle', 'streamdetails', 'title', 'tvshowid', 'writer' ], sort=dict(method='episode'), )) if not result.get('result'): return None self.log('Find current episode called', 2) sleep(100) # Find the next unwatched and the newest added episodes episodes = result.get('result', {}).get('episodes', []) for idx, episode in enumerate(episodes): # Find position of current episode if current_episode_id == episode.get('episodeid'): self.log( 'Find current episode found episode in position: %d' % idx, 2) return episode # No next episode found self.log('No next episode found', 1) return None
def play_playlist_item(position=0, resume=False): """Function to play episode in playlist""" log('Playing from playlist position: {0}'.format(position)) if position == 'next': position = get_playlist_position() # JSON Player.Open can be too slow but is needed if resuming is enabled # Unfortunately resuming from a playlist item does not seem to work... utils.jsonrpc( method='Player.Open', params={'item': { 'playlistid': get_player_id(), 'position': position }}, options={'resume': resume}, no_response=True)
def handle_just_watched(episodeid, playcount, reset_playcount=False, reset_resume=True): """Function to update playcount and resume point of just watched video""" result = utils.jsonrpc(method='VideoLibrary.GetEpisodeDetails', params={ 'episodeid': episodeid, 'properties': ['playcount', 'resume'], }) result = result.get('result', {}).get('episodedetails') if result: actual_playcount = utils.get_int(result, 'playcount', 0) actual_resume = utils.get_int(result.get('resume'), 'position', 0) else: return params = {} # If Kodi has not updated playcount then UpNext will if reset_playcount: playcount = -1 if reset_playcount or actual_playcount == playcount: playcount += 1 params['playcount'] = playcount # If resume point has been saved then reset it if actual_resume and reset_resume: params['resume'] = {'position': 0} # Only update library if playcount or resume point needs to change if params: params['episodeid'] = episodeid utils.jsonrpc(method='VideoLibrary.SetEpisodeDetails', params=params, no_response=True) log( 'Library update: id - {0}{1}{2}{3}'.format( episodeid, ', playcount - {0} to {1}'.format( actual_playcount, playcount) if 'playcount' in params else '', ', resume - {0} to 0'.format(actual_resume) if 'resume' in params else '', '' if params else ', no change'), utils.LOGDEBUG)
def get_player_speed(): """Function to get speed of active player""" result = utils.jsonrpc(method='Player.GetProperties', params={ 'playerid': get_player_id(), 'properties': ['speed'], }) result = utils.get_int(result.get('result', {}), 'speed', 1) return result
def test_diagnostics(kak, mock, send): send('exec 7oabcdefghijklmnopqrstuvwxyz<esc>gg') msg = utils.jsonrpc({ 'method': 'textDocument/publishDiagnostics', 'params': { 'uri': 'file://*scratch*', 'diagnostics': [{ 'message': 'line ' + str(y), 'range': { 'start': { 'line': y - 1, 'character': y * 2 - 1 }, 'end': { 'line': y - 1, 'character': y * 3 - 1 } } } for y in [2, 4, 6]] } }) mock.stdout.write(msg) time.sleep(0.1) first = True for y in [2, 4, 6, 2, 4]: send('lsp-diagnostics-jump next') # 2 4 6 2 4 if first: first = False print('listening...') obj = process(mock) pprint(obj) assert (obj['method'] == 'textDocument/didChange') assert (obj['params']['contentChanges'][0]['text'] == '\n' + 'abcdefghijklmnopqrstuvwxyz\n' * 7) time.sleep(0.1) call = libkak.Remote.onclient(kak.pid, 'unnamed0') d = call(lambda selection_desc: selection_desc) print('selection_desc:', d) assert (d == ((y, 2 * y), (y, 3 * y - 1)) ) # end point exclusive according to protocol.md send('lsp-diagnostics-jump prev') # 2 time.sleep(0.1) send('lsp-diagnostics docsclient') time.sleep(0.3) send('exec x') time.sleep(0.3) call = libkak.Remote.onclient(kak.pid, 'unnamed0') s = call(lambda selection: selection) print('final selection:', s) assert (s == 'line 2\n')
def get_next_in_playlist(position, unwatched_only=False): """Function to get details of next episode in playlist""" result = utils.jsonrpc( method='Playlist.GetItems', params={ 'playlistid': get_player_id(), # limits are zero indexed, position is one indexed 'limits': { 'start': position, 'end': -1 if unwatched_only else position + 1 }, 'properties': EPISODE_PROPERTIES }) items = result.get('result', {}).get('items') # Get first unwatched item in the list of remaining playlist entries if unwatched_only and items: position_offset, item = next( ((idx, item) for idx, item in enumerate(items) if utils.get_int(item, 'playcount') < 1), (0, None)) position += position_offset # Or just get the first item in the list of remaining playlist entries else: item = items[0] if items else None # Don't check if next item is an episode, just use it if it is there if not item: # item.get('type') != 'episode': log('Error: no next item found in playlist', utils.LOGWARNING) return None # Playlist item may not have had video info details set # Try and populate required details if missing if not item.get('title'): item['title'] = item.get('label', '') item['episodeid'] = utils.get_int(item, 'episodeid', utils.get_int(item, 'id')) item['tvshowid'] = utils.get_int(item, 'tvshowid') # If missing season/episode, change to empty string to avoid episode # formatting issues ("S-1E-1") in UpNext popup if utils.get_int(item, 'season') == constants.UNKNOWN_DATA: item['season'] = '' if utils.get_int(item, 'episode') == constants.UNKNOWN_DATA: item['episode'] = '' # Store current playlist position for later use item['playlist_position'] = position log('Next item in playlist at position {0}: {1}'.format(position, item)) return item
def get_episode_id(showid, show_season, show_episode): show_season = int(show_season) show_episode = int(show_episode) result = jsonrpc(method='VideoLibrary.GetEpisodes', params=dict( properties=['episode', 'season'], tvshowid=int(showid), )) episodeid = 0 for episode in result.get('result', {}).get('episodes', []): if episode.get('episodeid') and episode.get('season') == show_season and episode.get('episode') == show_episode: episodeid = episode.get('episodeid') return episodeid
def get_now_playing(): """Function to get detail of currently playing item""" result = utils.jsonrpc(method='Player.GetItem', params={ 'playerid': get_player_id(), 'properties': EPISODE_PROPERTIES, }) result = result.get('result', {}).get('item') if not result: log('Error: now playing item info not found', utils.LOGWARNING) return None log('Now playing: {0}'.format(result)) return result
def handle_kodi_lookup_of_episode(self, tvshowid, current_file, include_watched, current_episode_id): result = jsonrpc(method='VideoLibrary.GetEpisodes', params=dict( tvshowid=tvshowid, properties=['art', 'dateadded', 'episode', 'file', 'firstaired', 'lastplayed', 'playcount', 'plot', 'rating', 'resume', 'runtime', 'season', 'showtitle', 'streamdetails', 'title', 'tvshowid', 'writer'], sort=dict(method='episode'), )) if not result.get('result'): return None self.log('Got details of next up episode %s' % result, 2) sleep(100) # Find the next unwatched and the newest added episodes return self.find_next_episode(result, current_file, include_watched, current_episode_id)
def get_playlistid(playlistid_cache=[None]): # pylint: disable=dangerous-default-value """Function to get playlistid of active player""" # We don't need to actually get playlistid everytime, cache and reuse instead if playlistid_cache[0] is not None: return playlistid_cache[0] result = jsonrpc(method='Player.GetProperties', params={ 'playerid': Api._get_playerid(playerid_cache=[None]), 'properties': ['playlistid'], }) result = get_int(result.get('result', {}), 'playlistid', Api.PLAYER_PLAYLIST['video']) return result
def _get_playerid(playerid_cache=[None]): # pylint: disable=dangerous-default-value """Function to get active player playerid""" # We don't need to actually get playerid everytime, cache and reuse instead if playerid_cache[0] is not None: return playerid_cache[0] # Sometimes Kodi gets confused and uses a music playlist for video content, # so get the first active player instead, default to video player. result = jsonrpc(method='Player.GetActivePlayers') result = [ player for player in result.get('result', [{}]) if player.get('type', 'video') in Api.PLAYER_PLAYLIST ] playerid = get_int(result[0], 'playerid') if result else -1 if playerid == -1: return None playerid_cache[0] = playerid return playerid
def get_next_in_playlist(self, position): result = jsonrpc( method='Playlist.GetItems', params=dict( playlistid=Api.get_playlistid(), # limits are zero indexed, position is one indexed limits=dict(start=position, end=position + 1), properties=[ 'art', 'dateadded', 'episode', 'file', 'firstaired', 'lastplayed', 'playcount', 'plot', 'rating', 'resume', 'runtime', 'season', 'showtitle', 'streamdetails', 'title', 'tvshowid', 'writer' ], )) item = result.get('result', {}).get('items') # Don't check if next item is an episode, just use it if it is there if not item: # item.get('type') != 'episode': self.log('Error: no next item found in playlist', 1) return None item = item[0] # Playlist item may not have had video info details set # Try and populate required details if missing if not item.get('title'): item['title'] = item.get('label', '') item['episodeid'] = get_int(item, 'id') item['tvshowid'] = get_int(item, 'tvshowid') # If missing season/episode, change to empty string to avoid episode # formatting issues ("S-1E-1") in UpNext popup if get_int(item, 'season') == -1: item['season'] = '' if get_int(item, 'episode') == -1: item['episode'] = '' self.log('Next item in playlist: %s' % item, 2) return item
def get_tvshowid(title): """Function to search Kodi library for tshowid by title""" result = utils.jsonrpc(method='VideoLibrary.GetTVShows', params={ 'properties': ['title'], 'limits': { 'start': 0, 'end': 1 }, 'filter': { 'field': 'title', 'operator': 'is', 'value': title } }) result = result.get('result', {}).get('tvshows') if not result: log('Error: tvshowid not found in library', utils.LOGWARNING) return constants.UNKNOWN_DATA return utils.get_int(result[0], 'tvshowid')
def play_kodi_item(episode): jsonrpc(method='Player.Open', id=0, params=dict(item=dict(episodeid=episode.get('episodeid'))))
def reset_queue(): """Remove previously played item from video playlist""" jsonrpc(method='Playlist.Remove', id=0, params=dict(playlistid=Api.get_playlistid(), position=0))
def dequeue_next_item(): """Remove unplayed next item from video playlist""" jsonrpc(method='Playlist.Remove', id=0, params=dict(playlistid=Api.get_playlistid(), position=1)) return False
def get_next_from_library(episodeid=constants.UNKNOWN_DATA, tvshowid=None, unwatched_only=False, next_season=True, random=False, episode=None): """Function to get show and next episode details from Kodi library""" episode = episode.copy() if episode else get_from_library(episodeid) if not episode: log('Error: no next episode found, current episode not in library', utils.LOGWARNING) episode = None new_season = False return episode, new_season (path, filename) = os.path.split(episode['file']) filters = [ # Check that both next filename and path are different to current # to deal with different file naming schemes e.g. # Season 1/Episode 1.mkv # Season 1/Episode 1/video.mkv # Season 1/Episode 1-2-3.mkv { 'or': [{ 'field': 'filename', 'operator': 'isnot', 'value': filename }, { 'field': 'path', 'operator': 'isnot', 'value': path }] } ] if unwatched_only: # Exclude watched episodes filters.append({ 'field': 'playcount', 'operator': 'lessthan', 'value': '1' }) if not random: # Next episode in current season episode_filter = { 'and': [{ 'field': 'season', 'operator': 'is', 'value': str(episode['season']) }, { 'field': 'episode', 'operator': 'greaterthan', 'value': str(episode['episode']) }] } # Next episode in next season if next_season: episode_filter = [ episode_filter, { 'field': 'season', 'operator': 'greaterthan', 'value': str(episode['season']) } ] episode_filter = {'or': episode_filter} filters.append(episode_filter) filters = {'and': filters} if not tvshowid: tvshowid = episode.get('tvshowid', constants.UNKNOWN_DATA) result = utils.jsonrpc(method='VideoLibrary.GetEpisodes', params={ 'tvshowid': tvshowid, 'properties': EPISODE_PROPERTIES, 'sort': ({ 'method': 'random' } if random else { 'order': 'ascending', 'method': 'episode' }), 'limits': { 'start': 0, 'end': 1 }, 'filter': filters }) result = result.get('result', {}).get('episodes') if not result: log('No next episode found in library') episode = None new_season = False return episode, new_season log('Next episode from library: {0}'.format(result[0])) new_season = not random and episode['season'] != result[0]['season'] episode.update(result[0]) return episode, new_season