Merge remote-tracking branch 'MediaBrowser/master' into develop
This commit is contained in:
commit
4704d8e983
16 changed files with 195 additions and 90 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.plexkodiconnect"
|
<addon id="plugin.video.plexkodiconnect"
|
||||||
name="PlexKodiConnect"
|
name="PlexKodiConnect"
|
||||||
version="2.2.1"
|
version="2.2.4"
|
||||||
provider-name="croneter">
|
provider-name="croneter">
|
||||||
<requires>
|
<requires>
|
||||||
<import addon="xbmc.python" version="2.1.0"/>
|
<import addon="xbmc.python" version="2.1.0"/>
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
|
version 2.2.4
|
||||||
|
- Fix external subs being appended to direct play (via add-on playback)
|
||||||
|
- First attempt at keeping Kodi awake during the initial sync
|
||||||
|
|
||||||
|
version 2.2.3
|
||||||
|
- Fix resume
|
||||||
|
|
||||||
|
version 2.2.2
|
||||||
|
- Fix dialog crash in the manual sync
|
||||||
|
- Fix view duplicate views appearing via launching the emby add-on, when grouping views in emby
|
||||||
|
|
||||||
version 2.2.1
|
version 2.2.1
|
||||||
- Fix artist/album link for music videos
|
- Fix artist/album link for music videos
|
||||||
- Fix progress dialog when the manual sync runs at start up
|
- Fix progress dialog when the manual sync runs at start up
|
||||||
|
|
|
@ -65,7 +65,8 @@ class Main:
|
||||||
'recentepisodes': entrypoint.getRecentEpisodes,
|
'recentepisodes': entrypoint.getRecentEpisodes,
|
||||||
'refreshplaylist': entrypoint.refreshPlaylist,
|
'refreshplaylist': entrypoint.refreshPlaylist,
|
||||||
'companion': entrypoint.plexCompanion,
|
'companion': entrypoint.plexCompanion,
|
||||||
'switchuser': entrypoint.switchPlexUser
|
'switchuser': entrypoint.switchPlexUser,
|
||||||
|
'deviceid': entrypoint.resetDeviceId
|
||||||
}
|
}
|
||||||
|
|
||||||
if "extrafanart" in sys.argv[0]:
|
if "extrafanart" in sys.argv[0]:
|
||||||
|
|
|
@ -297,6 +297,7 @@
|
||||||
<string id="30532">Duration of the video library pop up (in seconds)</string>
|
<string id="30532">Duration of the video library pop up (in seconds)</string>
|
||||||
<string id="30533">Duration of the music library pop up (in seconds)</string>
|
<string id="30533">Duration of the music library pop up (in seconds)</string>
|
||||||
<string id="30534">Server messages</string>
|
<string id="30534">Server messages</string>
|
||||||
|
<string id="30535">Generate a new device Id</string>
|
||||||
|
|
||||||
<!-- service add-on -->
|
<!-- service add-on -->
|
||||||
<string id="33000">Welcome</string>
|
<string id="33000">Welcome</string>
|
||||||
|
@ -331,5 +332,7 @@
|
||||||
<string id="33029">Comparing tv shows from:</string>
|
<string id="33029">Comparing tv shows from:</string>
|
||||||
<string id="33030">Comparing episodes from:</string>
|
<string id="33030">Comparing episodes from:</string>
|
||||||
<string id="33031">Comparing:</string>
|
<string id="33031">Comparing:</string>
|
||||||
|
<string id="33032">Failed to generate a new device Id. See your logs for more information.</string>
|
||||||
|
<string id="33033">A new device Id has been generated. Kodi will now restart.</string>
|
||||||
|
|
||||||
</strings>
|
</strings>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import json
|
||||||
import requests
|
import requests
|
||||||
import os
|
import os
|
||||||
import urllib
|
import urllib
|
||||||
|
from sqlite3 import OperationalError
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
|
@ -442,13 +443,16 @@ class Artwork():
|
||||||
connection = utils.kodiSQL('texture')
|
connection = utils.kodiSQL('texture')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
cursor.execute("SELECT cachedurl FROM texture WHERE url = ?", (url,))
|
|
||||||
try:
|
try:
|
||||||
|
cursor.execute("SELECT cachedurl FROM texture WHERE url = ?", (url,))
|
||||||
cachedurl = cursor.fetchone()[0]
|
cachedurl = cursor.fetchone()[0]
|
||||||
|
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.logMsg("Could not find cached url.", 1)
|
self.logMsg("Could not find cached url.", 1)
|
||||||
|
|
||||||
|
except OperationalError:
|
||||||
|
self.logMsg("Database is locked. Skip deletion process.", 1)
|
||||||
|
|
||||||
else: # Delete thumbnail as well as the entry
|
else: # Delete thumbnail as well as the entry
|
||||||
thumbnails = xbmc.translatePath("special://thumbnails/%s" % cachedurl).decode('utf-8')
|
thumbnails = xbmc.translatePath("special://thumbnails/%s" % cachedurl).decode('utf-8')
|
||||||
self.logMsg("Deleting cached thumbnail: %s" % thumbnails, 1)
|
self.logMsg("Deleting cached thumbnail: %s" % thumbnails, 1)
|
||||||
|
@ -457,7 +461,7 @@ class Artwork():
|
||||||
try:
|
try:
|
||||||
cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
|
cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
|
||||||
connection.commit()
|
connection.commit()
|
||||||
except:
|
except OperationalError:
|
||||||
self.logMsg("Issue deleting url from cache. Skipping.", 2)
|
self.logMsg("Issue deleting url from cache. Skipping.", 2)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
|
|
@ -144,31 +144,32 @@ class DownloadUtils():
|
||||||
|
|
||||||
def startSession(self):
|
def startSession(self):
|
||||||
|
|
||||||
|
log = self.logMsg
|
||||||
|
|
||||||
self.deviceId = self.clientInfo.getDeviceId()
|
self.deviceId = self.clientInfo.getDeviceId()
|
||||||
|
|
||||||
# User is identified from this point
|
# User is identified from this point
|
||||||
# Attach authenticated header to the session
|
# Attach authenticated header to the session
|
||||||
verify = None
|
verify = False
|
||||||
cert = None
|
|
||||||
header = self.getHeader()
|
header = self.getHeader()
|
||||||
|
|
||||||
# If user enabled host certificate verification
|
# If user enabled host certificate verification
|
||||||
try:
|
try:
|
||||||
verify = self.sslverify
|
verify = self.sslverify
|
||||||
cert = self.sslclient
|
if self.sslclient is not None:
|
||||||
|
verify = self.sslclient
|
||||||
except:
|
except:
|
||||||
self.logMsg("Could not load SSL settings.", 1)
|
log("Could not load SSL settings.", 1)
|
||||||
|
|
||||||
# Start session
|
# Start session
|
||||||
self.s = requests.Session()
|
self.s = requests.Session()
|
||||||
self.s.headers = header
|
self.s.headers = header
|
||||||
self.s.verify = verify
|
self.s.verify = verify
|
||||||
self.s.cert = cert
|
|
||||||
# Retry connections to the server
|
# Retry connections to the server
|
||||||
self.s.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
|
self.s.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||||
self.s.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
|
self.s.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||||
|
|
||||||
self.logMsg("Requests session started on: %s" % self.server, 1)
|
log("Requests session started on: %s" % self.server, 1)
|
||||||
|
|
||||||
def stopSession(self):
|
def stopSession(self):
|
||||||
try:
|
try:
|
||||||
|
@ -232,7 +233,7 @@ class DownloadUtils():
|
||||||
if utils.settings('sslverify') == "true":
|
if utils.settings('sslverify') == "true":
|
||||||
verifyssl = True
|
verifyssl = True
|
||||||
if utils.settings('sslcert') != "None":
|
if utils.settings('sslcert') != "None":
|
||||||
cert = utils.settings('sslcert')
|
verifyssl = utils.settings('sslcert')
|
||||||
|
|
||||||
# Replace for the real values
|
# Replace for the real values
|
||||||
url = url.replace("{server}", self.server)
|
url = url.replace("{server}", self.server)
|
||||||
|
@ -245,7 +246,6 @@ class DownloadUtils():
|
||||||
params=parameters,
|
params=parameters,
|
||||||
headers=header,
|
headers=header,
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
cert=cert,
|
|
||||||
verify=verifyssl)
|
verify=verifyssl)
|
||||||
|
|
||||||
elif type == "POST":
|
elif type == "POST":
|
||||||
|
@ -253,7 +253,6 @@ class DownloadUtils():
|
||||||
json=postBody,
|
json=postBody,
|
||||||
headers=header,
|
headers=header,
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
cert=cert,
|
|
||||||
verify=verifyssl)
|
verify=verifyssl)
|
||||||
|
|
||||||
elif type == "DELETE":
|
elif type == "DELETE":
|
||||||
|
@ -261,7 +260,6 @@ class DownloadUtils():
|
||||||
json=postBody,
|
json=postBody,
|
||||||
headers=header,
|
headers=header,
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
cert=cert,
|
|
||||||
verify=verifyssl)
|
verify=verifyssl)
|
||||||
|
|
||||||
elif type == "OPTIONS":
|
elif type == "OPTIONS":
|
||||||
|
@ -287,6 +285,8 @@ class DownloadUtils():
|
||||||
# If user enables ssl verification
|
# If user enables ssl verification
|
||||||
try:
|
try:
|
||||||
verifyssl = self.sslverify
|
verifyssl = self.sslverify
|
||||||
|
if self.sslclient is not None:
|
||||||
|
verifyssl = self.sslclient
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
if utils.settings('sslverify') == "true":
|
if utils.settings('sslverify') == "true":
|
||||||
verifyssl = True
|
verifyssl = True
|
||||||
|
|
|
@ -143,6 +143,15 @@ class Embydb_Functions():
|
||||||
))
|
))
|
||||||
self.embycursor.execute(query, (name, tagid, mediafolderid))
|
self.embycursor.execute(query, (name, tagid, mediafolderid))
|
||||||
|
|
||||||
|
def removeView(self, viewid):
|
||||||
|
|
||||||
|
query = ' '.join((
|
||||||
|
|
||||||
|
"DELETE FROM view",
|
||||||
|
"WHERE view_id = ?"
|
||||||
|
))
|
||||||
|
self.embycursor.execute(query, (viewid,))
|
||||||
|
|
||||||
def getItem_byId(self, embyid):
|
def getItem_byId(self, embyid):
|
||||||
|
|
||||||
embycursor = self.embycursor
|
embycursor = self.embycursor
|
||||||
|
|
|
@ -203,6 +203,30 @@ def doMainListing():
|
||||||
|
|
||||||
xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
||||||
|
|
||||||
|
|
||||||
|
##### Generate a new deviceId
|
||||||
|
def resetDeviceId():
|
||||||
|
|
||||||
|
dialog = xbmcgui.Dialog()
|
||||||
|
language = utils.language
|
||||||
|
|
||||||
|
deviceId_old = utils.window('emby_deviceId')
|
||||||
|
try:
|
||||||
|
utils.window('emby_deviceId', clear=True)
|
||||||
|
deviceId = clientinfo.ClientInfo().getDeviceId(reset=True)
|
||||||
|
except Exception as e:
|
||||||
|
utils.logMsg("EMBY", "Failed to generate a new device Id: %s" % e, 1)
|
||||||
|
dialog.ok(
|
||||||
|
heading="Emby for Kodi",
|
||||||
|
line1=language(33032))
|
||||||
|
else:
|
||||||
|
utils.logMsg("EMBY", "Successfully removed old deviceId: %s New deviceId: %s"
|
||||||
|
% (deviceId_old, deviceId), 1)
|
||||||
|
dialog.ok(
|
||||||
|
heading="Emby for Kodi",
|
||||||
|
line1=language(33033))
|
||||||
|
xbmc.executebuiltin('RestartApp')
|
||||||
|
|
||||||
##### ADD ADDITIONAL USERS #####
|
##### ADD ADDITIONAL USERS #####
|
||||||
def addUser():
|
def addUser():
|
||||||
|
|
||||||
|
|
|
@ -1191,12 +1191,12 @@ class TVShows(Items):
|
||||||
season = -1
|
season = -1
|
||||||
|
|
||||||
# Specials ordering within season
|
# Specials ordering within season
|
||||||
# if item.get('AirsAfterSeasonNumber'):
|
if item.get('AirsAfterSeasonNumber'):
|
||||||
# airsBeforeSeason = item['AirsAfterSeasonNumber']
|
airsBeforeSeason = item['AirsAfterSeasonNumber']
|
||||||
# airsBeforeEpisode = 4096 # Kodi default number for afterseason ordering
|
airsBeforeEpisode = 4096 # Kodi default number for afterseason ordering
|
||||||
# else:
|
else:
|
||||||
# airsBeforeSeason = item.get('AirsBeforeSeasonNumber', "-1")
|
airsBeforeSeason = item.get('AirsBeforeSeasonNumber')
|
||||||
# airsBeforeEpisode = item.get('AirsBeforeEpisodeNumber', "-1")
|
airsBeforeEpisode = item.get('AirsBeforeEpisodeNumber')
|
||||||
|
|
||||||
airsBeforeSeason = "-1"
|
airsBeforeSeason = "-1"
|
||||||
airsBeforeEpisode = "-1"
|
airsBeforeEpisode = "-1"
|
||||||
|
|
|
@ -38,7 +38,9 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
|
|
||||||
def onSettingsChanged(self):
|
def onSettingsChanged(self):
|
||||||
# Monitor emby settings
|
# Monitor emby settings
|
||||||
currentPath = utils.settings('useDirectPaths')
|
# Review reset setting at a later time, need to be adjusted to account for initial setup
|
||||||
|
# changes.
|
||||||
|
'''currentPath = utils.settings('useDirectPaths')
|
||||||
if utils.window('emby_pluginpath') != currentPath:
|
if utils.window('emby_pluginpath') != currentPath:
|
||||||
# Plugin path value changed. Offer to reset
|
# Plugin path value changed. Offer to reset
|
||||||
self.logMsg("Changed to playback mode detected", 1)
|
self.logMsg("Changed to playback mode detected", 1)
|
||||||
|
@ -50,7 +52,7 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
"needs to be recreated for the change to be applied. "
|
"needs to be recreated for the change to be applied. "
|
||||||
"Proceed?"))
|
"Proceed?"))
|
||||||
if resp:
|
if resp:
|
||||||
utils.reset()
|
utils.reset()'''
|
||||||
|
|
||||||
currentLog = utils.settings('logLevel')
|
currentLog = utils.settings('logLevel')
|
||||||
if utils.window('emby_logLevel') != currentLog:
|
if utils.window('emby_logLevel') != currentLog:
|
||||||
|
|
|
@ -398,7 +398,6 @@ class LibrarySync(Thread):
|
||||||
xbmc.executebuiltin('UpdateLibrary(video)')
|
xbmc.executebuiltin('UpdateLibrary(video)')
|
||||||
if self.enableMusic:
|
if self.enableMusic:
|
||||||
xbmc.executebuiltin('UpdateLibrary(music)')
|
xbmc.executebuiltin('UpdateLibrary(music)')
|
||||||
|
|
||||||
elapsedtotal = datetime.now() - starttotal
|
elapsedtotal = datetime.now() - starttotal
|
||||||
utils.window('emby_initialScan', clear=True)
|
utils.window('emby_initialScan', clear=True)
|
||||||
self.showKodiNote("%s completed in: %s"
|
self.showKodiNote("%s completed in: %s"
|
||||||
|
@ -522,29 +521,6 @@ class LibrarySync(Thread):
|
||||||
vnodes.clearProperties()
|
vnodes.clearProperties()
|
||||||
totalnodes = 0
|
totalnodes = 0
|
||||||
|
|
||||||
with embydb.GetEmbyDB() as emby_db:
|
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
|
||||||
for folderItem in result:
|
|
||||||
self.processView(folderItem, kodi_db, emby_db, totalnodes)
|
|
||||||
else:
|
|
||||||
# Add video nodes listings
|
|
||||||
vnodes.singleNode(totalnodes,
|
|
||||||
"Favorite movies",
|
|
||||||
"movies",
|
|
||||||
"favourites")
|
|
||||||
totalnodes += 1
|
|
||||||
vnodes.singleNode(totalnodes,
|
|
||||||
"Favorite tvshows",
|
|
||||||
"tvshows",
|
|
||||||
"favourites")
|
|
||||||
totalnodes += 1
|
|
||||||
vnodes.singleNode(totalnodes,
|
|
||||||
"channels",
|
|
||||||
"movies",
|
|
||||||
"channels")
|
|
||||||
totalnodes += 1
|
|
||||||
# Save total
|
|
||||||
utils.window('Emby.nodes.total', str(totalnodes))
|
|
||||||
|
|
||||||
# update views for all:
|
# update views for all:
|
||||||
self.views = emby_db.getAllViewInfo()
|
self.views = emby_db.getAllViewInfo()
|
||||||
|
@ -558,6 +534,8 @@ class LibrarySync(Thread):
|
||||||
'itemtype': 'artist'
|
'itemtype': 'artist'
|
||||||
}
|
}
|
||||||
self.views.append(entry)
|
self.views.append(entry)
|
||||||
|
nodes = [] # Prevent duplicate for nodes of the same type
|
||||||
|
playlists = [] # Prevent duplicate for playlists of the same type
|
||||||
|
|
||||||
self.logMsg("views saved: %s" % self.views, 1)
|
self.logMsg("views saved: %s" % self.views, 1)
|
||||||
|
|
||||||
|
@ -645,6 +623,17 @@ class LibrarySync(Thread):
|
||||||
itemNumber = len(self.updatelist)
|
itemNumber = len(self.updatelist)
|
||||||
if itemNumber == 0:
|
if itemNumber == 0:
|
||||||
return True
|
return True
|
||||||
|
# Validate the playlist exists or recreate it
|
||||||
|
if (foldername not in playlists and
|
||||||
|
mediatype in ('movies', 'tvshows', 'musicvideos')):
|
||||||
|
utils.playlistXSP(mediatype, foldername, folderid, viewtype)
|
||||||
|
playlists.append(foldername)
|
||||||
|
if foldername not in nodes and mediatype != "musicvideos":
|
||||||
|
vnodes.viewNode(sorted_views.index(foldername), foldername,
|
||||||
|
mediatype, viewtype, folderid)
|
||||||
|
if viewtype == "mixed": # Change the value
|
||||||
|
sorted_views[sorted_views.index(foldername)] = "%ss" % foldername
|
||||||
|
nodes.append(foldername)
|
||||||
|
|
||||||
# Run through self.updatelist, get XML metadata per item
|
# Run through self.updatelist, get XML metadata per item
|
||||||
# Initiate threads
|
# Initiate threads
|
||||||
|
@ -693,6 +682,10 @@ class LibrarySync(Thread):
|
||||||
thread.start()
|
thread.start()
|
||||||
threads.append(thread)
|
threads.append(thread)
|
||||||
self.logMsg("Kodi Infobox thread spawned", 1)
|
self.logMsg("Kodi Infobox thread spawned", 1)
|
||||||
|
# Remove any old referenced views
|
||||||
|
log("Removing views: %s" % current_views, 1)
|
||||||
|
for view in current_views:
|
||||||
|
emby_db.removeView(view)
|
||||||
|
|
||||||
# Wait until finished
|
# Wait until finished
|
||||||
getMetadataQueue.join()
|
getMetadataQueue.join()
|
||||||
|
|
|
@ -274,6 +274,16 @@ class PlaybackUtils():
|
||||||
else:
|
else:
|
||||||
window('%s.refreshid' % embyitem, value=itemid)
|
window('%s.refreshid' % embyitem, value=itemid)
|
||||||
|
|
||||||
|
# Append external subtitles to stream
|
||||||
|
playmethod = utils.window('%s.playmethod' % embyitem)
|
||||||
|
# Only for direct stream
|
||||||
|
if playmethod in ("DirectStream"):
|
||||||
|
# Direct play automatically appends external
|
||||||
|
subtitles = self.externalSubs(playurl)
|
||||||
|
listitem.setSubtitles(subtitles)
|
||||||
|
|
||||||
|
self.setArtwork(listitem)
|
||||||
|
|
||||||
def externalSubs(self, playurl):
|
def externalSubs(self, playurl):
|
||||||
|
|
||||||
externalsubs = []
|
externalsubs = []
|
||||||
|
|
|
@ -414,7 +414,7 @@ class Player(xbmc.Player):
|
||||||
self.doUtils(
|
self.doUtils(
|
||||||
"{server}/:/timeline?" + urlencode(postdata), type="GET")
|
"{server}/:/timeline?" + urlencode(postdata), type="GET")
|
||||||
|
|
||||||
def onPlayBackPaused( self ):
|
def onPlayBackPaused(self):
|
||||||
|
|
||||||
currentFile = self.currentFile
|
currentFile = self.currentFile
|
||||||
self.logMsg("PLAYBACK_PAUSED: %s" % currentFile, 2)
|
self.logMsg("PLAYBACK_PAUSED: %s" % currentFile, 2)
|
||||||
|
@ -424,7 +424,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
self.reportPlayback()
|
self.reportPlayback()
|
||||||
|
|
||||||
def onPlayBackResumed( self ):
|
def onPlayBackResumed(self):
|
||||||
|
|
||||||
currentFile = self.currentFile
|
currentFile = self.currentFile
|
||||||
self.logMsg("PLAYBACK_RESUMED: %s" % currentFile, 2)
|
self.logMsg("PLAYBACK_RESUMED: %s" % currentFile, 2)
|
||||||
|
@ -434,7 +434,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
self.reportPlayback()
|
self.reportPlayback()
|
||||||
|
|
||||||
def onPlayBackSeek( self, time, seekOffset ):
|
def onPlayBackSeek(self, time, seekOffset):
|
||||||
# Make position when seeking a bit more accurate
|
# Make position when seeking a bit more accurate
|
||||||
currentFile = self.currentFile
|
currentFile = self.currentFile
|
||||||
self.logMsg("PLAYBACK_SEEK: %s" % currentFile, 2)
|
self.logMsg("PLAYBACK_SEEK: %s" % currentFile, 2)
|
||||||
|
@ -445,7 +445,7 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
self.reportPlayback()
|
self.reportPlayback()
|
||||||
|
|
||||||
def onPlayBackStopped( self ):
|
def onPlayBackStopped(self):
|
||||||
|
|
||||||
log = self.logMsg
|
log = self.logMsg
|
||||||
window = utils.window
|
window = utils.window
|
||||||
|
@ -458,7 +458,7 @@ class Player(xbmc.Player):
|
||||||
log("Clear playlist properties.", 1)
|
log("Clear playlist properties.", 1)
|
||||||
self.stopAll()
|
self.stopAll()
|
||||||
|
|
||||||
def onPlayBackEnded( self ):
|
def onPlayBackEnded(self):
|
||||||
# Will be called when xbmc stops playing a file
|
# Will be called when xbmc stops playing a file
|
||||||
self.logMsg("ONPLAYBACK_ENDED", 2)
|
self.logMsg("ONPLAYBACK_ENDED", 2)
|
||||||
utils.window('emby_customPlaylist.seektime', clear=True)
|
utils.window('emby_customPlaylist.seektime', clear=True)
|
||||||
|
@ -494,6 +494,9 @@ class Player(xbmc.Player):
|
||||||
type = data['Type']
|
type = data['Type']
|
||||||
playMethod = data['playmethod']
|
playMethod = data['playmethod']
|
||||||
|
|
||||||
|
# Prevent manually mark as watched in Kodi monitor
|
||||||
|
utils.window('emby_skipWatched%s' % itemid, value="true")
|
||||||
|
|
||||||
if currentPosition and runtime:
|
if currentPosition and runtime:
|
||||||
try:
|
try:
|
||||||
percentComplete = currentPosition / int(runtime)
|
percentComplete = currentPosition / int(runtime)
|
||||||
|
@ -505,16 +508,6 @@ class Player(xbmc.Player):
|
||||||
log("Percent complete: %s Mark played at: %s"
|
log("Percent complete: %s Mark played at: %s"
|
||||||
% (percentComplete, markPlayedAt), 1)
|
% (percentComplete, markPlayedAt), 1)
|
||||||
|
|
||||||
# Prevent manually mark as watched in Kodi monitor
|
|
||||||
utils.window('emby_skipWatched%s' % itemid, value="true")
|
|
||||||
|
|
||||||
self.stopPlayback(data)
|
|
||||||
# Stop transcoding
|
|
||||||
if playMethod == "Transcode":
|
|
||||||
log("Transcoding for %s terminated." % itemid, 1)
|
|
||||||
url = "{server}/video/:/transcode/universal/stop?session=%s" % self.clientInfo.getDeviceId()
|
|
||||||
doUtils(url, type="GET")
|
|
||||||
|
|
||||||
# Send the delete action to the server.
|
# Send the delete action to the server.
|
||||||
offerDelete = False
|
offerDelete = False
|
||||||
|
|
||||||
|
@ -553,6 +546,14 @@ class Player(xbmc.Player):
|
||||||
)
|
)
|
||||||
for item in cleanup:
|
for item in cleanup:
|
||||||
utils.window(item, clear=True)
|
utils.window(item, clear=True)
|
||||||
|
self.stopPlayback(data)
|
||||||
|
|
||||||
|
# Stop transcoding
|
||||||
|
if playMethod == "Transcode":
|
||||||
|
log("Transcoding for %s terminated." % itemid, 1)
|
||||||
|
deviceId = self.clientInfo.getDeviceId()
|
||||||
|
url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId
|
||||||
|
doUtils(url, type="DELETE")
|
||||||
|
|
||||||
self.played_info.clear()
|
self.played_info.clear()
|
||||||
|
|
||||||
|
|
|
@ -294,11 +294,11 @@ class Read_EmbyServer():
|
||||||
log("Increase jump limit to: %s" % jump, 1)
|
log("Increase jump limit to: %s" % jump, 1)
|
||||||
return items
|
return items
|
||||||
|
|
||||||
def getViews(self, type, root=False):
|
def getViews(self, mediatype="", root=False, sortedlist=False):
|
||||||
# Build a list of user views
|
# Build a list of user views
|
||||||
doUtils = self.doUtils
|
doUtils = self.doUtils
|
||||||
views = []
|
views = []
|
||||||
type = type.lower()
|
mediatype = mediatype.lower()
|
||||||
|
|
||||||
if not root:
|
if not root:
|
||||||
url = "{server}/emby/Users/{UserId}/Views?format=json"
|
url = "{server}/emby/Users/{UserId}/Views?format=json"
|
||||||
|
@ -308,10 +308,8 @@ class Read_EmbyServer():
|
||||||
result = doUtils(url)
|
result = doUtils(url)
|
||||||
try:
|
try:
|
||||||
items = result['Items']
|
items = result['Items']
|
||||||
|
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.logMsg("Error retrieving views for type: %s" % type, 2)
|
self.logMsg("Error retrieving views for type: %s" % mediatype, 2)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for item in items:
|
for item in items:
|
||||||
|
|
||||||
|
@ -336,15 +334,25 @@ class Read_EmbyServer():
|
||||||
if itemId == folder['Id']:
|
if itemId == folder['Id']:
|
||||||
itemtype = folder.get('CollectionType', "mixed")
|
itemtype = folder.get('CollectionType', "mixed")
|
||||||
|
|
||||||
if (name not in ('Collections', 'Trailers') and (itemtype == type or
|
if name not in ('Collections', 'Trailers'):
|
||||||
(itemtype == "mixed" and type in ("movies", "tvshows")))):
|
|
||||||
|
|
||||||
views.append({
|
if sortedlist:
|
||||||
|
views.append({
|
||||||
|
|
||||||
'name': name,
|
'name': name,
|
||||||
'type': itemtype,
|
'type': itemtype,
|
||||||
'id': itemId
|
'id': itemId
|
||||||
})
|
})
|
||||||
|
|
||||||
|
elif (itemtype == mediatype or
|
||||||
|
(itemtype == "mixed" and mediatype in ("movies", "tvshows"))):
|
||||||
|
|
||||||
|
views.append({
|
||||||
|
|
||||||
|
'name': name,
|
||||||
|
'type': itemtype,
|
||||||
|
'id': itemId
|
||||||
|
})
|
||||||
|
|
||||||
return views
|
return views
|
||||||
|
|
||||||
|
|
|
@ -296,7 +296,7 @@ def reset():
|
||||||
deleteNodes()
|
deleteNodes()
|
||||||
|
|
||||||
# Wipe the kodi databases
|
# Wipe the kodi databases
|
||||||
logMsg("EMBY", "Resetting the Kodi video database.")
|
logMsg("EMBY", "Resetting the Kodi video database.", 0)
|
||||||
connection = kodiSQL('video')
|
connection = kodiSQL('video')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||||
|
@ -322,7 +322,7 @@ def reset():
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
||||||
# Wipe the emby database
|
# Wipe the emby database
|
||||||
logMsg("EMBY", "Resetting the Emby database.")
|
logMsg("EMBY", "Resetting the Emby database.", 0)
|
||||||
connection = kodiSQL('emby')
|
connection = kodiSQL('emby')
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||||
|
@ -331,15 +331,45 @@ def reset():
|
||||||
tablename = row[0]
|
tablename = row[0]
|
||||||
if tablename != "version":
|
if tablename != "version":
|
||||||
cursor.execute("DELETE FROM " + tablename)
|
cursor.execute("DELETE FROM " + tablename)
|
||||||
|
cursor.execute('DROP table IF EXISTS emby')
|
||||||
|
cursor.execute('DROP table IF EXISTS view')
|
||||||
connection.commit()
|
connection.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
||||||
|
# Offer to wipe cached thumbnails
|
||||||
|
resp = dialog.yesno("Warning", "Removed all cached artwork?")
|
||||||
|
if resp:
|
||||||
|
logMsg("EMBY", "Resetting all cached artwork.", 0)
|
||||||
|
# Remove all existing textures first
|
||||||
|
path = xbmc.translatePath("special://thumbnails/").decode('utf-8')
|
||||||
|
if xbmcvfs.exists(path):
|
||||||
|
allDirs, allFiles = xbmcvfs.listdir(path)
|
||||||
|
for dir in allDirs:
|
||||||
|
allDirs, allFiles = xbmcvfs.listdir(path+dir)
|
||||||
|
for file in allFiles:
|
||||||
|
if os.path.supports_unicode_filenames:
|
||||||
|
xbmcvfs.delete(os.path.join(path+dir.decode('utf-8'),file.decode('utf-8')))
|
||||||
|
else:
|
||||||
|
xbmcvfs.delete(os.path.join(path.encode('utf-8')+dir,file))
|
||||||
|
|
||||||
|
# remove all existing data from texture DB
|
||||||
|
connection = kodiSQL('texture')
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
for row in rows:
|
||||||
|
tableName = row[0]
|
||||||
|
if(tableName != "version"):
|
||||||
|
cursor.execute("DELETE FROM " + tableName)
|
||||||
|
connection.commit()
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
# reset the install run flag
|
# reset the install run flag
|
||||||
settings('SyncInstallRunDone', value="false")
|
settings('SyncInstallRunDone', value="false")
|
||||||
|
|
||||||
# Remove emby info
|
# Remove emby info
|
||||||
resp = dialog.yesno("Warning", "Reset all Emby Addon settings?")
|
resp = dialog.yesno("Warning", "Reset all Emby Addon settings?")
|
||||||
if resp == 1:
|
if resp:
|
||||||
# Delete the settings
|
# Delete the settings
|
||||||
addon = xbmcaddon.Addon()
|
addon = xbmcaddon.Addon()
|
||||||
addondir = xbmc.translatePath(addon.getAddonInfo('profile')).decode('utf-8')
|
addondir = xbmc.translatePath(addon.getAddonInfo('profile')).decode('utf-8')
|
||||||
|
@ -601,17 +631,17 @@ def passwordsXML():
|
||||||
time=1000,
|
time=1000,
|
||||||
sound=False)
|
sound=False)
|
||||||
|
|
||||||
def playlistXSP(mediatype, tagname, viewtype="", delete=False):
|
def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
|
||||||
# Tagname is in unicode - actions: add or delete
|
# Tagname is in unicode - actions: add or delete
|
||||||
tagname = tagname.encode('utf-8')
|
tagname = tagname.encode('utf-8')
|
||||||
cleantagname = normalize_nodes(tagname)
|
|
||||||
path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8')
|
||||||
if viewtype == "mixed":
|
if viewtype == "mixed":
|
||||||
plname = "%s - %s" % (tagname, mediatype)
|
plname = "%s - %s" % (tagname, mediatype)
|
||||||
xsppath = "%sEmby %s - %s.xsp" % (path, cleantagname, mediatype)
|
xsppath = "%sEmby %s - %s.xsp" % (path, viewid, mediatype)
|
||||||
else:
|
else:
|
||||||
plname = tagname
|
plname = tagname
|
||||||
xsppath = "%sEmby %s.xsp" % (path, cleantagname)
|
xsppath = "%sEmby %s.xsp" % (path, viewid)
|
||||||
|
|
||||||
# Create the playlist directory
|
# Create the playlist directory
|
||||||
if not xbmcvfs.exists(path):
|
if not xbmcvfs.exists(path):
|
||||||
|
@ -668,7 +698,13 @@ def deleteNodes():
|
||||||
dirs, files = xbmcvfs.listdir(path)
|
dirs, files = xbmcvfs.listdir(path)
|
||||||
for dir in dirs:
|
for dir in dirs:
|
||||||
if dir.decode('utf-8').startswith('Emby'):
|
if dir.decode('utf-8').startswith('Emby'):
|
||||||
shutil.rmtree("%s%s" % (path, dir.decode('utf-8')))
|
try:
|
||||||
|
shutil.rmtree("%s%s" % (path, dir.decode('utf-8')))
|
||||||
|
except:
|
||||||
|
logMsg("EMBY", "Failed to delete directory: %s" % dir.decode('utf-8'))
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.decode('utf-8').startswith('emby'):
|
if file.decode('utf-8').startswith('emby'):
|
||||||
xbmcvfs.delete("%s%s" % (path, file.decode('utf-8')))
|
try:
|
||||||
|
xbmcvfs.delete("%s%s" % (path, file.decode('utf-8')))
|
||||||
|
except:
|
||||||
|
logMsg("EMBY", "Failed to file: %s" % file.decode('utf-8'))
|
|
@ -40,16 +40,15 @@ class VideoNodes(object):
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def viewNode(self, indexnumber, tagname, mediatype, viewtype, delete=False):
|
def viewNode(self, indexnumber, tagname, mediatype, viewtype, viewid, delete=False):
|
||||||
|
|
||||||
window = utils.window
|
window = utils.window
|
||||||
kodiversion = self.kodiversion
|
kodiversion = self.kodiversion
|
||||||
|
|
||||||
cleantagname = utils.normalize_nodes(tagname.encode('utf-8'))
|
|
||||||
if viewtype == "mixed":
|
if viewtype == "mixed":
|
||||||
dirname = "%s - %s" % (cleantagname, mediatype)
|
dirname = "%s - %s" % (viewid, mediatype)
|
||||||
else:
|
else:
|
||||||
dirname = cleantagname
|
dirname = viewid
|
||||||
|
|
||||||
path = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
|
path = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
|
||||||
nodepath = xbmc.translatePath(
|
nodepath = xbmc.translatePath(
|
||||||
|
@ -91,7 +90,11 @@ class VideoNodes(object):
|
||||||
|
|
||||||
# Root
|
# Root
|
||||||
if not mediatype == "photos":
|
if not mediatype == "photos":
|
||||||
root = self.commonRoot(order=0, label=tagname, tagname=tagname, roottype=0)
|
if viewtype == "mixed":
|
||||||
|
specialtag = "%s - %s" % (tagname, mediatype)
|
||||||
|
root = self.commonRoot(order=0, label=specialtag, tagname=tagname, roottype=0)
|
||||||
|
else:
|
||||||
|
root = self.commonRoot(order=0, label=tagname, tagname=tagname, roottype=0)
|
||||||
try:
|
try:
|
||||||
utils.indent(root)
|
utils.indent(root)
|
||||||
except: pass
|
except: pass
|
||||||
|
@ -166,7 +169,7 @@ class VideoNodes(object):
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
|
|
||||||
nodetype = nodetypes[node]
|
nodetype = nodetypes[node]
|
||||||
nodeXML = "%s%s_%s.xml" % (nodepath, cleantagname, nodetype)
|
nodeXML = "%s%s_%s.xml" % (nodepath, viewid, nodetype)
|
||||||
# Get label
|
# Get label
|
||||||
stringid = nodes[node]
|
stringid = nodes[node]
|
||||||
if node != "1":
|
if node != "1":
|
||||||
|
@ -193,7 +196,7 @@ class VideoNodes(object):
|
||||||
# Custom query
|
# Custom query
|
||||||
path = "plugin://plugin.video.plexkodiconnect/?id=%s&mode=inprogressepisodes&limit=25"% tagname
|
path = "plugin://plugin.video.plexkodiconnect/?id=%s&mode=inprogressepisodes&limit=25"% tagname
|
||||||
else:
|
else:
|
||||||
path = "library://video/plex%s/%s_%s.xml" % (dirname, cleantagname, nodetype)
|
path = "library://video/plex%s/%s_%s.xml" % (dirname, viewid, nodetype)
|
||||||
|
|
||||||
if mediatype == "photos":
|
if mediatype == "photos":
|
||||||
windowpath = "ActivateWindow(Pictures,%s,return)" % path
|
windowpath = "ActivateWindow(Pictures,%s,return)" % path
|
||||||
|
@ -203,7 +206,7 @@ class VideoNodes(object):
|
||||||
if nodetype == "all":
|
if nodetype == "all":
|
||||||
|
|
||||||
if viewtype == "mixed":
|
if viewtype == "mixed":
|
||||||
templabel = dirname
|
templabel = "%s - %s" % (tagname, mediatype)
|
||||||
else:
|
else:
|
||||||
templabel = label
|
templabel = label
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue