mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2024-11-10 04:06:11 +00:00
refactor the downloadutils a little
move emby DB verify to the database class and run it for the first time an emby db con is created refactor the play class playback started function a little
This commit is contained in:
parent
7a9e29c9b7
commit
0cf8f07daf
6 changed files with 181 additions and 171 deletions
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<addon id="plugin.video.emby"
|
<addon id="plugin.video.emby"
|
||||||
name="Emby"
|
name="Emby"
|
||||||
version="2.3.13"
|
version="2.3.14"
|
||||||
provider-name="Emby.media">
|
provider-name="Emby.media">
|
||||||
<requires>
|
<requires>
|
||||||
<import addon="xbmc.python" version="2.19.0"/>
|
<import addon="xbmc.python" version="2.19.0"/>
|
||||||
|
|
|
@ -98,6 +98,11 @@ class DatabaseConn(object):
|
||||||
|
|
||||||
log.info("opened: %s - %s", self.path, id(self.conn))
|
log.info("opened: %s - %s", self.path, id(self.conn))
|
||||||
self.cursor = self.conn.cursor()
|
self.cursor = self.conn.cursor()
|
||||||
|
|
||||||
|
if self.db_file == "emby":
|
||||||
|
verify_emby_database(self.cursor)
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
return self.cursor
|
return self.cursor
|
||||||
|
|
||||||
def _SQL(self, media_type):
|
def _SQL(self, media_type):
|
||||||
|
@ -130,6 +135,23 @@ class DatabaseConn(object):
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
def verify_emby_database(cursor):
|
||||||
|
# Create the tables for the emby database
|
||||||
|
# emby, view, version
|
||||||
|
|
||||||
|
if window('emby_db_checked') != "true":
|
||||||
|
log.info("Verifying emby DB")
|
||||||
|
cursor.execute(
|
||||||
|
"""CREATE TABLE IF NOT EXISTS emby(
|
||||||
|
emby_id TEXT UNIQUE, media_folder TEXT, emby_type TEXT, media_type TEXT,
|
||||||
|
kodi_id INTEGER, kodi_fileid INTEGER, kodi_pathid INTEGER, parent_id INTEGER,
|
||||||
|
checksum INTEGER)""")
|
||||||
|
cursor.execute(
|
||||||
|
"""CREATE TABLE IF NOT EXISTS view(
|
||||||
|
view_id TEXT UNIQUE, view_name TEXT, media_type TEXT, kodi_tagid INTEGER)""")
|
||||||
|
cursor.execute("CREATE TABLE IF NOT EXISTS version(idVersion TEXT)")
|
||||||
|
window('emby_db_checked', value="true")
|
||||||
|
|
||||||
def db_reset():
|
def db_reset():
|
||||||
|
|
||||||
dialog = xbmcgui.Dialog()
|
dialog = xbmcgui.Dialog()
|
||||||
|
|
|
@ -209,7 +209,6 @@ class DownloadUtils(object):
|
||||||
log.debug("===== ENTER downloadUrl =====")
|
log.debug("===== ENTER downloadUrl =====")
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
default_link = ""
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Ensure server info is loaded
|
# Ensure server info is loaded
|
||||||
|
@ -247,18 +246,17 @@ class DownloadUtils(object):
|
||||||
log.debug("====== 204 Success ======")
|
log.debug("====== 204 Success ======")
|
||||||
# Read response to release connection
|
# Read response to release connection
|
||||||
response.content
|
response.content
|
||||||
|
if action_type == "GET":
|
||||||
|
raise Warning("Response Code 204: No Content for GET request")
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
elif response.status_code == requests.codes.ok:
|
elif response.status_code == requests.codes.ok:
|
||||||
try:
|
# UNICODE - JSON object
|
||||||
# UNICODE - JSON object
|
json_data = response.json()
|
||||||
response = response.json()
|
log.debug("====== 200 Success ======")
|
||||||
log.debug("====== 200 Success ======")
|
log.debug("Response: %s", json_data)
|
||||||
log.debug("Response: %s", response)
|
return json_data
|
||||||
return response
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
if response.headers.get('content-type') != "text/html":
|
|
||||||
log.info("Unable to convert the response for: %s", url)
|
|
||||||
|
|
||||||
else: # Bad status code
|
else: # Bad status code
|
||||||
log.error("=== Bad status response: %s ===", response.status_code)
|
log.error("=== Bad status response: %s ===", response.status_code)
|
||||||
|
@ -282,7 +280,7 @@ class DownloadUtils(object):
|
||||||
|
|
||||||
if response.status_code == 400:
|
if response.status_code == 400:
|
||||||
log.error("Malformed request: %s", error)
|
log.error("Malformed request: %s", error)
|
||||||
raise Warning('400')
|
raise Warning('400:' + str(error))
|
||||||
|
|
||||||
if response.status_code == 401:
|
if response.status_code == 401:
|
||||||
# Unauthorized
|
# Unauthorized
|
||||||
|
@ -312,12 +310,13 @@ class DownloadUtils(object):
|
||||||
xbmcgui.Dialog().notification(heading="Error connecting",
|
xbmcgui.Dialog().notification(heading="Error connecting",
|
||||||
message="Unauthorized.",
|
message="Unauthorized.",
|
||||||
icon=xbmcgui.NOTIFICATION_ERROR)
|
icon=xbmcgui.NOTIFICATION_ERROR)
|
||||||
raise Warning('401')
|
raise Warning('401:' + str(error))
|
||||||
|
|
||||||
except requests.exceptions.RequestException as error:
|
except requests.exceptions.RequestException as error:
|
||||||
log.error("unknown error connecting to: %s", url)
|
log.error("unknown error connecting to: %s", url)
|
||||||
|
|
||||||
return default_link
|
# something went wrong so return a None as we have no valid data
|
||||||
|
return None
|
||||||
|
|
||||||
def _ensure_server(self, server_id=None):
|
def _ensure_server(self, server_id=None):
|
||||||
|
|
||||||
|
|
|
@ -606,20 +606,6 @@ class LibrarySync(threading.Thread):
|
||||||
# Database out of date.
|
# Database out of date.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _verify_emby_database(self):
|
|
||||||
# Create the tables for the emby database
|
|
||||||
with database.DatabaseConn('emby') as cursor:
|
|
||||||
# emby, view, version
|
|
||||||
cursor.execute(
|
|
||||||
"""CREATE TABLE IF NOT EXISTS emby(
|
|
||||||
emby_id TEXT UNIQUE, media_folder TEXT, emby_type TEXT, media_type TEXT,
|
|
||||||
kodi_id INTEGER, kodi_fileid INTEGER, kodi_pathid INTEGER, parent_id INTEGER,
|
|
||||||
checksum INTEGER)""")
|
|
||||||
cursor.execute(
|
|
||||||
"""CREATE TABLE IF NOT EXISTS view(
|
|
||||||
view_id TEXT UNIQUE, view_name TEXT, media_type TEXT, kodi_tagid INTEGER)""")
|
|
||||||
cursor.execute("CREATE TABLE IF NOT EXISTS version(idVersion TEXT)")
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -66,172 +66,178 @@ class Player(xbmc.Player):
|
||||||
break
|
break
|
||||||
else: count += 1
|
else: count += 1
|
||||||
|
|
||||||
|
# if we did not get the current file return
|
||||||
|
if currentFile == "":
|
||||||
|
return
|
||||||
|
|
||||||
if currentFile:
|
# process the playing file
|
||||||
|
self.currentFile = currentFile
|
||||||
|
|
||||||
self.currentFile = currentFile
|
# We may need to wait for info to be set in kodi monitor
|
||||||
|
itemId = window("emby_%s.itemid" % currentFile)
|
||||||
|
tryCount = 0
|
||||||
|
while not itemId:
|
||||||
|
|
||||||
# We may need to wait for info to be set in kodi monitor
|
xbmc.sleep(200)
|
||||||
itemId = window("emby_%s.itemid" % currentFile)
|
itemId = window("emby_%s.itemid" % currentFile)
|
||||||
tryCount = 0
|
if tryCount == 20: # try 20 times or about 10 seconds
|
||||||
while not itemId:
|
log.info("Could not find itemId, cancelling playback report...")
|
||||||
|
break
|
||||||
|
else: tryCount += 1
|
||||||
|
|
||||||
xbmc.sleep(200)
|
else:
|
||||||
itemId = window("emby_%s.itemid" % currentFile)
|
log.info("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId))
|
||||||
if tryCount == 20: # try 20 times or about 10 seconds
|
|
||||||
log.info("Could not find itemId, cancelling playback report...")
|
|
||||||
break
|
|
||||||
else: tryCount += 1
|
|
||||||
|
|
||||||
else:
|
# Only proceed if an itemId was found.
|
||||||
log.info("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId))
|
embyitem = "emby_%s" % currentFile
|
||||||
|
runtime = window("%s.runtime" % embyitem)
|
||||||
|
refresh_id = window("%s.refreshid" % embyitem)
|
||||||
|
playMethod = window("%s.playmethod" % embyitem)
|
||||||
|
itemType = window("%s.type" % embyitem)
|
||||||
|
window('emby_skipWatched%s' % itemId, value="true")
|
||||||
|
|
||||||
# Only proceed if an itemId was found.
|
customseek = window('emby_customPlaylist.seektime')
|
||||||
embyitem = "emby_%s" % currentFile
|
if window('emby_customPlaylist') == "true" and customseek:
|
||||||
runtime = window("%s.runtime" % embyitem)
|
# Start at, when using custom playlist (play to Kodi from webclient)
|
||||||
refresh_id = window("%s.refreshid" % embyitem)
|
log.info("Seeking to: %s" % customseek)
|
||||||
playMethod = window("%s.playmethod" % embyitem)
|
self.xbmcplayer.seekTime(int(customseek)/10000000.0)
|
||||||
itemType = window("%s.type" % embyitem)
|
window('emby_customPlaylist.seektime', clear=True)
|
||||||
window('emby_skipWatched%s' % itemId, value="true")
|
|
||||||
|
|
||||||
customseek = window('emby_customPlaylist.seektime')
|
|
||||||
if window('emby_customPlaylist') == "true" and customseek:
|
|
||||||
# Start at, when using custom playlist (play to Kodi from webclient)
|
|
||||||
log.info("Seeking to: %s" % customseek)
|
|
||||||
self.xbmcplayer.seekTime(int(customseek)/10000000.0)
|
|
||||||
window('emby_customPlaylist.seektime', clear=True)
|
|
||||||
|
|
||||||
|
try:
|
||||||
seekTime = self.xbmcplayer.getTime()
|
seekTime = self.xbmcplayer.getTime()
|
||||||
|
except:
|
||||||
|
# at this point we should be playing and if not then bail out
|
||||||
|
return
|
||||||
|
|
||||||
# Get playback volume
|
# Get playback volume
|
||||||
volume_query = {
|
volume_query = {
|
||||||
|
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "Application.GetProperties",
|
||||||
|
"params": {
|
||||||
|
|
||||||
|
"properties": ["volume", "muted"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = xbmc.executeJSONRPC(json.dumps(volume_query))
|
||||||
|
result = json.loads(result)
|
||||||
|
result = result.get('result')
|
||||||
|
|
||||||
|
volume = result.get('volume')
|
||||||
|
muted = result.get('muted')
|
||||||
|
|
||||||
|
# Postdata structure to send to Emby server
|
||||||
|
url = "{server}/emby/Sessions/Playing"
|
||||||
|
postdata = {
|
||||||
|
|
||||||
|
'QueueableMediaTypes': "Video",
|
||||||
|
'CanSeek': True,
|
||||||
|
'ItemId': itemId,
|
||||||
|
'MediaSourceId': itemId,
|
||||||
|
'PlayMethod': playMethod,
|
||||||
|
'VolumeLevel': volume,
|
||||||
|
'PositionTicks': int(seekTime * 10000000),
|
||||||
|
'IsMuted': muted
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the current audio track and subtitles
|
||||||
|
if playMethod == "Transcode":
|
||||||
|
# property set in PlayUtils.py
|
||||||
|
postdata['AudioStreamIndex'] = window("%sAudioStreamIndex" % currentFile)
|
||||||
|
postdata['SubtitleStreamIndex'] = window("%sSubtitleStreamIndex" % currentFile)
|
||||||
|
else:
|
||||||
|
# Get the current kodi audio and subtitles and convert to Emby equivalent
|
||||||
|
tracks_query = {
|
||||||
|
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"method": "Application.GetProperties",
|
"method": "Player.GetProperties",
|
||||||
"params": {
|
"params": {
|
||||||
|
|
||||||
"properties": ["volume", "muted"]
|
"playerid": 1,
|
||||||
|
"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = xbmc.executeJSONRPC(json.dumps(volume_query))
|
result = xbmc.executeJSONRPC(json.dumps(tracks_query))
|
||||||
result = json.loads(result)
|
result = json.loads(result)
|
||||||
result = result.get('result')
|
result = result.get('result')
|
||||||
|
|
||||||
volume = result.get('volume')
|
try: # Audio tracks
|
||||||
muted = result.get('muted')
|
indexAudio = result['currentaudiostream']['index']
|
||||||
|
except (KeyError, TypeError):
|
||||||
|
indexAudio = 0
|
||||||
|
|
||||||
# Postdata structure to send to Emby server
|
try: # Subtitles tracks
|
||||||
url = "{server}/emby/Sessions/Playing"
|
indexSubs = result['currentsubtitle']['index']
|
||||||
postdata = {
|
except (KeyError, TypeError):
|
||||||
|
indexSubs = 0
|
||||||
|
|
||||||
'QueueableMediaTypes': "Video",
|
try: # If subtitles are enabled
|
||||||
'CanSeek': True,
|
subsEnabled = result['subtitleenabled']
|
||||||
'ItemId': itemId,
|
except (KeyError, TypeError):
|
||||||
'MediaSourceId': itemId,
|
subsEnabled = ""
|
||||||
'PlayMethod': playMethod,
|
|
||||||
'VolumeLevel': volume,
|
|
||||||
'PositionTicks': int(seekTime * 10000000),
|
|
||||||
'IsMuted': muted
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get the current audio track and subtitles
|
# Postdata for the audio
|
||||||
if playMethod == "Transcode":
|
postdata['AudioStreamIndex'] = indexAudio + 1
|
||||||
# property set in PlayUtils.py
|
|
||||||
postdata['AudioStreamIndex'] = window("%sAudioStreamIndex" % currentFile)
|
# Postdata for the subtitles
|
||||||
postdata['SubtitleStreamIndex'] = window("%sSubtitleStreamIndex" % currentFile)
|
if subsEnabled and len(xbmc.Player().getAvailableSubtitleStreams()) > 0:
|
||||||
|
|
||||||
|
# Number of audiotracks to help get Emby Index
|
||||||
|
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
||||||
|
mapping = window("%s.indexMapping" % embyitem)
|
||||||
|
|
||||||
|
if mapping: # Set in playbackutils.py
|
||||||
|
|
||||||
|
log.debug("Mapping for external subtitles index: %s" % mapping)
|
||||||
|
externalIndex = json.loads(mapping)
|
||||||
|
|
||||||
|
if externalIndex.get(str(indexSubs)):
|
||||||
|
# If the current subtitle is in the mapping
|
||||||
|
postdata['SubtitleStreamIndex'] = externalIndex[str(indexSubs)]
|
||||||
|
else:
|
||||||
|
# Internal subtitle currently selected
|
||||||
|
subindex = indexSubs - len(externalIndex) + audioTracks + 1
|
||||||
|
postdata['SubtitleStreamIndex'] = subindex
|
||||||
|
|
||||||
|
else: # Direct paths enabled scenario or no external subtitles set
|
||||||
|
postdata['SubtitleStreamIndex'] = indexSubs + audioTracks + 1
|
||||||
else:
|
else:
|
||||||
# Get the current kodi audio and subtitles and convert to Emby equivalent
|
postdata['SubtitleStreamIndex'] = ""
|
||||||
tracks_query = {
|
|
||||||
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"id": 1,
|
|
||||||
"method": "Player.GetProperties",
|
|
||||||
"params": {
|
|
||||||
|
|
||||||
"playerid": 1,
|
|
||||||
"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = xbmc.executeJSONRPC(json.dumps(tracks_query))
|
|
||||||
result = json.loads(result)
|
|
||||||
result = result.get('result')
|
|
||||||
|
|
||||||
try: # Audio tracks
|
|
||||||
indexAudio = result['currentaudiostream']['index']
|
|
||||||
except (KeyError, TypeError):
|
|
||||||
indexAudio = 0
|
|
||||||
|
|
||||||
try: # Subtitles tracks
|
|
||||||
indexSubs = result['currentsubtitle']['index']
|
|
||||||
except (KeyError, TypeError):
|
|
||||||
indexSubs = 0
|
|
||||||
|
|
||||||
try: # If subtitles are enabled
|
|
||||||
subsEnabled = result['subtitleenabled']
|
|
||||||
except (KeyError, TypeError):
|
|
||||||
subsEnabled = ""
|
|
||||||
|
|
||||||
# Postdata for the audio
|
|
||||||
postdata['AudioStreamIndex'] = indexAudio + 1
|
|
||||||
|
|
||||||
# Postdata for the subtitles
|
|
||||||
if subsEnabled and len(xbmc.Player().getAvailableSubtitleStreams()) > 0:
|
|
||||||
|
|
||||||
# Number of audiotracks to help get Emby Index
|
|
||||||
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
|
||||||
mapping = window("%s.indexMapping" % embyitem)
|
|
||||||
|
|
||||||
if mapping: # Set in playbackutils.py
|
|
||||||
|
|
||||||
log.debug("Mapping for external subtitles index: %s" % mapping)
|
|
||||||
externalIndex = json.loads(mapping)
|
|
||||||
|
|
||||||
if externalIndex.get(str(indexSubs)):
|
|
||||||
# If the current subtitle is in the mapping
|
|
||||||
postdata['SubtitleStreamIndex'] = externalIndex[str(indexSubs)]
|
|
||||||
else:
|
|
||||||
# Internal subtitle currently selected
|
|
||||||
subindex = indexSubs - len(externalIndex) + audioTracks + 1
|
|
||||||
postdata['SubtitleStreamIndex'] = subindex
|
|
||||||
|
|
||||||
else: # Direct paths enabled scenario or no external subtitles set
|
|
||||||
postdata['SubtitleStreamIndex'] = indexSubs + audioTracks + 1
|
|
||||||
else:
|
|
||||||
postdata['SubtitleStreamIndex'] = ""
|
|
||||||
|
|
||||||
|
|
||||||
# Post playback to server
|
# Post playback to server
|
||||||
log.debug("Sending POST play started: %s." % postdata)
|
log.debug("Sending POST play started: %s." % postdata)
|
||||||
self.doUtils(url, postBody=postdata, action_type="POST")
|
self.doUtils(url, postBody=postdata, action_type="POST")
|
||||||
|
|
||||||
# Ensure we do have a runtime
|
# Ensure we do have a runtime
|
||||||
try:
|
try:
|
||||||
runtime = int(runtime)
|
runtime = int(runtime)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
runtime = self.xbmcplayer.getTotalTime()
|
runtime = self.xbmcplayer.getTotalTime()
|
||||||
log.info("Runtime is missing, Kodi runtime: %s" % runtime)
|
log.info("Runtime is missing, Kodi runtime: %s" % runtime)
|
||||||
|
|
||||||
# Save data map for updates and position calls
|
# Save data map for updates and position calls
|
||||||
data = {
|
data = {
|
||||||
|
|
||||||
'runtime': runtime,
|
'runtime': runtime,
|
||||||
'item_id': itemId,
|
'item_id': itemId,
|
||||||
'refresh_id': refresh_id,
|
'refresh_id': refresh_id,
|
||||||
'currentfile': currentFile,
|
'currentfile': currentFile,
|
||||||
'AudioStreamIndex': postdata['AudioStreamIndex'],
|
'AudioStreamIndex': postdata['AudioStreamIndex'],
|
||||||
'SubtitleStreamIndex': postdata['SubtitleStreamIndex'],
|
'SubtitleStreamIndex': postdata['SubtitleStreamIndex'],
|
||||||
'playmethod': playMethod,
|
'playmethod': playMethod,
|
||||||
'Type': itemType,
|
'Type': itemType,
|
||||||
'currentPosition': int(seekTime)
|
'currentPosition': int(seekTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.played_info[currentFile] = data
|
self.played_info[currentFile] = data
|
||||||
log.info("ADDING_FILE: %s" % self.played_info)
|
log.info("ADDING_FILE: %s" % self.played_info)
|
||||||
|
|
||||||
ga = GoogleAnalytics()
|
ga = GoogleAnalytics()
|
||||||
ga.sendEventData("PlayAction", itemType, playMethod)
|
ga.sendEventData("PlayAction", itemType, playMethod)
|
||||||
ga.sendScreenView(itemType)
|
ga.sendScreenView(itemType)
|
||||||
|
|
||||||
def reportPlayback(self):
|
def reportPlayback(self):
|
||||||
|
|
||||||
|
|
|
@ -100,9 +100,6 @@ class Service(object):
|
||||||
self.websocket_thread = wsc.WebSocketClient()
|
self.websocket_thread = wsc.WebSocketClient()
|
||||||
self.library_thread = librarysync.LibrarySync()
|
self.library_thread = librarysync.LibrarySync()
|
||||||
|
|
||||||
# Verify database structure, otherwise create it.
|
|
||||||
self.library_thread._verify_emby_database()
|
|
||||||
|
|
||||||
while not self.monitor.abortRequested():
|
while not self.monitor.abortRequested():
|
||||||
|
|
||||||
if window('emby_kodiProfile') != kodi_profile:
|
if window('emby_kodiProfile') != kodi_profile:
|
||||||
|
|
Loading…
Reference in a new issue