mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2024-12-25 02:06:09 +00:00
Support up next
This commit is contained in:
parent
5808c449c4
commit
c3fbdf082c
5 changed files with 110 additions and 18 deletions
|
@ -159,6 +159,13 @@ def get_next(index=None, limit=1):
|
|||
'StartIndex': None if index is None else int(index)
|
||||
})
|
||||
|
||||
def get_adjacent_episodes(show_id, item_id):
|
||||
return shows("/%s/Episodes" % show_id, {
|
||||
'UserId': "{UserId}",
|
||||
'AdjacentTo': item_id,
|
||||
'Fields': "Overview"
|
||||
})
|
||||
|
||||
def get_genres(parent_id=None):
|
||||
return _get("Genres", {
|
||||
'ParentId': parent_id,
|
||||
|
|
|
@ -46,7 +46,8 @@ def set_properties(item, method, server_id=None):
|
|||
'SubsMapping': info.get('Subtitles'),
|
||||
'AudioStreamIndex': info.get('AudioStreamIndex'),
|
||||
'SubtitleStreamIndex': info.get('SubtitleStreamIndex'),
|
||||
'CurrentPosition': info.get('CurrentPosition')
|
||||
'CurrentPosition': info.get('CurrentPosition'),
|
||||
'CurrentEpisode': info.get('CurrentEpisode')
|
||||
})
|
||||
|
||||
window('emby_play.json', current)
|
||||
|
@ -400,6 +401,14 @@ class PlayUtils(object):
|
|||
}
|
||||
if settings('transcode_h265.bool'):
|
||||
profile['DirectPlayProfiles'][0]['VideoCodec'] = "h264,mpeg4,mpeg2video"
|
||||
else:
|
||||
profile['TranscodingProfiles'].insert(0, {
|
||||
"Container": "m3u8",
|
||||
"Type": "Video",
|
||||
"AudioCodec": "aac,mp3,ac3,opus,flac,vorbis",
|
||||
"VideoCodec": "h264,h265,hevc,mpeg4,mpeg2video",
|
||||
"MaxAudioChannels": "6"
|
||||
})
|
||||
|
||||
if settings('transcodeHi10P.bool'):
|
||||
profile['CodecProfiles'].append(
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#################################################################################################
|
||||
|
||||
import binascii
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
@ -127,13 +128,20 @@ def find(dict, item):
|
|||
if re.match(key, item, re.I):
|
||||
return dict[key]
|
||||
|
||||
def event(method, data=None):
|
||||
def event(method, data=None, sender=None, hexlify=False):
|
||||
|
||||
''' Data is a dictionary.
|
||||
'''
|
||||
data = data or {}
|
||||
xbmc.executebuiltin('NotifyAll(plugin.video.emby, %s, "[%s]")' % (method, json.dumps(data).replace('"', '\\"')))
|
||||
LOG.debug("---[ event: %s ] %s", method, data)
|
||||
sender = sender or "plugin.video.emby"
|
||||
|
||||
if hexlify:
|
||||
data = '\\"[\\"{0}\\"]\\"'.format(binascii.hexlify(json.dumps(data)))
|
||||
else:
|
||||
data = '"[%s]"' % json.dumps(data).replace('"', '\\"')
|
||||
|
||||
xbmc.executebuiltin('NotifyAll(%s, %s, %s)' % (sender, method, data))
|
||||
LOG.debug("---[ event: %s/%s ] %s", sender, method, data)
|
||||
|
||||
def dialog(dialog_type, *args, **kwargs):
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#################################################################################################
|
||||
|
||||
import binascii
|
||||
import json
|
||||
import logging
|
||||
import threading
|
||||
|
@ -45,8 +46,8 @@ class Monitor(xbmc.Monitor):
|
|||
LOG.info("--<[ kodi scan/%s ]", library)
|
||||
|
||||
def onNotification(self, sender, method, data):
|
||||
|
||||
if sender.lower() not in ('plugin.video.emby', 'xbmc'):
|
||||
|
||||
if sender.lower() not in ('plugin.video.emby', 'xbmc', 'upnextprovider'):
|
||||
return
|
||||
|
||||
if sender == 'plugin.video.emby':
|
||||
|
@ -61,6 +62,18 @@ class Monitor(xbmc.Monitor):
|
|||
return
|
||||
|
||||
data = json.loads(data)[0]
|
||||
|
||||
elif sender == 'upnextprovider':
|
||||
method = method.split('.')[1]
|
||||
|
||||
if method not in ('plugin.video.emby_play_action'):
|
||||
return
|
||||
|
||||
data = json.loads(data)
|
||||
method = "Play"
|
||||
|
||||
if data:
|
||||
data = json.loads(binascii.unhexlify(data[0]))
|
||||
else:
|
||||
if method not in ('Player.OnPlay', 'VideoLibrary.OnUpdate', 'Player.OnAVChange'):
|
||||
return
|
||||
|
|
|
@ -9,7 +9,8 @@ import os
|
|||
import xbmc
|
||||
import xbmcvfs
|
||||
|
||||
from helper import _, window, settings, dialog, JSONRPC
|
||||
from objects.obj import Objects
|
||||
from helper import _, api, window, settings, dialog, event, JSONRPC
|
||||
from emby import Emby
|
||||
|
||||
#################################################################################################
|
||||
|
@ -24,6 +25,7 @@ class Player(xbmc.Player):
|
|||
# Borg - multiple instances, shared state
|
||||
_shared_state = {}
|
||||
played = {}
|
||||
up_next = False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
|
@ -35,6 +37,7 @@ class Player(xbmc.Player):
|
|||
''' We may need to wait for info to be set in kodi monitor.
|
||||
Accounts for scenario where Kodi starts playback and exits immediately.
|
||||
'''
|
||||
self.up_next = False
|
||||
count = 0
|
||||
monitor = xbmc.Monitor()
|
||||
|
||||
|
@ -219,6 +222,49 @@ class Player(xbmc.Player):
|
|||
else:
|
||||
item['SubtitleStreamIndex'] = subs + tracks + 1
|
||||
|
||||
def next_up(self):
|
||||
|
||||
current_file = self.getPlayingFile()
|
||||
item = self.played[current_file]
|
||||
objects = Objects()
|
||||
|
||||
if item['Type'] != 'Episode':
|
||||
return
|
||||
|
||||
next_items = item['Server']['api'].get_adjacent_episodes(item['CurrentEpisode']['tvshowid'], item['Id'])
|
||||
|
||||
for index, next_item in enumerate(next_items['Items']):
|
||||
if next_item['Id'] == item['Id']:
|
||||
|
||||
try:
|
||||
next_item = next_items['Items'][index + 1]
|
||||
except IndexError:
|
||||
LOG.warn("No next up episode.")
|
||||
|
||||
return
|
||||
|
||||
break
|
||||
|
||||
API = api.API(next_item, item['Server']['auth/server-address'])
|
||||
data = objects.map(next_item, "UpNext")
|
||||
artwork = API.get_all_artwork(objects.map(next_item, 'ArtworkParent'), True)
|
||||
data['art'] = {
|
||||
'tvshow.poster': artwork.get('Series.Primary'),
|
||||
'tvshow.fanart': None,
|
||||
'thumb': artwork.get('Primary')
|
||||
}
|
||||
if artwork['Backdrop']:
|
||||
data['art']['tvshow.fanart'] = artwork['Backdrop'][0]
|
||||
|
||||
next_info = {
|
||||
'play_info': {'ItemIds': [data['episodeid']], 'ServerId': item['ServerId'], 'PlayCommand': 'PlayNow'},
|
||||
'current_episode': item['CurrentEpisode'],
|
||||
'next_episode': data
|
||||
}
|
||||
|
||||
LOG.debug("--[ next up ] %s", json.dumps(next_info, indent=4))
|
||||
event("upnext_data", next_info, hexlify=True)
|
||||
|
||||
def onPlayBackPaused(self):
|
||||
current_file = self.getPlayingFile()
|
||||
|
||||
|
@ -268,15 +314,32 @@ class Player(xbmc.Player):
|
|||
|
||||
item = self.played[current_file]
|
||||
|
||||
if window('emby.external.bool'):
|
||||
return
|
||||
|
||||
if not report:
|
||||
|
||||
previous = item['CurrentPosition']
|
||||
item['CurrentPosition'] = int(self.getTime())
|
||||
|
||||
if int(item['CurrentPosition']) == 1:
|
||||
return
|
||||
|
||||
try:
|
||||
played = float(item['CurrentPosition'] * 10000000) / int(item['Runtime']) * 100
|
||||
except ZeroDivisionError: # Runtime is 0.
|
||||
played = 0
|
||||
|
||||
if played > 2.0 and not self.up_next:
|
||||
|
||||
self.up_next = True
|
||||
self.next_up()
|
||||
|
||||
if (item['CurrentPosition'] - previous) < 30:
|
||||
|
||||
return
|
||||
|
||||
|
||||
result = JSONRPC('Application.GetProperties').execute({'properties': ["volume", "muted"]})
|
||||
result = result.get('result', {})
|
||||
item['Volume'] = result.get('volume')
|
||||
|
@ -330,18 +393,10 @@ class Player(xbmc.Player):
|
|||
if item:
|
||||
window('emby.skip.%s.bool' % item['Id'], True)
|
||||
|
||||
if item['CurrentPosition'] and item['Runtime']:
|
||||
if window('emby.external.bool'):
|
||||
window('emby.external', clear=True)
|
||||
|
||||
try:
|
||||
if window('emby.external.bool'):
|
||||
window('emby.external', clear=True)
|
||||
raise ValueError
|
||||
|
||||
played = float(item['CurrentPosition'] * 10000000) / int(item['Runtime'])
|
||||
except ZeroDivisionError: # Runtime is 0.
|
||||
played = 0
|
||||
except ValueError:
|
||||
played = 100
|
||||
if int(item['CurrentPosition']) == 1:
|
||||
item['CurrentPosition'] = int(item['Runtime'])
|
||||
|
||||
data = {
|
||||
|
|
Loading…
Reference in a new issue