Merge remote-tracking branch 'MediaBrowser/master' into develop
This commit is contained in:
commit
450437b812
18 changed files with 1428 additions and 1622 deletions
|
@ -147,7 +147,7 @@ if __name__ == '__main__':
|
|||
doUtils = downloadutils.DownloadUtils()
|
||||
url = "{server}/emby/Items/%s?format=json" % embyid
|
||||
logMsg("Deleting request: %s" % embyid, 0)
|
||||
doUtils.downloadUrl(url, type="DELETE")
|
||||
doUtils.downloadUrl(url, action_type="DELETE")
|
||||
|
||||
'''if utils.settings('skipContextMenu') != "true":
|
||||
if xbmcgui.Dialog().yesno(
|
||||
|
@ -156,8 +156,7 @@ if __name__ == '__main__':
|
|||
"also delete the file(s) from disk!")):
|
||||
import downloadutils
|
||||
doUtils = downloadutils.DownloadUtils()
|
||||
url = "{server}/emby/Items/%s?format=json" % embyid
|
||||
doUtils.downloadUrl(url, type="DELETE")'''
|
||||
doUtils.downloadUrl("{server}/emby/Items/%s?format=json" % embyid, action_type="DELETE")'''
|
||||
|
||||
xbmc.sleep(500)
|
||||
xbmc.executebuiltin("Container.Update")
|
|
@ -328,7 +328,7 @@
|
|||
<string id="33020">Gathering tv shows from:</string>
|
||||
<string id="33021">Gathering:</string>
|
||||
<string id="33022">Detected the database needs to be recreated for this version of Emby for Kodi. Proceed?</string>
|
||||
<string id="33023">Emby for Kod may not work correctly until the database is reset.</string>
|
||||
<string id="33023">Emby for Kodi may not work correctly until the database is reset.</string>
|
||||
<string id="33024">Cancelling the database syncing process. The current Kodi version is unsupported.</string>
|
||||
<string id="33025">completed in:</string>
|
||||
<string id="33026">Comparing movies from:</string>
|
||||
|
|
|
@ -36,7 +36,7 @@ class Artwork():
|
|||
|
||||
self.enableTextureCache = utils.settings('enableTextureCache') == "true"
|
||||
self.imageCacheLimitThreads = int(utils.settings("imageCacheLimit"))
|
||||
self.imageCacheLimitThreads = int(self.imageCacheLimitThreads * 5);
|
||||
self.imageCacheLimitThreads = int(self.imageCacheLimitThreads * 5)
|
||||
utils.logMsg("Using Image Cache Thread Count: " + str(self.imageCacheLimitThreads), 1)
|
||||
|
||||
if not self.xbmc_port and self.enableTextureCache:
|
||||
|
@ -74,7 +74,7 @@ class Artwork():
|
|||
result = json.loads(result)
|
||||
try:
|
||||
xbmc_webserver_enabled = result['result']['value']
|
||||
except KeyError, TypeError:
|
||||
except (KeyError, TypeError):
|
||||
xbmc_webserver_enabled = False
|
||||
|
||||
if not xbmc_webserver_enabled:
|
||||
|
@ -394,6 +394,8 @@ class Artwork():
|
|||
|
||||
except TypeError: # Add the artwork
|
||||
cacheimage = True
|
||||
self.logMsg("Adding Art Link for kodiId: %s (%s)" % (kodiId, imageUrl), 2)
|
||||
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO art(media_id, media_type, type, url)
|
||||
|
@ -413,6 +415,10 @@ class Artwork():
|
|||
# Delete current entry before updating with the new one
|
||||
self.deleteCachedArtwork(url)
|
||||
|
||||
self.logMsg(
|
||||
"Updating Art url for %s kodiId: %s (%s) -> (%s)"
|
||||
% (imageType, kodiId, url, imageUrl), 1)
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE art",
|
||||
|
@ -501,8 +507,6 @@ class Artwork():
|
|||
|
||||
def getAllArtwork(self, item, parentInfo=False):
|
||||
|
||||
server = self.server
|
||||
|
||||
itemid = item['Id']
|
||||
artworks = item['ImageTags']
|
||||
backdrops = item.get('BackdropImageTags',[])
|
||||
|
@ -533,7 +537,7 @@ def getAllArtwork(self, item, parentInfo=False):
|
|||
artwork = (
|
||||
"%s/emby/Items/%s/Images/Backdrop/%s?"
|
||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||
% (server, itemid, index, maxWidth, maxHeight, tag, customquery))
|
||||
% (self.server, itemid, index, maxWidth, maxHeight, tag, customquery))
|
||||
allartworks['Backdrop'].append(artwork)
|
||||
|
||||
# Process the rest of the artwork
|
||||
|
@ -544,7 +548,7 @@ def getAllArtwork(self, item, parentInfo=False):
|
|||
artwork = (
|
||||
"%s/emby/Items/%s/Images/%s/0?"
|
||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||
% (server, itemid, art, maxWidth, maxHeight, tag, customquery))
|
||||
% (self.server, itemid, art, maxWidth, maxHeight, tag, customquery))
|
||||
allartworks[art] = artwork
|
||||
|
||||
# Process parent items if the main item is missing artwork
|
||||
|
@ -562,7 +566,7 @@ def getAllArtwork(self, item, parentInfo=False):
|
|||
artwork = (
|
||||
"%s/emby/Items/%s/Images/Backdrop/%s?"
|
||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||
% (server, parentId, index, maxWidth, maxHeight, tag, customquery))
|
||||
% (self.server, parentId, index, maxWidth, maxHeight, tag, customquery))
|
||||
allartworks['Backdrop'].append(artwork)
|
||||
|
||||
# Process the rest of the artwork
|
||||
|
@ -578,7 +582,7 @@ def getAllArtwork(self, item, parentInfo=False):
|
|||
artwork = (
|
||||
"%s/emby/Items/%s/Images/%s/0?"
|
||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||
% (server, parentId, parentart,
|
||||
% (self.server, parentId, parentart,
|
||||
maxWidth, maxHeight, parentTag, customquery))
|
||||
allartworks[parentart] = artwork
|
||||
|
||||
|
@ -592,7 +596,7 @@ def getAllArtwork(self, item, parentInfo=False):
|
|||
artwork = (
|
||||
"%s/emby/Items/%s/Images/Primary/0?"
|
||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||
% (server, parentId, maxWidth, maxHeight, parentTag, customquery))
|
||||
% (self.server, parentId, maxWidth, maxHeight, parentTag, customquery))
|
||||
allartworks['Primary'] = artwork
|
||||
|
||||
return allartworks
|
|
@ -60,8 +60,6 @@ class ConnectUtils():
|
|||
|
||||
def startSession(self):
|
||||
|
||||
log = self.logMsg
|
||||
|
||||
self.deviceId = self.clientInfo.getDeviceId()
|
||||
|
||||
# User is identified from this point
|
||||
|
@ -75,7 +73,7 @@ class ConnectUtils():
|
|||
if self.sslclient is not None:
|
||||
verify = self.sslclient
|
||||
except:
|
||||
log("Could not load SSL settings.", 1)
|
||||
self.logMsg("Could not load SSL settings.", 1)
|
||||
|
||||
# Start session
|
||||
self.c = requests.Session()
|
||||
|
@ -85,7 +83,7 @@ class ConnectUtils():
|
|||
self.c.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||
self.c.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||
|
||||
log("Requests session started on: %s" % self.server, 1)
|
||||
self.logMsg("Requests session started on: %s" % self.server, 1)
|
||||
|
||||
def stopSession(self):
|
||||
try:
|
||||
|
@ -95,8 +93,7 @@ class ConnectUtils():
|
|||
|
||||
def getHeader(self, authenticate=True):
|
||||
|
||||
clientInfo = self.clientInfo
|
||||
version = clientInfo.getVersion()
|
||||
version = self.clientInfo.getVersion()
|
||||
|
||||
if not authenticate:
|
||||
# If user is not authenticated
|
||||
|
@ -125,10 +122,9 @@ class ConnectUtils():
|
|||
def doUrl(self, url, data=None, postBody=None, rtype="GET",
|
||||
parameters=None, authenticate=True, timeout=None):
|
||||
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
|
||||
log("=== ENTER connectUrl ===", 2)
|
||||
self.logMsg("=== ENTER connectUrl ===", 2)
|
||||
default_link = ""
|
||||
if timeout is None:
|
||||
timeout = self.timeout
|
||||
|
@ -213,25 +209,25 @@ class ConnectUtils():
|
|||
verify=verifyssl)
|
||||
|
||||
##### THE RESPONSE #####
|
||||
log(r.url, 1)
|
||||
log(r, 1)
|
||||
self.logMsg(r.url, 1)
|
||||
self.logMsg(r, 1)
|
||||
|
||||
if r.status_code == 204:
|
||||
# No body in the response
|
||||
log("====== 204 Success ======", 1)
|
||||
self.logMsg("====== 204 Success ======", 1)
|
||||
|
||||
elif r.status_code == requests.codes.ok:
|
||||
|
||||
try:
|
||||
# UNICODE - JSON object
|
||||
r = r.json()
|
||||
log("====== 200 Success ======", 1)
|
||||
log("Response: %s" % r, 1)
|
||||
self.logMsg("====== 200 Success ======", 1)
|
||||
self.logMsg("Response: %s" % r, 1)
|
||||
return r
|
||||
|
||||
except:
|
||||
if r.headers.get('content-type') != "text/html":
|
||||
log("Unable to convert the response for: %s" % url, 1)
|
||||
self.logMsg("Unable to convert the response for: %s" % url, 1)
|
||||
else:
|
||||
r.raise_for_status()
|
||||
|
||||
|
@ -242,8 +238,8 @@ class ConnectUtils():
|
|||
pass
|
||||
|
||||
except requests.exceptions.ConnectTimeout as e:
|
||||
log("Server timeout at: %s" % url, 0)
|
||||
log(e, 1)
|
||||
self.logMsg("Server timeout at: %s" % url, 0)
|
||||
self.logMsg(e, 1)
|
||||
|
||||
except requests.exceptions.HTTPError as e:
|
||||
|
||||
|
@ -259,11 +255,11 @@ class ConnectUtils():
|
|||
pass
|
||||
|
||||
except requests.exceptions.SSLError as e:
|
||||
log("Invalid SSL certificate for: %s" % url, 0)
|
||||
log(e, 1)
|
||||
self.logMsg("Invalid SSL certificate for: %s" % url, 0)
|
||||
self.logMsg(e, 1)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
log("Unknown error connecting to: %s" % url, 0)
|
||||
log(e, 1)
|
||||
self.logMsg("Unknown error connecting to: %s" % url, 0)
|
||||
self.logMsg(e, 1)
|
||||
|
||||
return default_link
|
||||
|
|
|
@ -34,7 +34,6 @@ class Embydb_Functions():
|
|||
|
||||
def getViews(self):
|
||||
|
||||
embycursor = self.embycursor
|
||||
views = []
|
||||
|
||||
query = ' '.join((
|
||||
|
@ -42,8 +41,8 @@ class Embydb_Functions():
|
|||
"SELECT view_id",
|
||||
"FROM view"
|
||||
))
|
||||
embycursor.execute(query)
|
||||
rows = embycursor.fetchall()
|
||||
self.embycursor.execute(query)
|
||||
rows = self.embycursor.fetchall()
|
||||
for row in rows:
|
||||
views.append(row[0])
|
||||
return views
|
||||
|
@ -68,7 +67,6 @@ class Embydb_Functions():
|
|||
|
||||
def getView_byId(self, viewid):
|
||||
|
||||
embycursor = self.embycursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
|
@ -76,13 +74,13 @@ class Embydb_Functions():
|
|||
"FROM view",
|
||||
"WHERE view_id = ?"
|
||||
))
|
||||
embycursor.execute(query, (viewid,))
|
||||
view = embycursor.fetchone()
|
||||
self.embycursor.execute(query, (viewid,))
|
||||
view = self.embycursor.fetchone()
|
||||
|
||||
return view
|
||||
|
||||
def getView_byType(self, mediatype):
|
||||
|
||||
embycursor = self.embycursor
|
||||
views = []
|
||||
|
||||
query = ' '.join((
|
||||
|
@ -91,8 +89,8 @@ class Embydb_Functions():
|
|||
"FROM view",
|
||||
"WHERE media_type = ?"
|
||||
))
|
||||
embycursor.execute(query, (mediatype,))
|
||||
rows = embycursor.fetchall()
|
||||
self.embycursor.execute(query, (mediatype,))
|
||||
rows = self.embycursor.fetchall()
|
||||
for row in rows:
|
||||
views.append({
|
||||
|
||||
|
@ -105,17 +103,16 @@ class Embydb_Functions():
|
|||
|
||||
def getView_byName(self, tagname):
|
||||
|
||||
embycursor = self.embycursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT view_id",
|
||||
"FROM view",
|
||||
"WHERE view_name = ?"
|
||||
))
|
||||
embycursor.execute(query, (tagname,))
|
||||
self.embycursor.execute(query, (tagname,))
|
||||
try:
|
||||
view = embycursor.fetchone()[0]
|
||||
view = self.embycursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
view = None
|
||||
|
||||
|
@ -190,8 +187,6 @@ class Embydb_Functions():
|
|||
|
||||
def getItem_byId(self, embyid):
|
||||
|
||||
embycursor = self.embycursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT kodi_id, kodi_fileid, kodi_pathid, parent_id, media_type, emby_type",
|
||||
|
@ -199,40 +194,32 @@ class Embydb_Functions():
|
|||
"WHERE emby_id = ?"
|
||||
))
|
||||
try:
|
||||
embycursor.execute(query, (embyid,))
|
||||
item = embycursor.fetchone()
|
||||
self.embycursor.execute(query, (embyid,))
|
||||
item = self.embycursor.fetchone()
|
||||
return item
|
||||
except: return None
|
||||
|
||||
def getItem_byWildId(self, embyid):
|
||||
|
||||
embycursor = self.embycursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT kodi_id, media_type",
|
||||
"FROM emby",
|
||||
"WHERE emby_id LIKE ?"
|
||||
))
|
||||
embycursor.execute(query, (embyid+"%",))
|
||||
items = embycursor.fetchall()
|
||||
|
||||
return items
|
||||
self.embycursor.execute(query, (embyid+"%",))
|
||||
return self.embycursor.fetchall()
|
||||
|
||||
def getItem_byView(self, mediafolderid):
|
||||
|
||||
embycursor = self.embycursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT kodi_id",
|
||||
"FROM emby",
|
||||
"WHERE media_folder = ?"
|
||||
))
|
||||
embycursor.execute(query, (mediafolderid,))
|
||||
items = embycursor.fetchall()
|
||||
|
||||
return items
|
||||
self.embycursor.execute(query, (mediafolderid,))
|
||||
return self.embycursor.fetchall()
|
||||
|
||||
def getPlexId(self, kodiid, mediatype):
|
||||
"""
|
||||
|
@ -253,8 +240,6 @@ class Embydb_Functions():
|
|||
|
||||
def getItem_byKodiId(self, kodiid, mediatype):
|
||||
|
||||
embycursor = self.embycursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT emby_id, parent_id",
|
||||
|
@ -262,15 +247,11 @@ class Embydb_Functions():
|
|||
"WHERE kodi_id = ?",
|
||||
"AND media_type = ?"
|
||||
))
|
||||
embycursor.execute(query, (kodiid, mediatype,))
|
||||
item = embycursor.fetchone()
|
||||
|
||||
return item
|
||||
self.embycursor.execute(query, (kodiid, mediatype,))
|
||||
return self.embycursor.fetchone()
|
||||
|
||||
def getItem_byParentId(self, parentid, mediatype):
|
||||
|
||||
embycursor = self.embycursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT emby_id, kodi_id, kodi_fileid",
|
||||
|
@ -278,15 +259,11 @@ class Embydb_Functions():
|
|||
"WHERE parent_id = ?",
|
||||
"AND media_type = ?"
|
||||
))
|
||||
embycursor.execute(query, (parentid, mediatype,))
|
||||
items = embycursor.fetchall()
|
||||
|
||||
return items
|
||||
self.embycursor.execute(query, (parentid, mediatype,))
|
||||
return self.embycursor.fetchall()
|
||||
|
||||
def getItemId_byParentId(self, parentid, mediatype):
|
||||
|
||||
embycursor = self.embycursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT emby_id, kodi_id",
|
||||
|
@ -294,39 +271,32 @@ class Embydb_Functions():
|
|||
"WHERE parent_id = ?",
|
||||
"AND media_type = ?"
|
||||
))
|
||||
embycursor.execute(query, (parentid, mediatype,))
|
||||
items = embycursor.fetchall()
|
||||
|
||||
return items
|
||||
self.embycursor.execute(query, (parentid, mediatype,))
|
||||
return self.embycursor.fetchall()
|
||||
|
||||
def getChecksum(self, mediatype):
|
||||
|
||||
embycursor = self.embycursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT emby_id, checksum",
|
||||
"FROM emby",
|
||||
"WHERE emby_type = ?"
|
||||
))
|
||||
embycursor.execute(query, (mediatype,))
|
||||
items = embycursor.fetchall()
|
||||
|
||||
return items
|
||||
self.embycursor.execute(query, (mediatype,))
|
||||
return self.embycursor.fetchall()
|
||||
|
||||
def getMediaType_byId(self, embyid):
|
||||
|
||||
embycursor = self.embycursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT emby_type",
|
||||
"FROM emby",
|
||||
"WHERE emby_id = ?"
|
||||
))
|
||||
embycursor.execute(query, (embyid,))
|
||||
self.embycursor.execute(query, (embyid,))
|
||||
try:
|
||||
itemtype = embycursor.fetchone()[0]
|
||||
itemtype = self.embycursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
itemtype = None
|
||||
|
||||
|
|
|
@ -328,12 +328,12 @@ def doMainListing():
|
|||
if not path:
|
||||
path = utils.window('Emby.nodes.%s.content' % i)
|
||||
label = utils.window('Emby.nodes.%s.title' % i)
|
||||
type = utils.window('Emby.nodes.%s.type' % i)
|
||||
node_type = utils.window('Emby.nodes.%s.type' % i)
|
||||
#because we do not use seperate entrypoints for each content type, we need to figure out which items to show in each listing.
|
||||
#for now we just only show picture nodes in the picture library video nodes in the video library and all nodes in any other window
|
||||
if path and xbmc.getCondVisibility("Window.IsActive(Pictures)") and type == "photos":
|
||||
if path and xbmc.getCondVisibility("Window.IsActive(Pictures)") and node_type == "photos":
|
||||
addDirectoryItem(label, path)
|
||||
elif path and xbmc.getCondVisibility("Window.IsActive(VideoLibrary)") and type != "photos":
|
||||
elif path and xbmc.getCondVisibility("Window.IsActive(VideoLibrary)") and node_type != "photos":
|
||||
addDirectoryItem(label, path)
|
||||
elif path and not xbmc.getCondVisibility("Window.IsActive(VideoLibrary) | Window.IsActive(Pictures) | Window.IsActive(MusicLibrary)"):
|
||||
addDirectoryItem(label, path)
|
||||
|
@ -431,7 +431,7 @@ def deleteItem():
|
|||
doUtils = downloadutils.DownloadUtils()
|
||||
url = "{server}/emby/Items/%s?format=json" % embyid
|
||||
utils.logMsg("EMBY delete", "Deleting request: %s" % embyid, 0)
|
||||
doUtils.downloadUrl(url, type="DELETE")
|
||||
doUtils.downloadUrl(url, action_type="DELETE")
|
||||
|
||||
##### ADD ADDITIONAL USERS #####
|
||||
def addUser():
|
||||
|
@ -486,7 +486,7 @@ def addUser():
|
|||
selected = additionalUsername[resp]
|
||||
selected_userId = additionalUserlist[selected]
|
||||
url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId)
|
||||
doUtils.downloadUrl(url, postBody={}, type="DELETE")
|
||||
doUtils.downloadUrl(url, postBody={}, action_type="DELETE")
|
||||
dialog.notification(
|
||||
heading="Success!",
|
||||
message="%s removed from viewing session" % selected,
|
||||
|
@ -519,7 +519,7 @@ def addUser():
|
|||
selected = users[resp]
|
||||
selected_userId = userlist[selected]
|
||||
url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId)
|
||||
doUtils.downloadUrl(url, postBody={}, type="POST")
|
||||
doUtils.downloadUrl(url, postBody={}, action_type="POST")
|
||||
dialog.notification(
|
||||
heading="Success!",
|
||||
message="%s added to viewing session" % selected,
|
||||
|
@ -759,22 +759,22 @@ def GetSubFolders(nodeindex):
|
|||
title = utils.window('Emby.nodes.%s%s.title' %(nodeindex,node))
|
||||
if title:
|
||||
path = utils.window('Emby.nodes.%s%s.content' %(nodeindex,node))
|
||||
type = utils.window('Emby.nodes.%s%s.type' %(nodeindex,node))
|
||||
addDirectoryItem(title, path)
|
||||
xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
||||
|
||||
##### BROWSE EMBY NODES DIRECTLY #####
|
||||
def BrowseContent(viewname, type="", folderid=""):
|
||||
def BrowseContent(viewname, browse_type="", folderid=""):
|
||||
|
||||
emby = embyserver.Read_EmbyServer()
|
||||
art = artwork.Artwork()
|
||||
doUtils = downloadutils.DownloadUtils()
|
||||
|
||||
#folderid used as filter ?
|
||||
if folderid in ["recent","recentepisodes","inprogress","inprogressepisodes","unwatched","nextepisodes","sets","genres","random","recommended"]:
|
||||
filter = folderid
|
||||
filter_type = folderid
|
||||
folderid = ""
|
||||
else:
|
||||
filter = ""
|
||||
filter_type = ""
|
||||
|
||||
xbmcplugin.setPluginCategory(int(sys.argv[1]), viewname)
|
||||
#get views for root level
|
||||
|
@ -783,33 +783,35 @@ def BrowseContent(viewname, type="", folderid=""):
|
|||
for view in views:
|
||||
if view.get("name") == viewname.decode('utf-8'):
|
||||
folderid = view.get("id")
|
||||
break
|
||||
|
||||
utils.logMsg("BrowseContent","viewname: %s - type: %s - folderid: %s - filter: %s" %(viewname.decode('utf-8'), type.decode('utf-8'), folderid.decode('utf-8'), filter.decode('utf-8')))
|
||||
if viewname is not None:
|
||||
utils.logMsg("BrowseContent","viewname: %s - type: %s - folderid: %s - filter: %s" %(viewname.decode('utf-8'), browse_type.decode('utf-8'), folderid.decode('utf-8'), filter_type.decode('utf-8')))
|
||||
#set the correct params for the content type
|
||||
#only proceed if we have a folderid
|
||||
if folderid:
|
||||
if type.lower() == "homevideos":
|
||||
if browse_type.lower() == "homevideos":
|
||||
xbmcplugin.setContent(int(sys.argv[1]), 'episodes')
|
||||
itemtype = "Video,Folder,PhotoAlbum"
|
||||
elif type.lower() == "photos":
|
||||
elif browse_type.lower() == "photos":
|
||||
xbmcplugin.setContent(int(sys.argv[1]), 'files')
|
||||
itemtype = "Photo,PhotoAlbum,Folder"
|
||||
else:
|
||||
itemtype = ""
|
||||
|
||||
#get the actual listing
|
||||
if type == "recordings":
|
||||
if browse_type == "recordings":
|
||||
listing = emby.getTvRecordings(folderid)
|
||||
elif type == "tvchannels":
|
||||
elif browse_type == "tvchannels":
|
||||
listing = emby.getTvChannels()
|
||||
elif filter == "recent":
|
||||
elif filter_type == "recent":
|
||||
listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[0], sortby="DateCreated", recursive=True, limit=25, sortorder="Descending")
|
||||
elif filter == "random":
|
||||
elif filter_type == "random":
|
||||
listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[0], sortby="Random", recursive=True, limit=150, sortorder="Descending")
|
||||
elif filter == "recommended":
|
||||
listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[0], sortby="SortName", recursive=True, limit=25, sortorder="Ascending", filter="IsFavorite")
|
||||
elif filter == "sets":
|
||||
listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[1], sortby="SortName", recursive=True, limit=25, sortorder="Ascending", filter="IsFavorite")
|
||||
elif filter_type == "recommended":
|
||||
listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[0], sortby="SortName", recursive=True, limit=25, sortorder="Ascending", filter_type="IsFavorite")
|
||||
elif filter_type == "sets":
|
||||
listing = emby.getFilteredSection(folderid, itemtype=itemtype.split(",")[1], sortby="SortName", recursive=True, limit=25, sortorder="Ascending", filter_type="IsFavorite")
|
||||
else:
|
||||
listing = emby.getFilteredSection(folderid, itemtype=itemtype, recursive=False)
|
||||
|
||||
|
@ -819,14 +821,14 @@ def BrowseContent(viewname, type="", folderid=""):
|
|||
li = createListItemFromEmbyItem(item,art,doUtils)
|
||||
if item.get("IsFolder") == True:
|
||||
#for folders we add an additional browse request, passing the folderId
|
||||
path = "%s?id=%s&mode=browsecontent&type=%s&folderid=%s" % (sys.argv[0].decode('utf-8'), viewname.decode('utf-8'), type.decode('utf-8'), item.get("Id").decode('utf-8'))
|
||||
path = "%s?id=%s&mode=browsecontent&type=%s&folderid=%s" % (sys.argv[0].decode('utf-8'), viewname.decode('utf-8'), browse_type.decode('utf-8'), item.get("Id").decode('utf-8'))
|
||||
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=True)
|
||||
else:
|
||||
#playable item, set plugin path and mediastreams
|
||||
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=li.getProperty("path"), listitem=li)
|
||||
|
||||
|
||||
if filter == "recent":
|
||||
if filter_type == "recent":
|
||||
xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE)
|
||||
else:
|
||||
xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE)
|
||||
|
|
|
@ -309,10 +309,9 @@ class Movies(Items):
|
|||
count = 0
|
||||
for boxset in items:
|
||||
|
||||
title = boxset['Name']
|
||||
if pdialog:
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
pdialog.update(percentage, message=title)
|
||||
pdialog.update(percentage, message=boxset['Name'])
|
||||
count += 1
|
||||
self.add_updateBoxset(boxset)
|
||||
|
||||
|
@ -333,7 +332,6 @@ class Movies(Items):
|
|||
# Process single movie
|
||||
kodicursor = self.kodicursor
|
||||
emby_db = self.emby_db
|
||||
kodi_db = self.kodi_db
|
||||
artwork = self.artwork
|
||||
API = PlexAPI.API(item)
|
||||
|
||||
|
@ -517,23 +515,23 @@ class Movies(Items):
|
|||
kodi_db.addCountries(movieid, countries, "movie")
|
||||
# Process cast
|
||||
people = API.getPeopleList()
|
||||
kodi_db.addPeople(movieid, people, "movie")
|
||||
self.kodi_db.addPeople(movieid, people, "movie")
|
||||
# Process genres
|
||||
kodi_db.addGenres(movieid, genres, "movie")
|
||||
self.kodi_db.addGenres(movieid, genres, "movie")
|
||||
# Process artwork
|
||||
allartworks = API.getAllArtwork()
|
||||
artwork.addArtwork(allartworks, movieid, "movie", kodicursor)
|
||||
# Process stream details
|
||||
streams = API.getMediaStreams()
|
||||
kodi_db.addStreams(fileid, streams, runtime)
|
||||
self.kodi_db.addStreams(fileid, streams, runtime)
|
||||
# Process studios
|
||||
kodi_db.addStudios(movieid, studios, "movie")
|
||||
self.kodi_db.addStudios(movieid, studios, "movie")
|
||||
# Process tags: view, Plex collection tags
|
||||
tags = [viewtag]
|
||||
tags.extend(collections)
|
||||
if userdata['Favorite']:
|
||||
tags.append("Favorite movies")
|
||||
kodi_db.addTags(movieid, tags, "movie")
|
||||
self.kodi_db.addTags(movieid, tags, "movie")
|
||||
# Process playstates
|
||||
kodi_db.addPlaystate(fileid, resume, runtime, playcount, dateplayed)
|
||||
|
||||
|
@ -603,7 +601,6 @@ class MusicVideos(Items):
|
|||
# Process single music video
|
||||
kodicursor = self.kodicursor
|
||||
emby_db = self.emby_db
|
||||
kodi_db = self.kodi_db
|
||||
artwork = self.artwork
|
||||
API = api.API(item)
|
||||
|
||||
|
@ -794,32 +791,31 @@ class MusicVideos(Items):
|
|||
artist['Type'] = "Artist"
|
||||
people.extend(artists)
|
||||
people = artwork.getPeopleArtwork(people)
|
||||
kodi_db.addPeople(mvideoid, people, "musicvideo")
|
||||
self.kodi_db.addPeople(mvideoid, people, "musicvideo")
|
||||
# Process genres
|
||||
kodi_db.addGenres(mvideoid, genres, "musicvideo")
|
||||
self.kodi_db.addGenres(mvideoid, genres, "musicvideo")
|
||||
# Process artwork
|
||||
artwork.addArtwork(artwork.getAllArtwork(item), mvideoid, "musicvideo", kodicursor)
|
||||
# Process stream details
|
||||
streams = API.getMediaStreams()
|
||||
kodi_db.addStreams(fileid, streams, runtime)
|
||||
self.kodi_db.addStreams(fileid, streams, runtime)
|
||||
# Process studios
|
||||
kodi_db.addStudios(mvideoid, studios, "musicvideo")
|
||||
self.kodi_db.addStudios(mvideoid, studios, "musicvideo")
|
||||
# Process tags: view, emby tags
|
||||
tags = [viewtag]
|
||||
tags.extend(item['Tags'])
|
||||
if userdata['Favorite']:
|
||||
tags.append("Favorite musicvideos")
|
||||
kodi_db.addTags(mvideoid, tags, "musicvideo")
|
||||
self.kodi_db.addTags(mvideoid, tags, "musicvideo")
|
||||
# Process playstates
|
||||
resume = API.adjustResume(userdata['Resume'])
|
||||
total = round(float(runtime), 6)
|
||||
kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
|
||||
self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
|
||||
|
||||
def updateUserdata(self, item):
|
||||
# This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
|
||||
# Poster with progress bar
|
||||
emby_db = self.emby_db
|
||||
kodi_db = self.kodi_db
|
||||
API = api.API(item)
|
||||
|
||||
# Get emby information
|
||||
|
@ -841,9 +837,9 @@ class MusicVideos(Items):
|
|||
|
||||
# Process favorite tags
|
||||
if userdata['Favorite']:
|
||||
kodi_db.addTag(mvideoid, "Favorite musicvideos", "musicvideo")
|
||||
self.kodi_db.addTag(mvideoid, "Favorite musicvideos", "musicvideo")
|
||||
else:
|
||||
kodi_db.removeTag(mvideoid, "Favorite musicvideos", "musicvideo")
|
||||
self.kodi_db.removeTag(mvideoid, "Favorite musicvideos", "musicvideo")
|
||||
|
||||
# Process playstates
|
||||
playcount = userdata['PlayCount']
|
||||
|
@ -851,7 +847,7 @@ class MusicVideos(Items):
|
|||
resume = API.adjustResume(userdata['Resume'])
|
||||
total = round(float(runtime), 6)
|
||||
|
||||
kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
|
||||
self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
|
||||
def remove(self, itemid):
|
||||
|
@ -878,8 +874,7 @@ class MusicVideos(Items):
|
|||
"AND media_type = 'musicvideo'"
|
||||
))
|
||||
kodicursor.execute(query, (mvideoid,))
|
||||
rows = kodicursor.fetchall()
|
||||
for row in rows:
|
||||
for row in kodicursor.fetchall():
|
||||
|
||||
url = row[0]
|
||||
imagetype = row[1]
|
||||
|
@ -959,7 +954,6 @@ class TVShows(Items):
|
|||
# Process single tvshow
|
||||
kodicursor = self.kodicursor
|
||||
emby_db = self.emby_db
|
||||
kodi_db = self.kodi_db
|
||||
artwork = self.artwork
|
||||
API = PlexAPI.API(item)
|
||||
|
||||
|
@ -1079,7 +1073,7 @@ class TVShows(Items):
|
|||
kodicursor.execute(query, (toplevelpath, "tvshows", "metadata.local", 1, toppathid))
|
||||
|
||||
# Add path
|
||||
pathid = kodi_db.addPath(path)
|
||||
pathid = self.kodi_db.addPath(path)
|
||||
|
||||
# Create the tvshow entry
|
||||
query = (
|
||||
|
@ -1112,18 +1106,18 @@ class TVShows(Items):
|
|||
|
||||
# Process cast
|
||||
people = API.getPeopleList()
|
||||
kodi_db.addPeople(showid, people, "tvshow")
|
||||
self.kodi_db.addPeople(showid, people, "tvshow")
|
||||
# Process genres
|
||||
kodi_db.addGenres(showid, genres, "tvshow")
|
||||
self.kodi_db.addGenres(showid, genres, "tvshow")
|
||||
# Process artwork
|
||||
allartworks = API.getAllArtwork()
|
||||
artwork.addArtwork(allartworks, showid, "tvshow", kodicursor)
|
||||
# Process studios
|
||||
kodi_db.addStudios(showid, studios, "tvshow")
|
||||
self.kodi_db.addStudios(showid, studios, "tvshow")
|
||||
# Process tags: view, PMS collection tags
|
||||
tags = [viewtag]
|
||||
tags.extend(collections)
|
||||
kodi_db.addTags(showid, tags, "tvshow")
|
||||
self.kodi_db.addTags(showid, tags, "tvshow")
|
||||
|
||||
if force_episodes:
|
||||
# We needed to recreate the show entry. Re-add episodes now.
|
||||
|
@ -1154,7 +1148,6 @@ class TVShows(Items):
|
|||
return
|
||||
kodicursor = self.kodicursor
|
||||
emby_db = self.emby_db
|
||||
kodi_db = self.kodi_db
|
||||
artwork = self.artwork
|
||||
seasonnum = API.getIndex()
|
||||
# Get parent tv show Plex id
|
||||
|
@ -1210,10 +1203,8 @@ class TVShows(Items):
|
|||
viewtag and viewid are irrelevant!
|
||||
"""
|
||||
# Process single episode
|
||||
kodiversion = self.kodiversion
|
||||
kodicursor = self.kodicursor
|
||||
emby_db = self.emby_db
|
||||
kodi_db = self.kodi_db
|
||||
artwork = self.artwork
|
||||
API = PlexAPI.API(item)
|
||||
|
||||
|
@ -1310,7 +1301,7 @@ class TVShows(Items):
|
|||
# self.logMsg("Skipping: %s. Unable to add series: %s." % (itemid, seriesId), -1)
|
||||
self.logMsg("Parent tvshow now found, skip item", 2)
|
||||
return False
|
||||
seasonid = kodi_db.addSeason(showid, season)
|
||||
seasonid = self.kodi_db.addSeason(showid, season)
|
||||
|
||||
# GET THE FILE AND PATH #####
|
||||
doIndirect = not self.directpath
|
||||
|
@ -1368,7 +1359,7 @@ class TVShows(Items):
|
|||
self.logMsg("UPDATE episode itemid: %s" % (itemid), 1)
|
||||
|
||||
# Update the movie entry
|
||||
if kodiversion in (16, 17):
|
||||
if self.kodiversion in (16, 17):
|
||||
# Kodi Jarvis, Krypton
|
||||
query = ' '.join((
|
||||
|
||||
|
@ -1400,10 +1391,9 @@ class TVShows(Items):
|
|||
|
||||
##### OR ADD THE EPISODE #####
|
||||
else:
|
||||
self.logMsg("ADD episode itemid: %s" % (itemid), 1)
|
||||
|
||||
self.logMsg("ADD episode itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
# Create the episode entry
|
||||
if kodiversion in (16, 17):
|
||||
if self.kodiversion in (16, 17):
|
||||
# Kodi Jarvis, Krypton
|
||||
query = (
|
||||
'''
|
||||
|
@ -1454,7 +1444,7 @@ class TVShows(Items):
|
|||
kodicursor.execute(query, (pathid, filename, dateadded, fileid))
|
||||
# Process cast
|
||||
people = API.getPeopleList()
|
||||
kodi_db.addPeople(episodeid, people, "episode")
|
||||
self.kodi_db.addPeople(episodeid, people, "episode")
|
||||
# Process artwork
|
||||
# Wide "screenshot" of particular episode
|
||||
poster = item.attrib.get('thumb')
|
||||
|
@ -1473,13 +1463,13 @@ class TVShows(Items):
|
|||
|
||||
# Process stream details
|
||||
streams = API.getMediaStreams()
|
||||
kodi_db.addStreams(fileid, streams, runtime)
|
||||
self.kodi_db.addStreams(fileid, streams, runtime)
|
||||
# Process playstates
|
||||
kodi_db.addPlaystate(fileid, resume, runtime, playcount, dateplayed)
|
||||
self.kodi_db.addPlaystate(fileid, resume, runtime, playcount, dateplayed)
|
||||
if not self.directpath and resume:
|
||||
# Create additional entry for widgets. This is only required for plugin/episode.
|
||||
temppathid = kodi_db.getPath("plugin://plugin.video.plexkodiconnect.tvshows/")
|
||||
tempfileid = kodi_db.addFile(filename, temppathid)
|
||||
temppathid = self.kodi_db.getPath("plugin://plugin.video.plexkodiconnect.tvshows/")
|
||||
tempfileid = self.kodi_db.addFile(filename, temppathid)
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE files",
|
||||
|
@ -1487,7 +1477,7 @@ class TVShows(Items):
|
|||
"WHERE idFile = ?"
|
||||
))
|
||||
kodicursor.execute(query, (temppathid, filename, dateadded, tempfileid))
|
||||
kodi_db.addPlaystate(tempfileid, resume, runtime, playcount, dateplayed)
|
||||
self.kodi_db.addPlaystate(tempfileid, resume, runtime, playcount, dateplayed)
|
||||
self.kodiconn.commit()
|
||||
self.embyconn.commit()
|
||||
|
||||
|
@ -1600,27 +1590,23 @@ class TVShows(Items):
|
|||
def removeShow(self, kodiid):
|
||||
|
||||
kodicursor = self.kodicursor
|
||||
artwork = self.artwork
|
||||
|
||||
artwork.deleteArtwork(kodiid, "tvshow", kodicursor)
|
||||
self.artwork.deleteArtwork(kodiid, "tvshow", kodicursor)
|
||||
kodicursor.execute("DELETE FROM tvshow WHERE idShow = ?", (kodiid,))
|
||||
self.logMsg("Removed tvshow: %s." % kodiid, 2)
|
||||
|
||||
def removeSeason(self, kodiid):
|
||||
|
||||
kodicursor = self.kodicursor
|
||||
artwork = self.artwork
|
||||
|
||||
artwork.deleteArtwork(kodiid, "season", kodicursor)
|
||||
self.artwork.deleteArtwork(kodiid, "season", kodicursor)
|
||||
kodicursor.execute("DELETE FROM seasons WHERE idSeason = ?", (kodiid,))
|
||||
self.logMsg("Removed season: %s." % kodiid, 2)
|
||||
|
||||
def removeEpisode(self, kodiid, fileid):
|
||||
|
||||
kodicursor = self.kodicursor
|
||||
artwork = self.artwork
|
||||
|
||||
artwork.deleteArtwork(kodiid, "episode", kodicursor)
|
||||
self.artwork.deleteArtwork(kodiid, "episode", kodicursor)
|
||||
kodicursor.execute("DELETE FROM episode WHERE idEpisode = ?", (kodiid,))
|
||||
kodicursor.execute("DELETE FROM files WHERE idFile = ?", (fileid,))
|
||||
self.logMsg("Removed episode: %s." % kodiid, 2)
|
||||
|
@ -1656,10 +1642,9 @@ class Music(Items):
|
|||
count = 0
|
||||
for artist in items:
|
||||
|
||||
title = artist['Name']
|
||||
if pdialog:
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
pdialog.update(percentage, message=title)
|
||||
pdialog.update(percentage, message=artist['Name'])
|
||||
count += 1
|
||||
self.add_updateArtist(artist)
|
||||
# Add albums
|
||||
|
@ -1672,10 +1657,9 @@ class Music(Items):
|
|||
count = 0
|
||||
for album in items:
|
||||
|
||||
title = album['Name']
|
||||
if pdialog:
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
pdialog.update(percentage, message=title)
|
||||
pdialog.update(percentage, message=album['Name'])
|
||||
count += 1
|
||||
self.add_updateAlbum(album)
|
||||
# Add songs
|
||||
|
@ -1688,14 +1672,13 @@ class Music(Items):
|
|||
count = 0
|
||||
for song in items:
|
||||
|
||||
title = song['Name']
|
||||
if pdialog:
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
pdialog.update(percentage, message=title)
|
||||
pdialog.update(percentage, message=song['Name'])
|
||||
count += 1
|
||||
self.add_updateSong(song)
|
||||
if not pdialog and self.contentmsg:
|
||||
self.contentPop(title, self.newmusic_time)
|
||||
self.contentPop(song['Name'], self.newmusic_time)
|
||||
|
||||
def add_updateArtist(self, item, viewtag=None, viewid=None, artisttype="MusicArtist"):
|
||||
try:
|
||||
|
@ -1715,7 +1698,6 @@ class Music(Items):
|
|||
artisttype="MusicArtist"):
|
||||
kodicursor = self.kodicursor
|
||||
emby_db = self.emby_db
|
||||
kodi_db = self.kodi_db
|
||||
artwork = self.artwork
|
||||
API = PlexAPI.API(item)
|
||||
|
||||
|
@ -1764,7 +1746,7 @@ class Music(Items):
|
|||
# multiple times.
|
||||
# Kodi doesn't allow that. In case that happens we just merge the
|
||||
# artist entries.
|
||||
artistid = kodi_db.addArtist(name, musicBrainzId)
|
||||
artistid = self.kodi_db.addArtist(name, musicBrainzId)
|
||||
# Create the reference in emby table
|
||||
emby_db.addReference(
|
||||
itemid, artistid, artisttype, "artist", checksum=checksum)
|
||||
|
@ -1811,10 +1793,8 @@ class Music(Items):
|
|||
return
|
||||
|
||||
def run_add_updateAlbum(self, item, viewtag=None, viewid=None):
|
||||
kodiversion = self.kodiversion
|
||||
kodicursor = self.kodicursor
|
||||
emby_db = self.emby_db
|
||||
kodi_db = self.kodi_db
|
||||
artwork = self.artwork
|
||||
API = PlexAPI.API(item)
|
||||
|
||||
|
@ -1875,13 +1855,13 @@ class Music(Items):
|
|||
# multiple times.
|
||||
# Kodi doesn't allow that. In case that happens we just merge the
|
||||
# artist entries.
|
||||
albumid = kodi_db.addAlbum(name, musicBrainzId)
|
||||
albumid = self.kodi_db.addAlbum(name, musicBrainzId)
|
||||
# Create the reference in emby table
|
||||
emby_db.addReference(
|
||||
itemid, albumid, "MusicAlbum", "album", checksum=checksum)
|
||||
|
||||
# Process the album info
|
||||
if kodiversion == 17:
|
||||
if self.kodiversion == 17:
|
||||
# Kodi Krypton
|
||||
query = ' '.join((
|
||||
|
||||
|
@ -1894,7 +1874,7 @@ class Music(Items):
|
|||
kodicursor.execute(query, (artistname, year, genre, bio, thumb,
|
||||
rating, lastScraped, "album", studio,
|
||||
albumid))
|
||||
elif kodiversion == 16:
|
||||
elif self.kodiversion == 16:
|
||||
# Kodi Jarvis
|
||||
query = ' '.join((
|
||||
|
||||
|
@ -1907,7 +1887,7 @@ class Music(Items):
|
|||
kodicursor.execute(query, (artistname, year, genre, bio, thumb,
|
||||
rating, lastScraped, "album", studio,
|
||||
albumid))
|
||||
elif kodiversion == 15:
|
||||
elif self.kodiversion == 15:
|
||||
# Kodi Isengard
|
||||
query = ' '.join((
|
||||
|
||||
|
@ -1998,7 +1978,7 @@ class Music(Items):
|
|||
# Update emby reference with parentid
|
||||
emby_db.updateParentId(artistId, albumid)
|
||||
# Add genres
|
||||
kodi_db.addMusicGenres(albumid, genres, "album")
|
||||
self.kodi_db.addMusicGenres(albumid, genres, "album")
|
||||
# Update artwork
|
||||
artwork.addArtwork(artworks, albumid, "album", kodicursor)
|
||||
self.embyconn.commit()
|
||||
|
@ -2020,11 +2000,9 @@ class Music(Items):
|
|||
|
||||
def run_add_updateSong(self, item, viewtag=None, viewid=None):
|
||||
# Process single song
|
||||
kodiversion = self.kodiversion
|
||||
kodicursor = self.kodicursor
|
||||
emby = self.emby
|
||||
emby_db = self.emby_db
|
||||
kodi_db = self.kodi_db
|
||||
artwork = self.artwork
|
||||
API = PlexAPI.API(item)
|
||||
|
||||
|
@ -2136,7 +2114,7 @@ class Music(Items):
|
|||
self.logMsg("ADD song itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
|
||||
# Add path
|
||||
pathid = kodi_db.addPath(path, strHash="123")
|
||||
pathid = self.kodi_db.addPath(path, strHash="123")
|
||||
|
||||
try:
|
||||
# Get the album
|
||||
|
@ -2148,7 +2126,7 @@ class Music(Items):
|
|||
album_name = item.get('parentTitle')
|
||||
if album_name:
|
||||
self.logMsg("Creating virtual music album for song: %s." % itemid, 1)
|
||||
albumid = kodi_db.addAlbum(album_name, API.getProvider('MusicBrainzAlbum'))
|
||||
albumid = self.kodi_db.addAlbum(album_name, API.getProvider('MusicBrainzAlbum'))
|
||||
emby_db.addReference("%salbum%s" % (itemid, albumid), albumid, "MusicAlbum_", "album")
|
||||
else:
|
||||
# No album Id associated to the song.
|
||||
|
@ -2173,7 +2151,7 @@ class Music(Items):
|
|||
self.logMsg("Failed to add album. Creating singles.", 1)
|
||||
kodicursor.execute("select coalesce(max(idAlbum),0) from album")
|
||||
albumid = kodicursor.fetchone()[0] + 1
|
||||
if kodiversion == 16:
|
||||
if self.kodiversion == 16:
|
||||
# Kodi Jarvis
|
||||
query = (
|
||||
'''
|
||||
|
@ -2183,7 +2161,7 @@ class Music(Items):
|
|||
'''
|
||||
)
|
||||
kodicursor.execute(query, (albumid, genre, year, "single"))
|
||||
elif kodiversion == 15:
|
||||
elif self.kodiversion == 15:
|
||||
# Kodi Isengard
|
||||
query = (
|
||||
'''
|
||||
|
@ -2316,11 +2294,11 @@ class Music(Items):
|
|||
result = kodicursor.fetchone()
|
||||
if result and result[0] != album_artists:
|
||||
# Field is empty
|
||||
if kodiversion in (16, 17):
|
||||
if self.kodiversion in (16, 17):
|
||||
# Kodi Jarvis, Krypton
|
||||
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
|
||||
kodicursor.execute(query, (album_artists, albumid))
|
||||
elif kodiversion == 15:
|
||||
elif self.kodiversion == 15:
|
||||
# Kodi Isengard
|
||||
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
|
||||
kodicursor.execute(query, (album_artists, albumid))
|
||||
|
@ -2330,7 +2308,7 @@ class Music(Items):
|
|||
kodicursor.execute(query, (album_artists, albumid))
|
||||
|
||||
# Add genres
|
||||
kodi_db.addMusicGenres(songid, genres, "song")
|
||||
self.kodi_db.addMusicGenres(songid, genres, "song")
|
||||
|
||||
# Update artwork
|
||||
allart = API.getAllArtwork(parentInfo=True)
|
||||
|
@ -2372,10 +2350,9 @@ class Music(Items):
|
|||
self.removeSong(kodiid)
|
||||
# This should only address single song scenario, where server doesn't actually
|
||||
# create an album for the song.
|
||||
customitems = emby_db.getItem_byWildId(itemid)
|
||||
emby_db.removeWildItem(itemid)
|
||||
|
||||
for item in customitems:
|
||||
for item in emby_db.getItem_byWildId(itemid):
|
||||
|
||||
item_kid = item[0]
|
||||
item_mediatype = item[1]
|
||||
|
@ -2431,23 +2408,16 @@ class Music(Items):
|
|||
def removeSong(self, kodiid):
|
||||
|
||||
kodicursor = self.kodicursor
|
||||
artwork = self.artwork
|
||||
|
||||
artwork.deleteArtwork(kodiid, "song", kodicursor)
|
||||
kodicursor.execute("DELETE FROM song WHERE idSong = ?", (kodiid,))
|
||||
self.artwork.deleteArtwork(kodiid, "song", self.kodicursor)
|
||||
self.kodicursor.execute("DELETE FROM song WHERE idSong = ?", (kodiid,))
|
||||
|
||||
def removeAlbum(self, kodiid):
|
||||
|
||||
kodicursor = self.kodicursor
|
||||
artwork = self.artwork
|
||||
|
||||
artwork.deleteArtwork(kodiid, "album", kodicursor)
|
||||
kodicursor.execute("DELETE FROM album WHERE idAlbum = ?", (kodiid,))
|
||||
self.artwork.deleteArtwork(kodiid, "album", self.kodicursor)
|
||||
self.kodicursor.execute("DELETE FROM album WHERE idAlbum = ?", (kodiid,))
|
||||
|
||||
def removeArtist(self, kodiid):
|
||||
|
||||
kodicursor = self.kodicursor
|
||||
artwork = self.artwork
|
||||
|
||||
artwork.deleteArtwork(kodiid, "artist", kodicursor)
|
||||
kodicursor.execute("DELETE FROM artist WHERE idArtist = ?", (kodiid,))
|
||||
self.artwork.deleteArtwork(kodiid, "artist", self.kodicursor)
|
||||
self.kodicursor.execute("DELETE FROM artist WHERE idArtist = ?", (kodiid,))
|
||||
|
|
|
@ -100,19 +100,18 @@ class Kodidb_Functions():
|
|||
# SQL won't return existing paths otherwise
|
||||
if path is None:
|
||||
path = ""
|
||||
cursor = self.cursor
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idPath",
|
||||
"FROM path",
|
||||
"WHERE strPath = ?"
|
||||
))
|
||||
cursor.execute(query, (path,))
|
||||
self.cursor.execute(query, (path,))
|
||||
try:
|
||||
pathid = cursor.fetchone()[0]
|
||||
pathid = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
cursor.execute("select coalesce(max(idPath),0) from path")
|
||||
pathid = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(idPath),0) from path")
|
||||
pathid = self.cursor.fetchone()[0] + 1
|
||||
if strHash is None:
|
||||
query = (
|
||||
'''
|
||||
|
@ -122,7 +121,7 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (pathid, path))
|
||||
self.cursor.execute(query, (pathid, path))
|
||||
else:
|
||||
query = (
|
||||
'''
|
||||
|
@ -132,23 +131,21 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (pathid, path, strHash))
|
||||
self.cursor.execute(query, (pathid, path, strHash))
|
||||
|
||||
return pathid
|
||||
|
||||
def getPath(self, path):
|
||||
|
||||
cursor = self.cursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idPath",
|
||||
"FROM path",
|
||||
"WHERE strPath = ?"
|
||||
))
|
||||
cursor.execute(query, (path,))
|
||||
self.cursor.execute(query, (path,))
|
||||
try:
|
||||
pathid = cursor.fetchone()[0]
|
||||
pathid = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
pathid = None
|
||||
|
||||
|
@ -156,8 +153,6 @@ class Kodidb_Functions():
|
|||
|
||||
def addFile(self, filename, pathid):
|
||||
|
||||
cursor = self.cursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idFile",
|
||||
|
@ -165,12 +160,12 @@ class Kodidb_Functions():
|
|||
"WHERE strFilename = ?",
|
||||
"AND idPath = ?"
|
||||
))
|
||||
cursor.execute(query, (filename, pathid,))
|
||||
self.cursor.execute(query, (filename, pathid,))
|
||||
try:
|
||||
fileid = cursor.fetchone()[0]
|
||||
fileid = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
cursor.execute("select coalesce(max(idFile),0) from files")
|
||||
fileid = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(idFile),0) from files")
|
||||
fileid = self.cursor.fetchone()[0] + 1
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO files(
|
||||
|
@ -179,23 +174,21 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (fileid, filename))
|
||||
self.cursor.execute(query, (fileid, filename))
|
||||
|
||||
return fileid
|
||||
|
||||
def getFile(self, fileid):
|
||||
|
||||
cursor = self.cursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT strFilename",
|
||||
"FROM files",
|
||||
"WHERE idFile = ?"
|
||||
))
|
||||
cursor.execute(query, (fileid,))
|
||||
self.cursor.execute(query, (fileid,))
|
||||
try:
|
||||
filename = cursor.fetchone()[0]
|
||||
filename = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
filename = ""
|
||||
|
||||
|
@ -216,8 +209,6 @@ class Kodidb_Functions():
|
|||
|
||||
def addCountries(self, kodiid, countries, mediatype):
|
||||
|
||||
cursor = self.cursor
|
||||
|
||||
if self.kodiversion in (15, 16, 17):
|
||||
# Kodi Isengard, Jarvis, Krypton
|
||||
for country in countries:
|
||||
|
@ -228,18 +219,18 @@ class Kodidb_Functions():
|
|||
"WHERE name = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (country,))
|
||||
self.cursor.execute(query, (country,))
|
||||
|
||||
try:
|
||||
country_id = cursor.fetchone()[0]
|
||||
country_id = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Country entry does not exists
|
||||
cursor.execute("select coalesce(max(country_id),0) from country")
|
||||
country_id = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(country_id),0) from country")
|
||||
country_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO country(country_id, name) values(?, ?)"
|
||||
cursor.execute(query, (country_id, country))
|
||||
self.cursor.execute(query, (country_id, country))
|
||||
self.logMsg("Add country to media, processing: %s" % country, 2)
|
||||
|
||||
finally: # Assign country to content
|
||||
|
@ -251,7 +242,7 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (country_id, kodiid, mediatype))
|
||||
self.cursor.execute(query, (country_id, kodiid, mediatype))
|
||||
else:
|
||||
# Kodi Helix
|
||||
for country in countries:
|
||||
|
@ -262,18 +253,18 @@ class Kodidb_Functions():
|
|||
"WHERE strCountry = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (country,))
|
||||
self.cursor.execute(query, (country,))
|
||||
|
||||
try:
|
||||
idCountry = cursor.fetchone()[0]
|
||||
idCountry = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Country entry does not exists
|
||||
cursor.execute("select coalesce(max(idCountry),0) from country")
|
||||
idCountry = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(idCountry),0) from country")
|
||||
idCountry = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO country(idCountry, strCountry) values(?, ?)"
|
||||
cursor.execute(query, (idCountry, country))
|
||||
self.cursor.execute(query, (idCountry, country))
|
||||
self.logMsg("Add country to media, processing: %s" % country, 2)
|
||||
|
||||
finally:
|
||||
|
@ -287,23 +278,19 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (idCountry, kodiid))
|
||||
self.cursor.execute(query, (idCountry, kodiid))
|
||||
|
||||
def addPeople(self, kodiid, people, mediatype):
|
||||
|
||||
cursor = self.cursor
|
||||
artwork = self.artwork
|
||||
kodiversion = self.kodiversion
|
||||
|
||||
castorder = 1
|
||||
for person in people:
|
||||
|
||||
name = person['Name']
|
||||
type = person['Type']
|
||||
person_type = person['Type']
|
||||
thumb = person['imageurl']
|
||||
|
||||
# Kodi Isengard, Jarvis, Krypton
|
||||
if kodiversion in (15, 16, 17):
|
||||
if self.kodiversion in (15, 16, 17):
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT actor_id",
|
||||
|
@ -311,22 +298,23 @@ class Kodidb_Functions():
|
|||
"WHERE name = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (name,))
|
||||
self.cursor.execute(query, (name,))
|
||||
|
||||
try:
|
||||
actorid = cursor.fetchone()[0]
|
||||
actorid = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Cast entry does not exists
|
||||
cursor.execute("select coalesce(max(actor_id),0) from actor")
|
||||
actorid = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(actor_id),0) from actor")
|
||||
actorid = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO actor(actor_id, name) values(?, ?)"
|
||||
cursor.execute(query, (actorid, name))
|
||||
self.cursor.execute(query, (actorid, name))
|
||||
self.logMsg("Add people to media, processing: %s" % name, 2)
|
||||
|
||||
finally:
|
||||
# Link person to content
|
||||
if "Actor" in type:
|
||||
if "Actor" in person_type:
|
||||
role = person.get('Role')
|
||||
query = (
|
||||
'''
|
||||
|
@ -336,10 +324,10 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (actorid, kodiid, mediatype, role, castorder))
|
||||
self.cursor.execute(query, (actorid, kodiid, mediatype, role, castorder))
|
||||
castorder += 1
|
||||
|
||||
elif "Director" in type:
|
||||
elif "Director" in person_type:
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO director_link(
|
||||
|
@ -348,9 +336,9 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (actorid, kodiid, mediatype))
|
||||
self.cursor.execute(query, (actorid, kodiid, mediatype))
|
||||
|
||||
elif type in ("Writing", "Writer"):
|
||||
elif person_type in ("Writing", "Writer"):
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO writer_link(
|
||||
|
@ -359,9 +347,9 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (actorid, kodiid, mediatype))
|
||||
self.cursor.execute(query, (actorid, kodiid, mediatype))
|
||||
|
||||
elif "Artist" in type:
|
||||
elif "Artist" in person_type:
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO actor_link(
|
||||
|
@ -370,7 +358,7 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (actorid, kodiid, mediatype))
|
||||
self.cursor.execute(query, (actorid, kodiid, mediatype))
|
||||
# Kodi Helix
|
||||
else:
|
||||
query = ' '.join((
|
||||
|
@ -380,22 +368,23 @@ class Kodidb_Functions():
|
|||
"WHERE strActor = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (name,))
|
||||
self.cursor.execute(query, (name,))
|
||||
|
||||
try:
|
||||
actorid = cursor.fetchone()[0]
|
||||
actorid = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Cast entry does not exists
|
||||
cursor.execute("select coalesce(max(idActor),0) from actors")
|
||||
actorid = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(idActor),0) from actors")
|
||||
actorid = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO actors(idActor, strActor) values(?, ?)"
|
||||
cursor.execute(query, (actorid, name))
|
||||
self.cursor.execute(query, (actorid, name))
|
||||
self.logMsg("Add people to media, processing: %s" % name, 2)
|
||||
|
||||
finally:
|
||||
# Link person to content
|
||||
if "Actor" in type:
|
||||
if "Actor" in person_type:
|
||||
role = person.get('Role')
|
||||
|
||||
if "movie" in mediatype:
|
||||
|
@ -427,10 +416,10 @@ class Kodidb_Functions():
|
|||
)
|
||||
else: return # Item is invalid
|
||||
|
||||
cursor.execute(query, (actorid, kodiid, role, castorder))
|
||||
self.cursor.execute(query, (actorid, kodiid, role, castorder))
|
||||
castorder += 1
|
||||
|
||||
elif "Director" in type:
|
||||
elif "Director" in person_type:
|
||||
if "movie" in mediatype:
|
||||
query = (
|
||||
'''
|
||||
|
@ -470,9 +459,9 @@ class Kodidb_Functions():
|
|||
)
|
||||
else: return # Item is invalid
|
||||
|
||||
cursor.execute(query, (actorid, kodiid))
|
||||
self.cursor.execute(query, (actorid, kodiid))
|
||||
|
||||
elif type in ("Writing", "Writer"):
|
||||
elif person_type in ("Writing", "Writer"):
|
||||
if "movie" in mediatype:
|
||||
query = (
|
||||
'''
|
||||
|
@ -493,9 +482,9 @@ class Kodidb_Functions():
|
|||
)
|
||||
else: return # Item is invalid
|
||||
|
||||
cursor.execute(query, (actorid, kodiid))
|
||||
self.cursor.execute(query, (actorid, kodiid))
|
||||
|
||||
elif "Artist" in type:
|
||||
elif "Artist" in person_type:
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO artistlinkmusicvideo(
|
||||
|
@ -504,20 +493,19 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (actorid, kodiid))
|
||||
self.cursor.execute(query, (actorid, kodiid))
|
||||
|
||||
# Add person image to art table
|
||||
if thumb:
|
||||
arttype = type.lower()
|
||||
arttype = person_type.lower()
|
||||
|
||||
if "writing" in arttype:
|
||||
arttype = "writer"
|
||||
|
||||
artwork.addOrUpdateArt(thumb, actorid, arttype, "thumb", cursor)
|
||||
self.artwork.addOrUpdateArt(thumb, actorid, arttype, "thumb", self.cursor)
|
||||
|
||||
def addGenres(self, kodiid, genres, mediatype):
|
||||
|
||||
cursor = self.cursor
|
||||
|
||||
# Kodi Isengard, Jarvis, Krypton
|
||||
if self.kodiversion in (15, 16, 17):
|
||||
|
@ -528,7 +516,7 @@ class Kodidb_Functions():
|
|||
"WHERE media_id = ?",
|
||||
"AND media_type = ?"
|
||||
))
|
||||
cursor.execute(query, (kodiid, mediatype,))
|
||||
self.cursor.execute(query, (kodiid, mediatype,))
|
||||
|
||||
# Add genres
|
||||
for genre in genres:
|
||||
|
@ -540,18 +528,19 @@ class Kodidb_Functions():
|
|||
"WHERE name = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (genre,))
|
||||
self.cursor.execute(query, (genre,))
|
||||
|
||||
try:
|
||||
genre_id = cursor.fetchone()[0]
|
||||
genre_id = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Create genre in database
|
||||
cursor.execute("select coalesce(max(genre_id),0) from genre")
|
||||
genre_id = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(genre_id),0) from genre")
|
||||
genre_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO genre(genre_id, name) values(?, ?)"
|
||||
cursor.execute(query, (genre_id, genre))
|
||||
self.cursor.execute(query, (genre_id, genre))
|
||||
self.logMsg("Add Genres to media, processing: %s" % genre, 2)
|
||||
|
||||
finally:
|
||||
# Assign genre to item
|
||||
|
@ -563,16 +552,16 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (genre_id, kodiid, mediatype))
|
||||
self.cursor.execute(query, (genre_id, kodiid, mediatype))
|
||||
else:
|
||||
# Kodi Helix
|
||||
# Delete current genres for clean slate
|
||||
if "movie" in mediatype:
|
||||
cursor.execute("DELETE FROM genrelinkmovie WHERE idMovie = ?", (kodiid,))
|
||||
self.cursor.execute("DELETE FROM genrelinkmovie WHERE idMovie = ?", (kodiid,))
|
||||
elif "tvshow" in mediatype:
|
||||
cursor.execute("DELETE FROM genrelinktvshow WHERE idShow = ?", (kodiid,))
|
||||
self.cursor.execute("DELETE FROM genrelinktvshow WHERE idShow = ?", (kodiid,))
|
||||
elif "musicvideo" in mediatype:
|
||||
cursor.execute("DELETE FROM genrelinkmusicvideo WHERE idMVideo = ?", (kodiid,))
|
||||
self.cursor.execute("DELETE FROM genrelinkmusicvideo WHERE idMVideo = ?", (kodiid,))
|
||||
|
||||
# Add genres
|
||||
for genre in genres:
|
||||
|
@ -584,18 +573,19 @@ class Kodidb_Functions():
|
|||
"WHERE strGenre = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (genre,))
|
||||
self.cursor.execute(query, (genre,))
|
||||
|
||||
try:
|
||||
idGenre = cursor.fetchone()[0]
|
||||
idGenre = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Create genre in database
|
||||
cursor.execute("select coalesce(max(idGenre),0) from genre")
|
||||
idGenre = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(idGenre),0) from genre")
|
||||
idGenre = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)"
|
||||
cursor.execute(query, (idGenre, genre))
|
||||
self.cursor.execute(query, (idGenre, genre))
|
||||
self.logMsg("Add Genres to media, processing: %s" % genre, 2)
|
||||
|
||||
finally:
|
||||
# Assign genre to item
|
||||
|
@ -628,16 +618,13 @@ class Kodidb_Functions():
|
|||
)
|
||||
else: return # Item is invalid
|
||||
|
||||
cursor.execute(query, (idGenre, kodiid))
|
||||
self.cursor.execute(query, (idGenre, kodiid))
|
||||
|
||||
def addStudios(self, kodiid, studios, mediatype):
|
||||
|
||||
cursor = self.cursor
|
||||
kodiversion = self.kodiversion
|
||||
|
||||
for studio in studios:
|
||||
|
||||
if kodiversion in (15, 16, 17):
|
||||
if self.kodiversion in (15, 16, 17):
|
||||
# Kodi Isengard, Jarvis, Krypton
|
||||
query = ' '.join((
|
||||
|
||||
|
@ -646,17 +633,18 @@ class Kodidb_Functions():
|
|||
"WHERE name = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (studio,))
|
||||
self.cursor.execute(query, (studio,))
|
||||
try:
|
||||
studioid = cursor.fetchone()[0]
|
||||
studioid = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Studio does not exists.
|
||||
cursor.execute("select coalesce(max(studio_id),0) from studio")
|
||||
studioid = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(studio_id),0) from studio")
|
||||
studioid = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO studio(studio_id, name) values(?, ?)"
|
||||
cursor.execute(query, (studioid, studio))
|
||||
self.cursor.execute(query, (studioid, studio))
|
||||
self.logMsg("Add Studios to media, processing: %s" % studio, 2)
|
||||
|
||||
finally: # Assign studio to item
|
||||
query = (
|
||||
|
@ -666,7 +654,7 @@ class Kodidb_Functions():
|
|||
|
||||
VALUES (?, ?, ?)
|
||||
''')
|
||||
cursor.execute(query, (studioid, kodiid, mediatype))
|
||||
self.cursor.execute(query, (studioid, kodiid, mediatype))
|
||||
else:
|
||||
# Kodi Helix
|
||||
query = ' '.join((
|
||||
|
@ -676,17 +664,18 @@ class Kodidb_Functions():
|
|||
"WHERE strstudio = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (studio,))
|
||||
self.cursor.execute(query, (studio,))
|
||||
try:
|
||||
studioid = cursor.fetchone()[0]
|
||||
studioid = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Studio does not exists.
|
||||
cursor.execute("select coalesce(max(idstudio),0) from studio")
|
||||
studioid = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(idstudio),0) from studio")
|
||||
studioid = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO studio(idstudio, strstudio) values(?, ?)"
|
||||
cursor.execute(query, (studioid, studio))
|
||||
self.cursor.execute(query, (studioid, studio))
|
||||
self.logMsg("Add Studios to media, processing: %s" % studio, 2)
|
||||
|
||||
finally: # Assign studio to item
|
||||
if "movie" in mediatype:
|
||||
|
@ -713,14 +702,12 @@ class Kodidb_Functions():
|
|||
INSERT OR REPLACE INTO studiolinkepisode(idstudio, idEpisode)
|
||||
VALUES (?, ?)
|
||||
''')
|
||||
cursor.execute(query, (studioid, kodiid))
|
||||
self.cursor.execute(query, (studioid, kodiid))
|
||||
|
||||
def addStreams(self, fileid, streamdetails, runtime):
|
||||
|
||||
cursor = self.cursor
|
||||
|
||||
# First remove any existing entries
|
||||
cursor.execute("DELETE FROM streamdetails WHERE idFile = ?", (fileid,))
|
||||
self.cursor.execute("DELETE FROM streamdetails WHERE idFile = ?", (fileid,))
|
||||
if streamdetails:
|
||||
# Video details
|
||||
for videotrack in streamdetails['video']:
|
||||
|
@ -733,7 +720,7 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (fileid, 0, videotrack['codec'],
|
||||
self.cursor.execute(query, (fileid, 0, videotrack['codec'],
|
||||
videotrack['aspect'], videotrack['width'], videotrack['height'],
|
||||
runtime ,videotrack['video3DFormat']))
|
||||
|
||||
|
@ -747,7 +734,7 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (fileid, 1, audiotrack['codec'],
|
||||
self.cursor.execute(query, (fileid, 1, audiotrack['codec'],
|
||||
audiotrack['channels'], audiotrack['language']))
|
||||
|
||||
# Subtitles details
|
||||
|
@ -760,7 +747,7 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (fileid, 2, subtitletrack))
|
||||
self.cursor.execute(query, (fileid, 2, subtitletrack))
|
||||
|
||||
def getResumes(self):
|
||||
"""
|
||||
|
@ -926,35 +913,27 @@ class Kodidb_Functions():
|
|||
return int(runtime)
|
||||
|
||||
def addPlaystate(self, fileid, resume_seconds, total_seconds, playcount, dateplayed):
|
||||
cursor = self.cursor
|
||||
# Delete existing resume point
|
||||
query = ' '.join((
|
||||
|
||||
"DELETE FROM bookmark",
|
||||
"WHERE idFile = ?"
|
||||
))
|
||||
cursor.execute(query, (fileid,))
|
||||
self.cursor.execute(query, (fileid,))
|
||||
|
||||
# Set watched count
|
||||
if playcount is None:
|
||||
query = ' '.join((
|
||||
"UPDATE files",
|
||||
"SET lastPlayed = ?",
|
||||
"WHERE idFile = ?"
|
||||
))
|
||||
cursor.execute(query, (dateplayed, fileid))
|
||||
else:
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE files",
|
||||
"SET playCount = ?, lastPlayed = ?",
|
||||
"WHERE idFile = ?"
|
||||
))
|
||||
cursor.execute(query, (playcount, dateplayed, fileid))
|
||||
self.cursor.execute(query, (playcount, dateplayed, fileid))
|
||||
|
||||
# Set the resume bookmark
|
||||
if resume_seconds:
|
||||
cursor.execute("select coalesce(max(idBookmark),0) from bookmark")
|
||||
bookmarkId = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(idBookmark),0) from bookmark")
|
||||
bookmarkId = self.cursor.fetchone()[0] + 1
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO bookmark(
|
||||
|
@ -963,13 +942,11 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (bookmarkId, fileid, resume_seconds, total_seconds,
|
||||
self.cursor.execute(query, (bookmarkId, fileid, resume_seconds, total_seconds,
|
||||
"DVDPlayer", 1))
|
||||
|
||||
def addTags(self, kodiid, tags, mediatype):
|
||||
|
||||
cursor = self.cursor
|
||||
|
||||
# First, delete any existing tags associated to the id
|
||||
if self.kodiversion in (15, 16, 17):
|
||||
# Kodi Isengard, Jarvis, Krypton
|
||||
|
@ -979,7 +956,7 @@ class Kodidb_Functions():
|
|||
"WHERE media_id = ?",
|
||||
"AND media_type = ?"
|
||||
))
|
||||
cursor.execute(query, (kodiid, mediatype))
|
||||
self.cursor.execute(query, (kodiid, mediatype))
|
||||
else:
|
||||
# Kodi Helix
|
||||
query = ' '.join((
|
||||
|
@ -988,16 +965,15 @@ class Kodidb_Functions():
|
|||
"WHERE idMedia = ?",
|
||||
"AND media_type = ?"
|
||||
))
|
||||
cursor.execute(query, (kodiid, mediatype))
|
||||
self.cursor.execute(query, (kodiid, mediatype))
|
||||
|
||||
# Add tags
|
||||
self.logMsg("Adding Tags: %s" % tags, 2)
|
||||
for tag in tags:
|
||||
self.addTag(kodiid, tag, mediatype)
|
||||
|
||||
def addTag(self, kodiid, tag, mediatype):
|
||||
|
||||
cursor = self.cursor
|
||||
|
||||
if self.kodiversion in (15, 16, 17):
|
||||
# Kodi Isengard, Jarvis, Krypton
|
||||
query = ' '.join((
|
||||
|
@ -1007,9 +983,9 @@ class Kodidb_Functions():
|
|||
"WHERE name = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (tag,))
|
||||
self.cursor.execute(query, (tag,))
|
||||
try:
|
||||
tag_id = cursor.fetchone()[0]
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Create the tag, because it does not exist
|
||||
|
@ -1026,7 +1002,7 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (tag_id, kodiid, mediatype))
|
||||
self.cursor.execute(query, (tag_id, kodiid, mediatype))
|
||||
else:
|
||||
# Kodi Helix
|
||||
query = ' '.join((
|
||||
|
@ -1036,9 +1012,9 @@ class Kodidb_Functions():
|
|||
"WHERE strTag = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (tag,))
|
||||
self.cursor.execute(query, (tag,))
|
||||
try:
|
||||
tag_id = cursor.fetchone()[0]
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
# Create the tag
|
||||
|
@ -1055,12 +1031,10 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (tag_id, kodiid, mediatype))
|
||||
self.cursor.execute(query, (tag_id, kodiid, mediatype))
|
||||
|
||||
def createTag(self, name):
|
||||
|
||||
cursor = self.cursor
|
||||
|
||||
# This will create and return the tag_id
|
||||
if self.kodiversion in (15, 16, 17):
|
||||
# Kodi Isengard, Jarvis, Krypton
|
||||
|
@ -1071,16 +1045,16 @@ class Kodidb_Functions():
|
|||
"WHERE name = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (name,))
|
||||
self.cursor.execute(query, (name,))
|
||||
try:
|
||||
tag_id = cursor.fetchone()[0]
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
cursor.execute("select coalesce(max(tag_id),0) from tag")
|
||||
tag_id = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(tag_id),0) from tag")
|
||||
tag_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO tag(tag_id, name) values(?, ?)"
|
||||
cursor.execute(query, (tag_id, name))
|
||||
self.cursor.execute(query, (tag_id, name))
|
||||
self.logMsg("Create tag_id: %s name: %s" % (tag_id, name), 2)
|
||||
else:
|
||||
# Kodi Helix
|
||||
|
@ -1091,23 +1065,22 @@ class Kodidb_Functions():
|
|||
"WHERE strTag = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (name,))
|
||||
self.cursor.execute(query, (name,))
|
||||
try:
|
||||
tag_id = cursor.fetchone()[0]
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
cursor.execute("select coalesce(max(idTag),0) from tag")
|
||||
tag_id = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(idTag),0) from tag")
|
||||
tag_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO tag(idTag, strTag) values(?, ?)"
|
||||
cursor.execute(query, (tag_id, name))
|
||||
self.cursor.execute(query, (tag_id, name))
|
||||
self.logMsg("Create idTag: %s name: %s" % (tag_id, name), 2)
|
||||
|
||||
return tag_id
|
||||
|
||||
def updateTag(self, oldtag, newtag, kodiid, mediatype):
|
||||
|
||||
cursor = self.cursor
|
||||
self.logMsg("Updating: %s with %s for %s: %s" % (oldtag, newtag, mediatype, kodiid), 2)
|
||||
|
||||
if self.kodiversion in (15, 16, 17):
|
||||
|
@ -1121,7 +1094,7 @@ class Kodidb_Functions():
|
|||
"AND media_type = ?",
|
||||
"AND tag_id = ?"
|
||||
))
|
||||
cursor.execute(query, (newtag, kodiid, mediatype, oldtag,))
|
||||
self.cursor.execute(query, (newtag, kodiid, mediatype, oldtag,))
|
||||
except Exception as e:
|
||||
# The new tag we are going to apply already exists for this item
|
||||
# delete current tag instead
|
||||
|
@ -1133,7 +1106,7 @@ class Kodidb_Functions():
|
|||
"AND media_type = ?",
|
||||
"AND tag_id = ?"
|
||||
))
|
||||
cursor.execute(query, (kodiid, mediatype, oldtag,))
|
||||
self.cursor.execute(query, (kodiid, mediatype, oldtag,))
|
||||
else:
|
||||
# Kodi Helix
|
||||
try:
|
||||
|
@ -1145,7 +1118,7 @@ class Kodidb_Functions():
|
|||
"AND media_type = ?",
|
||||
"AND idTag = ?"
|
||||
))
|
||||
cursor.execute(query, (newtag, kodiid, mediatype, oldtag,))
|
||||
self.cursor.execute(query, (newtag, kodiid, mediatype, oldtag,))
|
||||
except Exception as e:
|
||||
# The new tag we are going to apply already exists for this item
|
||||
# delete current tag instead
|
||||
|
@ -1157,12 +1130,10 @@ class Kodidb_Functions():
|
|||
"AND media_type = ?",
|
||||
"AND idTag = ?"
|
||||
))
|
||||
cursor.execute(query, (kodiid, mediatype, oldtag,))
|
||||
self.cursor.execute(query, (kodiid, mediatype, oldtag,))
|
||||
|
||||
def removeTag(self, kodiid, tagname, mediatype):
|
||||
|
||||
cursor = self.cursor
|
||||
|
||||
if self.kodiversion in (15, 16, 17):
|
||||
# Kodi Isengard, Jarvis, Krypton
|
||||
query = ' '.join((
|
||||
|
@ -1172,9 +1143,9 @@ class Kodidb_Functions():
|
|||
"WHERE name = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (tagname,))
|
||||
self.cursor.execute(query, (tagname,))
|
||||
try:
|
||||
tag_id = cursor.fetchone()[0]
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
else:
|
||||
|
@ -1185,7 +1156,7 @@ class Kodidb_Functions():
|
|||
"AND media_type = ?",
|
||||
"AND tag_id = ?"
|
||||
))
|
||||
cursor.execute(query, (kodiid, mediatype, tag_id,))
|
||||
self.cursor.execute(query, (kodiid, mediatype, tag_id,))
|
||||
else:
|
||||
# Kodi Helix
|
||||
query = ' '.join((
|
||||
|
@ -1195,9 +1166,9 @@ class Kodidb_Functions():
|
|||
"WHERE strTag = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (tagname,))
|
||||
self.cursor.execute(query, (tagname,))
|
||||
try:
|
||||
tag_id = cursor.fetchone()[0]
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
else:
|
||||
|
@ -1208,11 +1179,10 @@ class Kodidb_Functions():
|
|||
"AND media_type = ?",
|
||||
"AND idTag = ?"
|
||||
))
|
||||
cursor.execute(query, (kodiid, mediatype, tag_id,))
|
||||
self.cursor.execute(query, (kodiid, mediatype, tag_id,))
|
||||
|
||||
def createBoxset(self, boxsetname):
|
||||
|
||||
cursor = self.cursor
|
||||
self.logMsg("Adding boxset: %s" % boxsetname, 2)
|
||||
query = ' '.join((
|
||||
|
||||
|
@ -1221,16 +1191,16 @@ class Kodidb_Functions():
|
|||
"WHERE strSet = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (boxsetname,))
|
||||
self.cursor.execute(query, (boxsetname,))
|
||||
try:
|
||||
setid = cursor.fetchone()[0]
|
||||
setid = self.cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
cursor.execute("select coalesce(max(idSet),0) from sets")
|
||||
setid = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(idSet),0) from sets")
|
||||
setid = self.cursor.fetchone()[0] + 1
|
||||
|
||||
query = "INSERT INTO sets(idSet, strSet) values(?, ?)"
|
||||
cursor.execute(query, (setid, boxsetname))
|
||||
self.cursor.execute(query, (setid, boxsetname))
|
||||
|
||||
return setid
|
||||
|
||||
|
@ -1256,8 +1226,6 @@ class Kodidb_Functions():
|
|||
|
||||
def addSeason(self, showid, seasonnumber):
|
||||
|
||||
cursor = self.cursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idSeason",
|
||||
|
@ -1265,30 +1233,28 @@ class Kodidb_Functions():
|
|||
"WHERE idShow = ?",
|
||||
"AND season = ?"
|
||||
))
|
||||
cursor.execute(query, (showid, seasonnumber,))
|
||||
self.cursor.execute(query, (showid, seasonnumber,))
|
||||
try:
|
||||
seasonid = cursor.fetchone()[0]
|
||||
seasonid = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
cursor.execute("select coalesce(max(idSeason),0) from seasons")
|
||||
seasonid = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(idSeason),0) from seasons")
|
||||
seasonid = self.cursor.fetchone()[0] + 1
|
||||
query = "INSERT INTO seasons(idSeason, idShow, season) values(?, ?, ?)"
|
||||
cursor.execute(query, (seasonid, showid, seasonnumber))
|
||||
self.cursor.execute(query, (seasonid, showid, seasonnumber))
|
||||
|
||||
return seasonid
|
||||
|
||||
def addArtist(self, name, musicbrainz):
|
||||
|
||||
cursor = self.cursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idArtist, strArtist",
|
||||
"FROM artist",
|
||||
"WHERE strMusicBrainzArtistID = ?"
|
||||
))
|
||||
cursor.execute(query, (musicbrainz,))
|
||||
self.cursor.execute(query, (musicbrainz,))
|
||||
try:
|
||||
result = cursor.fetchone()
|
||||
result = self.cursor.fetchone()
|
||||
artistid = result[0]
|
||||
artistname = result[1]
|
||||
|
||||
|
@ -1301,12 +1267,12 @@ class Kodidb_Functions():
|
|||
"WHERE strArtist = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (name,))
|
||||
self.cursor.execute(query, (name,))
|
||||
try:
|
||||
artistid = cursor.fetchone()[0]
|
||||
artistid = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
cursor.execute("select coalesce(max(idArtist),0) from artist")
|
||||
artistid = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(idArtist),0) from artist")
|
||||
artistid = self.cursor.fetchone()[0] + 1
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO artist(idArtist, strArtist, strMusicBrainzArtistID)
|
||||
|
@ -1314,33 +1280,30 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (artistid, name, musicbrainz))
|
||||
self.cursor.execute(query, (artistid, name, musicbrainz))
|
||||
else:
|
||||
if artistname != name:
|
||||
query = "UPDATE artist SET strArtist = ? WHERE idArtist = ?"
|
||||
cursor.execute(query, (name, artistid,))
|
||||
self.cursor.execute(query, (name, artistid,))
|
||||
|
||||
return artistid
|
||||
|
||||
def addAlbum(self, name, musicbrainz):
|
||||
|
||||
kodiversion = self.kodiversion
|
||||
cursor = self.cursor
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idAlbum",
|
||||
"FROM album",
|
||||
"WHERE strMusicBrainzAlbumID = ?"
|
||||
))
|
||||
cursor.execute(query, (musicbrainz,))
|
||||
self.cursor.execute(query, (musicbrainz,))
|
||||
try:
|
||||
albumid = cursor.fetchone()[0]
|
||||
albumid = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
# Create the album
|
||||
cursor.execute("select coalesce(max(idAlbum),0) from album")
|
||||
albumid = cursor.fetchone()[0] + 1
|
||||
if kodiversion in (15, 16, 17):
|
||||
self.cursor.execute("select coalesce(max(idAlbum),0) from album")
|
||||
albumid = self.cursor.fetchone()[0] + 1
|
||||
if self.kodiversion in (15, 16, 17):
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strReleaseType)
|
||||
|
@ -1348,7 +1311,7 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (albumid, name, musicbrainz, "album"))
|
||||
self.cursor.execute(query, (albumid, name, musicbrainz, "album"))
|
||||
else: # Helix
|
||||
query = (
|
||||
'''
|
||||
|
@ -1357,14 +1320,12 @@ class Kodidb_Functions():
|
|||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (albumid, name, musicbrainz))
|
||||
self.cursor.execute(query, (albumid, name, musicbrainz))
|
||||
|
||||
return albumid
|
||||
|
||||
def addMusicGenres(self, kodiid, genres, mediatype):
|
||||
|
||||
cursor = self.cursor
|
||||
|
||||
if mediatype == "album":
|
||||
|
||||
# Delete current genres for clean slate
|
||||
|
@ -1373,7 +1334,7 @@ class Kodidb_Functions():
|
|||
"DELETE FROM album_genre",
|
||||
"WHERE idAlbum = ?"
|
||||
))
|
||||
cursor.execute(query, (kodiid,))
|
||||
self.cursor.execute(query, (kodiid,))
|
||||
|
||||
for genre in genres:
|
||||
query = ' '.join((
|
||||
|
@ -1383,18 +1344,18 @@ class Kodidb_Functions():
|
|||
"WHERE strGenre = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (genre,))
|
||||
self.cursor.execute(query, (genre,))
|
||||
try:
|
||||
genreid = cursor.fetchone()[0]
|
||||
genreid = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
# Create the genre
|
||||
cursor.execute("select coalesce(max(idGenre),0) from genre")
|
||||
genreid = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(idGenre),0) from genre")
|
||||
genreid = self.cursor.fetchone()[0] + 1
|
||||
query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)"
|
||||
cursor.execute(query, (genreid, genre))
|
||||
self.cursor.execute(query, (genreid, genre))
|
||||
|
||||
query = "INSERT OR REPLACE INTO album_genre(idGenre, idAlbum) values(?, ?)"
|
||||
cursor.execute(query, (genreid, kodiid))
|
||||
self.cursor.execute(query, (genreid, kodiid))
|
||||
|
||||
elif mediatype == "song":
|
||||
|
||||
|
@ -1404,7 +1365,7 @@ class Kodidb_Functions():
|
|||
"DELETE FROM song_genre",
|
||||
"WHERE idSong = ?"
|
||||
))
|
||||
cursor.execute(query, (kodiid,))
|
||||
self.cursor.execute(query, (kodiid,))
|
||||
|
||||
for genre in genres:
|
||||
query = ' '.join((
|
||||
|
@ -1414,15 +1375,15 @@ class Kodidb_Functions():
|
|||
"WHERE strGenre = ?",
|
||||
"COLLATE NOCASE"
|
||||
))
|
||||
cursor.execute(query, (genre,))
|
||||
self.cursor.execute(query, (genre,))
|
||||
try:
|
||||
genreid = cursor.fetchone()[0]
|
||||
genreid = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
# Create the genre
|
||||
cursor.execute("select coalesce(max(idGenre),0) from genre")
|
||||
genreid = cursor.fetchone()[0] + 1
|
||||
self.cursor.execute("select coalesce(max(idGenre),0) from genre")
|
||||
genreid = self.cursor.fetchone()[0] + 1
|
||||
query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)"
|
||||
cursor.execute(query, (genreid, genre))
|
||||
self.cursor.execute(query, (genreid, genre))
|
||||
|
||||
query = "INSERT OR REPLACE INTO song_genre(idGenre, idSong) values(?, ?)"
|
||||
cursor.execute(query, (genreid, kodiid))
|
||||
self.cursor.execute(query, (genreid, kodiid))
|
|
@ -82,13 +82,13 @@ class KodiMonitor(xbmc.Monitor):
|
|||
item = data.get('item')
|
||||
try:
|
||||
kodiid = item['id']
|
||||
type = item['type']
|
||||
item_type = item['type']
|
||||
except (KeyError, TypeError):
|
||||
self.logMsg("Item is invalid for playstate update.", 1)
|
||||
else:
|
||||
# Send notification to the server.
|
||||
with embydb.GetEmbyDB() as emby_db:
|
||||
emby_dbitem = emby_db.getItem_byKodiId(kodiid, type)
|
||||
emby_dbitem = emby_db.getItem_byKodiId(kodiid, item_type)
|
||||
try:
|
||||
itemid = emby_dbitem[0]
|
||||
except TypeError:
|
||||
|
@ -137,7 +137,7 @@ class KodiMonitor(xbmc.Monitor):
|
|||
|
||||
url = "{server}/emby/Items/%s?format=json" % itemid
|
||||
self.logMsg("Deleting request: %s" % itemid)
|
||||
doUtils.downloadUrl(url, type="DELETE")
|
||||
doUtils.downloadUrl(url, action_type="DELETE")
|
||||
finally:
|
||||
embycursor.close()'''
|
||||
|
||||
|
|
|
@ -193,6 +193,7 @@ def getSongTags(file):
|
|||
if pic.type == 3 and pic.data:
|
||||
#the file has an embedded cover
|
||||
hasEmbeddedCover = True
|
||||
break
|
||||
if audio.get("rating"):
|
||||
rating = float(audio.get("rating")[0])
|
||||
#flac rating is 0-100 and needs to be converted to 0-5 range
|
||||
|
|
|
@ -44,7 +44,6 @@ class PlaybackUtils():
|
|||
|
||||
def play(self, itemid, dbid=None):
|
||||
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
settings = utils.settings
|
||||
|
||||
|
@ -56,7 +55,7 @@ class PlaybackUtils():
|
|||
listitem = xbmcgui.ListItem()
|
||||
playutils = putils.PlayUtils(item[0])
|
||||
|
||||
log("Play called.", 1)
|
||||
self.logMsg("Play called.", 1)
|
||||
playurl = playutils.getPlayUrl()
|
||||
if not playurl:
|
||||
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
|
||||
|
@ -101,9 +100,9 @@ class PlaybackUtils():
|
|||
introsPlaylist = False
|
||||
dummyPlaylist = False
|
||||
|
||||
log("Playlist start position: %s" % startPos, 1)
|
||||
log("Playlist plugin position: %s" % self.currentPosition, 1)
|
||||
log("Playlist size: %s" % sizePlaylist, 1)
|
||||
self.logMsg("Playlist start position: %s" % startPos, 2)
|
||||
self.logMsg("Playlist plugin position: %s" % currentPosition, 2)
|
||||
self.logMsg("Playlist size: %s" % sizePlaylist, 2)
|
||||
|
||||
############### RESUME POINT ################
|
||||
|
||||
|
@ -114,11 +113,11 @@ class PlaybackUtils():
|
|||
if not propertiesPlayback:
|
||||
|
||||
window('emby_playbackProps', value="true")
|
||||
log("Setting up properties in playlist.", 1)
|
||||
self.logMsg("Setting up properties in playlist.", 1)
|
||||
|
||||
if (not homeScreen and not seektime and
|
||||
window('emby_customPlaylist') != "true"):
|
||||
log("Adding dummy file to playlist.", 2)
|
||||
self.logMsg("Adding dummy file to playlist.", 2)
|
||||
dummyPlaylist = True
|
||||
playlist.add(playurl, listitem, index=startPos)
|
||||
# Remove the original item from playlist
|
||||
|
@ -181,13 +180,13 @@ class PlaybackUtils():
|
|||
if dummyPlaylist:
|
||||
# Added a dummy file to the playlist,
|
||||
# because the first item is going to fail automatically.
|
||||
log("Processed as a playlist. First item is skipped.", 1)
|
||||
self.logMsg("Processed as a playlist. First item is skipped.", 1)
|
||||
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
|
||||
|
||||
|
||||
# We just skipped adding properties. Reset flag for next time.
|
||||
elif propertiesPlayback:
|
||||
log("Resetting properties playback flag.", 2)
|
||||
self.logMsg("Resetting properties playback flag.", 2)
|
||||
window('emby_playbackProps', clear=True)
|
||||
|
||||
#self.pl.verifyPlaylist()
|
||||
|
@ -206,18 +205,18 @@ class PlaybackUtils():
|
|||
############### PLAYBACK ################
|
||||
|
||||
if homeScreen and seektime and window('emby_customPlaylist') != "true":
|
||||
log("Play as a widget item.", 1)
|
||||
self.logMsg("Play as a widget item.", 1)
|
||||
API.CreateListItemFromPlexItem(listitem)
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
|
||||
|
||||
elif ((introsPlaylist and window('emby_customPlaylist') == "true") or
|
||||
(homeScreen and not sizePlaylist)):
|
||||
# Playlist was created just now, play it.
|
||||
log("Play playlist.", 1)
|
||||
self.logMsg("Play playlist.", 1)
|
||||
xbmc.Player().play(playlist, startpos=startPos)
|
||||
|
||||
else:
|
||||
log("Play as a regular item.", 1)
|
||||
self.logMsg("Play as a regular item.", 1)
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
|
||||
|
||||
def AddTrailers(self, xml):
|
||||
|
|
|
@ -51,15 +51,13 @@ class Player(xbmc.Player):
|
|||
"""
|
||||
Window values need to have been set in Kodimonitor.py
|
||||
"""
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
# Will be called when xbmc starts playing a file
|
||||
xbmcplayer = self.xbmcplayer
|
||||
self.stopAll()
|
||||
|
||||
# Get current file (in utf-8!)
|
||||
try:
|
||||
currentFile = xbmcplayer.getPlayingFile()
|
||||
currentFile = self.xbmcplayer.getPlayingFile()
|
||||
xbmc.sleep(300)
|
||||
except:
|
||||
currentFile = ""
|
||||
|
@ -67,11 +65,11 @@ class Player(xbmc.Player):
|
|||
while not currentFile:
|
||||
xbmc.sleep(100)
|
||||
try:
|
||||
currentFile = xbmcplayer.getPlayingFile()
|
||||
currentFile = self.xbmcplayer.getPlayingFile()
|
||||
except:
|
||||
pass
|
||||
if count == 20:
|
||||
log("Cancelling playback report...", 1)
|
||||
self.logMsg("Cancelling playback report...", 1)
|
||||
break
|
||||
else:
|
||||
count += 1
|
||||
|
@ -220,6 +218,8 @@ class Player(xbmc.Player):
|
|||
except ValueError:
|
||||
runtime = xbmcplayer.getTotalTime()
|
||||
log("Runtime is missing, Kodi runtime: %s" % runtime, 1)
|
||||
runtime = self.xbmcplayer.getTotalTime()
|
||||
self.logMsg("Runtime is missing, Kodi runtime: %s" % runtime, 1)
|
||||
|
||||
playQueueVersion = window('playQueueVersion')
|
||||
playQueueID = window('playQueueID')
|
||||
|
@ -263,7 +263,7 @@ class Player(xbmc.Player):
|
|||
if not self.doNotify:
|
||||
return
|
||||
|
||||
log = self.logMsg
|
||||
self.logMsg("reportPlayback Called", 2)
|
||||
|
||||
log("reportPlayback Called", 2)
|
||||
|
||||
|
@ -382,7 +382,7 @@ class Player(xbmc.Player):
|
|||
|
||||
if mapping: # Set in PlaybackUtils.py
|
||||
|
||||
log("Mapping for external subtitles index: %s" % mapping, 2)
|
||||
self.logMsg("Mapping for external subtitles index: %s" % mapping, 2)
|
||||
externalIndex = json.loads(mapping)
|
||||
|
||||
if externalIndex.get(str(indexSubs)):
|
||||
|
@ -404,7 +404,7 @@ class Player(xbmc.Player):
|
|||
# postdata = json.dumps(postdata)
|
||||
# self.ws.sendProgressUpdate(postdata)
|
||||
self.doUtils(
|
||||
"{server}/:/timeline?" + urlencode(postdata), type="GET")
|
||||
"{server}/:/timeline?" + urlencode(postdata), action_type="GET")
|
||||
|
||||
def onPlayBackPaused(self):
|
||||
|
||||
|
@ -440,7 +440,6 @@ class Player(xbmc.Player):
|
|||
def onPlayBackStopped(self):
|
||||
# Will be called when user stops xbmc playing a file
|
||||
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
log("ONPLAYBACK_STOPPED", 1)
|
||||
|
||||
|
@ -459,31 +458,28 @@ class Player(xbmc.Player):
|
|||
|
||||
def stopAll(self):
|
||||
|
||||
log = self.logMsg
|
||||
lang = utils.language
|
||||
settings = utils.settings
|
||||
|
||||
doUtils = self.doUtils
|
||||
|
||||
if not self.played_info:
|
||||
return
|
||||
|
||||
log("Played_information: %s" % self.played_info, 1)
|
||||
self.logMsg("Played_information: %s" % self.played_info, 1)
|
||||
# Process each items
|
||||
for item in self.played_info:
|
||||
|
||||
data = self.played_info.get(item)
|
||||
if data:
|
||||
|
||||
log("Item path: %s" % item, 2)
|
||||
log("Item data: %s" % data, 2)
|
||||
self.logMsg("Item path: %s" % item, 2)
|
||||
self.logMsg("Item data: %s" % data, 2)
|
||||
|
||||
runtime = data['runtime']
|
||||
currentPosition = data['currentPosition']
|
||||
itemid = data['item_id']
|
||||
refresh_id = data['refresh_id']
|
||||
currentFile = data['currentfile']
|
||||
type = data['Type']
|
||||
media_type = data['Type']
|
||||
playMethod = data['playmethod']
|
||||
|
||||
# Prevent manually mark as watched in Kodi monitor
|
||||
|
@ -497,15 +493,15 @@ class Player(xbmc.Player):
|
|||
percentComplete = 0
|
||||
|
||||
markPlayedAt = float(settings('markPlayed')) / 100
|
||||
log("Percent complete: %s Mark played at: %s"
|
||||
self.logMsg("Percent complete: %s Mark played at: %s"
|
||||
% (percentComplete, markPlayedAt), 1)
|
||||
|
||||
# Send the delete action to the server.
|
||||
offerDelete = False
|
||||
|
||||
if type == "Episode" and settings('deleteTV') == "true":
|
||||
if media_type == "Episode" and settings('deleteTV') == "true":
|
||||
offerDelete = True
|
||||
elif type == "Movie" and settings('deleteMovies') == "true":
|
||||
elif media_type == "Movie" and settings('deleteMovies') == "true":
|
||||
offerDelete = True
|
||||
|
||||
if settings('offerDelete') != "true":
|
||||
|
@ -520,7 +516,7 @@ class Player(xbmc.Player):
|
|||
lang(33015),
|
||||
autoclose=120000)
|
||||
if not resp:
|
||||
log("User skipped deletion.", 1)
|
||||
self.logMsg("User skipped deletion.", 1)
|
||||
continue
|
||||
|
||||
url = "{server}/emby/Items/%s?format=json" % itemid
|
||||
|
@ -567,4 +563,4 @@ class Player(xbmc.Player):
|
|||
'duration': int(duration)
|
||||
}
|
||||
url = url + urlencode(args)
|
||||
self.doUtils(url, type="GET")
|
||||
self.doUtils(url, action_type="GET")
|
||||
|
|
|
@ -27,7 +27,6 @@ class Playlist():
|
|||
self.emby = embyserver.Read_EmbyServer()
|
||||
|
||||
def playAll(self, itemids, startat):
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
|
||||
embyconn = utils.kodiSQL('emby')
|
||||
|
@ -38,8 +37,8 @@ class Playlist():
|
|||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
playlist.clear()
|
||||
|
||||
log("---*** PLAY ALL ***---", 1)
|
||||
log("Items: %s and start at: %s" % (itemids, startat), 1)
|
||||
self.logMsg("---*** PLAY ALL ***---", 1)
|
||||
self.logMsg("Items: %s and start at: %s" % (itemids, startat), 1)
|
||||
|
||||
started = False
|
||||
window('emby_customplaylist', value="true")
|
||||
|
@ -76,14 +75,12 @@ class Playlist():
|
|||
|
||||
def modifyPlaylist(self, itemids):
|
||||
|
||||
log = self.logMsg
|
||||
|
||||
embyconn = utils.kodiSQL('emby')
|
||||
embycursor = embyconn.cursor()
|
||||
emby_db = embydb.Embydb_Functions(embycursor)
|
||||
|
||||
log("---*** ADD TO PLAYLIST ***---", 1)
|
||||
log("Items: %s" % itemids, 1)
|
||||
self.logMsg("---*** ADD TO PLAYLIST ***---", 1)
|
||||
self.logMsg("Items: %s" % itemids, 1)
|
||||
|
||||
# player = xbmc.Player()
|
||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
|
@ -101,7 +98,7 @@ class Playlist():
|
|||
# Add to playlist
|
||||
self.addtoPlaylist(dbid, mediatype)
|
||||
|
||||
log("Adding %s to playlist." % itemid, 1)
|
||||
self.logMsg("Adding %s to playlist." % itemid, 1)
|
||||
|
||||
self.verifyPlaylist()
|
||||
embycursor.close()
|
||||
|
@ -124,8 +121,7 @@ class Playlist():
|
|||
else:
|
||||
pl['params']['item'] = {'file': url}
|
||||
|
||||
result = xbmc.executeJSONRPC(json.dumps(pl))
|
||||
self.logMsg(result, 2)
|
||||
self.logMsg(xbmc.executeJSONRPC(json.dumps(pl)), 2)
|
||||
|
||||
def addtoPlaylist_xbmc(self, playlist, item):
|
||||
path = "plugin://plugin.video.plexkodiconnect.movies/"
|
||||
|
@ -160,8 +156,7 @@ class Playlist():
|
|||
else:
|
||||
pl['params']['item'] = {'file': url}
|
||||
|
||||
result = xbmc.executeJSONRPC(json.dumps(pl))
|
||||
self.logMsg(result, 2)
|
||||
self.logMsg(xbmc.executeJSONRPC(json.dumps(pl)), 2)
|
||||
|
||||
def verifyPlaylist(self):
|
||||
|
||||
|
@ -176,8 +171,7 @@ class Playlist():
|
|||
'properties': ['title', 'file']
|
||||
}
|
||||
}
|
||||
result = xbmc.executeJSONRPC(json.dumps(pl))
|
||||
self.logMsg(result, 2)
|
||||
self.logMsg(xbmc.executeJSONRPC(json.dumps(pl)), 2)
|
||||
|
||||
def removefromPlaylist(self, position):
|
||||
|
||||
|
@ -192,5 +186,4 @@ class Playlist():
|
|||
'position': position
|
||||
}
|
||||
}
|
||||
result = xbmc.executeJSONRPC(json.dumps(pl))
|
||||
self.logMsg(result, 2)
|
||||
self.logMsg(xbmc.executeJSONRPC(json.dumps(pl)), 2)
|
||||
|
|
|
@ -56,7 +56,7 @@ class PlayUtils():
|
|||
utils.window('emby_%s.playmethod' % playurl, "DirectStream")
|
||||
|
||||
elif self.isTranscoding():
|
||||
log("File is transcoding.", 1)
|
||||
self.logMsg("File is transcoding.", 1)
|
||||
quality = {
|
||||
'maxVideoBitrate': self.getBitrate(),
|
||||
'videoResolution': self.getResolution(),
|
||||
|
@ -73,16 +73,14 @@ class PlayUtils():
|
|||
|
||||
def httpPlay(self):
|
||||
# Audio, Video, Photo
|
||||
item = self.item
|
||||
server = self.server
|
||||
|
||||
itemid = item['Id']
|
||||
mediatype = item['MediaType']
|
||||
itemid = self.item['Id']
|
||||
mediatype = self.item['MediaType']
|
||||
|
||||
if mediatype == "Audio":
|
||||
playurl = "%s/emby/Audio/%s/stream" % (server, itemid)
|
||||
playurl = "%s/emby/Audio/%s/stream" % (self.server, itemid)
|
||||
else:
|
||||
playurl = "%s/emby/Videos/%s/stream?static=true" % (server, itemid)
|
||||
playurl = "%s/emby/Videos/%s/stream?static=true" % (self.server, itemid)
|
||||
|
||||
return playurl
|
||||
|
||||
|
@ -117,20 +115,16 @@ class PlayUtils():
|
|||
|
||||
def directPlay(self):
|
||||
|
||||
item = self.item
|
||||
|
||||
try:
|
||||
playurl = item['MediaSources'][0]['Path']
|
||||
playurl = self.item['MediaSources'][0]['Path']
|
||||
except (IndexError, KeyError):
|
||||
playurl = item['Path']
|
||||
playurl = self.item['Path']
|
||||
|
||||
if item.get('VideoType'):
|
||||
if self.item.get('VideoType'):
|
||||
# Specific format modification
|
||||
type = item['VideoType']
|
||||
|
||||
if type == "Dvd":
|
||||
if self.item['VideoType'] == "Dvd":
|
||||
playurl = "%s/VIDEO_TS/VIDEO_TS.IFO" % playurl
|
||||
elif type == "BluRay":
|
||||
elif self.item['VideoType'] == "BluRay":
|
||||
playurl = "%s/BDMV/index.bdmv" % playurl
|
||||
|
||||
# Assign network protocol
|
||||
|
@ -146,26 +140,24 @@ class PlayUtils():
|
|||
|
||||
def fileExists(self):
|
||||
|
||||
log = self.logMsg
|
||||
|
||||
if 'Path' not in self.item:
|
||||
# File has no path defined in server
|
||||
return False
|
||||
|
||||
# Convert path to direct play
|
||||
path = self.directPlay()
|
||||
log("Verifying path: %s" % path, 1)
|
||||
self.logMsg("Verifying path: %s" % path, 1)
|
||||
|
||||
if xbmcvfs.exists(path):
|
||||
log("Path exists.", 1)
|
||||
self.logMsg("Path exists.", 1)
|
||||
return True
|
||||
|
||||
elif ":" not in path:
|
||||
log("Can't verify path, assumed linux. Still try to direct play.", 1)
|
||||
self.logMsg("Can't verify path, assumed linux. Still try to direct play.", 1)
|
||||
return True
|
||||
|
||||
else:
|
||||
log("Failed to find file.", 1)
|
||||
self.logMsg("Failed to find file.", 1)
|
||||
return False
|
||||
|
||||
def h265enabled(self):
|
||||
|
@ -197,6 +189,8 @@ class PlayUtils():
|
|||
if utils.settings('playType') == "2":
|
||||
# User forcing to play via HTTP
|
||||
self.logMsg("User chose to transcode", 1)
|
||||
self.logMsg("Resolution is: %sP, transcode for resolution: %sP+"
|
||||
canDirectStream = self.item['MediaSources'][0]['SupportsDirectStream']
|
||||
return False
|
||||
if self.h265enabled():
|
||||
return False
|
||||
|
@ -244,26 +238,19 @@ class PlayUtils():
|
|||
return True
|
||||
|
||||
def isTranscoding(self):
|
||||
# I hope Plex transcodes everything
|
||||
return True
|
||||
item = self.item
|
||||
|
||||
canTranscode = item['MediaSources'][0]['SupportsTranscoding']
|
||||
# Make sure the server supports it
|
||||
if not canTranscode:
|
||||
if not self.item['MediaSources'][0]['SupportsTranscoding']:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def transcoding(self):
|
||||
|
||||
item = self.item
|
||||
|
||||
if 'Path' in item and item['Path'].endswith('.strm'):
|
||||
if 'Path' in self.item and self.item['Path'].endswith('.strm'):
|
||||
# Allow strm loading when transcoding
|
||||
playurl = self.directPlay()
|
||||
else:
|
||||
itemid = item['Id']
|
||||
itemid = self.item['Id']
|
||||
deviceId = self.clientInfo.getDeviceId()
|
||||
playurl = (
|
||||
"%s/emby/Videos/%s/master.m3u8?MediaSourceId=%s"
|
||||
|
|
|
@ -31,8 +31,7 @@ class Read_EmbyServer():
|
|||
# This will return the full item
|
||||
item = {}
|
||||
|
||||
url = "{server}/emby/Users/{UserId}/Items/%s?format=json" % itemid
|
||||
result = self.doUtils(url)
|
||||
result = self.doUtils("{server}/emby/Users/{UserId}/Items/%s?format=json" % itemid)
|
||||
if result:
|
||||
item = result
|
||||
|
||||
|
@ -45,13 +44,12 @@ class Read_EmbyServer():
|
|||
itemlists = self.split_list(itemlist, 50)
|
||||
for itemlist in itemlists:
|
||||
# Will return basic information
|
||||
url = "{server}/emby/Users/{UserId}/Items?&format=json"
|
||||
params = {
|
||||
|
||||
'Ids': ",".join(itemlist),
|
||||
'Fields': "Etag"
|
||||
}
|
||||
result = self.doUtils(url, parameters=params)
|
||||
result = self.doUtils("{server}/emby/Users/{UserId}/Items?&format=json", parameters=params)
|
||||
if result:
|
||||
items.extend(result['Items'])
|
||||
|
||||
|
@ -64,7 +62,6 @@ class Read_EmbyServer():
|
|||
itemlists = self.split_list(itemlist, 50)
|
||||
for itemlist in itemlists:
|
||||
|
||||
url = "{server}/emby/Users/{UserId}/Items?format=json"
|
||||
params = {
|
||||
|
||||
"Ids": ",".join(itemlist),
|
||||
|
@ -75,10 +72,10 @@ class Read_EmbyServer():
|
|||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,"
|
||||
"MediaSources"
|
||||
"MediaSources,VoteCount"
|
||||
)
|
||||
}
|
||||
result = self.doUtils(url, parameters=params)
|
||||
result = self.doUtils("{server}/emby/Users/{UserId}/Items?format=json", parameters=params)
|
||||
if result:
|
||||
items.extend(result['Items'])
|
||||
|
||||
|
@ -87,13 +84,10 @@ class Read_EmbyServer():
|
|||
def getView_embyId(self, itemid):
|
||||
# Returns ancestors using embyId
|
||||
viewId = None
|
||||
url = "{server}/emby/Items/%s/Ancestors?UserId={UserId}&format=json" % itemid
|
||||
result = self.doUtils(url)
|
||||
|
||||
for view in result:
|
||||
for view in self.doUtils("{server}/emby/Items/%s/Ancestors?UserId={UserId}&format=json" % itemid):
|
||||
|
||||
viewtype = view['Type']
|
||||
if viewtype == "CollectionFolder":
|
||||
if view['Type'] == "CollectionFolder":
|
||||
# Found view
|
||||
viewId = view['Id']
|
||||
|
||||
|
@ -120,8 +114,6 @@ class Read_EmbyServer():
|
|||
return [viewName, viewId, mediatype]
|
||||
|
||||
def getFilteredSection(self, parentid, itemtype=None, sortby="SortName", recursive=True, limit=None, sortorder="Ascending", filter=""):
|
||||
doUtils = self.doUtils
|
||||
url = "{server}/emby/Users/{UserId}/Items?format=json"
|
||||
params = {
|
||||
|
||||
'ParentId': parentid,
|
||||
|
@ -140,11 +132,9 @@ class Read_EmbyServer():
|
|||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
|
||||
}
|
||||
return doUtils(url, parameters=params)
|
||||
return self.doUtils("{server}/emby/Users/{UserId}/Items?format=json", parameters=params)
|
||||
|
||||
def getTvChannels(self):
|
||||
doUtils = self.doUtils
|
||||
url = "{server}/emby/LiveTv/Channels/?userid={UserId}&format=json"
|
||||
params = {
|
||||
|
||||
'EnableImages': True,
|
||||
|
@ -154,11 +144,9 @@ class Read_EmbyServer():
|
|||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
|
||||
}
|
||||
return doUtils(url, parameters=params)
|
||||
return self.doUtils("{server}/emby/LiveTv/Channels/?userid={UserId}&format=json", parameters=params)
|
||||
|
||||
def getTvRecordings(self, groupid):
|
||||
doUtils = self.doUtils
|
||||
url = "{server}/emby/LiveTv/Recordings/?userid={UserId}&format=json"
|
||||
if groupid == "root": groupid = ""
|
||||
params = {
|
||||
|
||||
|
@ -170,13 +158,10 @@ class Read_EmbyServer():
|
|||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
|
||||
}
|
||||
return doUtils(url, parameters=params)
|
||||
return self.doUtils("{server}/emby/LiveTv/Recordings/?userid={UserId}&format=json", parameters=params)
|
||||
|
||||
def getSection(self, parentid, itemtype=None, sortby="SortName", basic=False, dialog=None):
|
||||
|
||||
log = self.logMsg
|
||||
|
||||
doUtils = self.doUtils
|
||||
items = {
|
||||
|
||||
'Items': [],
|
||||
|
@ -195,13 +180,13 @@ class Read_EmbyServer():
|
|||
'Recursive': True,
|
||||
'Limit': 1
|
||||
}
|
||||
result = doUtils(url, parameters=params)
|
||||
result = self.doUtils(url, parameters=params)
|
||||
try:
|
||||
total = result['TotalRecordCount']
|
||||
items['TotalRecordCount'] = total
|
||||
|
||||
except TypeError: # Failed to retrieve
|
||||
log("%s:%s Failed to retrieve the server response." % (url, params), 2)
|
||||
self.logMsg("%s:%s Failed to retrieve the server response." % (url, params), 2)
|
||||
|
||||
else:
|
||||
index = 0
|
||||
|
@ -234,36 +219,36 @@ class Read_EmbyServer():
|
|||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers,"
|
||||
"MediaSources"
|
||||
"MediaSources,VoteCount"
|
||||
)
|
||||
result = doUtils(url, parameters=params)
|
||||
result = self.doUtils(url, parameters=params)
|
||||
try:
|
||||
items['Items'].extend(result['Items'])
|
||||
except TypeError:
|
||||
# Something happened to the connection
|
||||
if not throttled:
|
||||
throttled = True
|
||||
log("Throttle activated.", 1)
|
||||
self.logMsg("Throttle activated.", 1)
|
||||
|
||||
if jump == highestjump:
|
||||
# We already tried with the highestjump, but it failed. Reset value.
|
||||
log("Reset highest value.", 1)
|
||||
self.logMsg("Reset highest value.", 1)
|
||||
highestjump = 0
|
||||
|
||||
# Lower the number by half
|
||||
if highestjump:
|
||||
throttled = False
|
||||
jump = highestjump
|
||||
log("Throttle deactivated.", 1)
|
||||
self.logMsg("Throttle deactivated.", 1)
|
||||
else:
|
||||
jump = int(jump/4)
|
||||
log("Set jump limit to recover: %s" % jump, 2)
|
||||
self.logMsg("Set jump limit to recover: %s" % jump, 2)
|
||||
|
||||
retry = 0
|
||||
while utils.window('emby_online') != "true":
|
||||
# Wait server to come back online
|
||||
if retry == 5:
|
||||
log("Unable to reconnect to server. Abort process.", 1)
|
||||
self.logMsg("Unable to reconnect to server. Abort process.", 1)
|
||||
return items
|
||||
|
||||
retry += 1
|
||||
|
@ -291,12 +276,11 @@ class Read_EmbyServer():
|
|||
increment = 10
|
||||
|
||||
jump += increment
|
||||
log("Increase jump limit to: %s" % jump, 1)
|
||||
self.logMsg("Increase jump limit to: %s" % jump, 1)
|
||||
return items
|
||||
|
||||
def getViews(self, mediatype="", root=False, sortedlist=False):
|
||||
# Build a list of user views
|
||||
doUtils = self.doUtils
|
||||
views = []
|
||||
mediatype = mediatype.lower()
|
||||
|
||||
|
@ -305,7 +289,7 @@ class Read_EmbyServer():
|
|||
else: # Views ungrouped
|
||||
url = "{server}/emby/Users/{UserId}/Items?Sortby=SortName&format=json"
|
||||
|
||||
result = doUtils(url)
|
||||
result = self.doUtils(url)
|
||||
try:
|
||||
items = result['Items']
|
||||
except TypeError:
|
||||
|
@ -313,11 +297,8 @@ class Read_EmbyServer():
|
|||
else:
|
||||
for item in items:
|
||||
|
||||
name = item['Name']
|
||||
itemId = item['Id']
|
||||
viewtype = item['Type']
|
||||
|
||||
if viewtype == "Channel":
|
||||
item['Name'] = item['Name']
|
||||
if item['Type'] == "Channel":
|
||||
# Filter view types
|
||||
continue
|
||||
|
||||
|
@ -328,20 +309,20 @@ class Read_EmbyServer():
|
|||
# Assumed missing is mixed then.
|
||||
'''if itemtype is None:
|
||||
url = "{server}/emby/Library/MediaFolders?format=json"
|
||||
result = doUtils(url)
|
||||
result = self.doUtils(url)
|
||||
|
||||
for folder in result['Items']:
|
||||
if itemId == folder['Id']:
|
||||
if item['Id'] == folder['Id']:
|
||||
itemtype = folder.get('CollectionType', "mixed")'''
|
||||
|
||||
if name not in ('Collections', 'Trailers'):
|
||||
if item['Name'] not in ('Collections', 'Trailers'):
|
||||
|
||||
if sortedlist:
|
||||
views.append({
|
||||
|
||||
'name': name,
|
||||
'name': item['Name'],
|
||||
'type': itemtype,
|
||||
'id': itemId
|
||||
'id': item['Id']
|
||||
})
|
||||
|
||||
elif (itemtype == mediatype or
|
||||
|
@ -349,9 +330,9 @@ class Read_EmbyServer():
|
|||
|
||||
views.append({
|
||||
|
||||
'name': name,
|
||||
'name': item['Name'],
|
||||
'type': itemtype,
|
||||
'id': itemId
|
||||
'id': item['Id']
|
||||
})
|
||||
|
||||
return views
|
||||
|
@ -359,8 +340,6 @@ class Read_EmbyServer():
|
|||
def verifyView(self, parentid, itemid):
|
||||
|
||||
belongs = False
|
||||
|
||||
url = "{server}/emby/Users/{UserId}/Items?format=json"
|
||||
params = {
|
||||
|
||||
'ParentId': parentid,
|
||||
|
@ -370,7 +349,7 @@ class Read_EmbyServer():
|
|||
'Recursive': True,
|
||||
'Ids': itemid
|
||||
}
|
||||
result = self.doUtils(url, parameters=params)
|
||||
result = self.doUtils("{server}/emby/Users/{UserId}/Items?format=json", parameters=params)
|
||||
try:
|
||||
total = result['TotalRecordCount']
|
||||
except TypeError:
|
||||
|
@ -383,40 +362,23 @@ class Read_EmbyServer():
|
|||
return belongs
|
||||
|
||||
def getMovies(self, parentId, basic=False, dialog=None):
|
||||
|
||||
items = self.getSection(parentId, "Movie", basic=basic, dialog=dialog)
|
||||
|
||||
return items
|
||||
return self.getSection(parentId, "Movie", basic=basic, dialog=dialog)
|
||||
|
||||
def getBoxset(self, dialog=None):
|
||||
|
||||
items = self.getSection(None, "BoxSet", dialog=dialog)
|
||||
|
||||
return items
|
||||
return self.getSection(None, "BoxSet", dialog=dialog)
|
||||
|
||||
def getMovies_byBoxset(self, boxsetid):
|
||||
|
||||
items = self.getSection(boxsetid, "Movie")
|
||||
|
||||
return items
|
||||
return self.getSection(boxsetid, "Movie")
|
||||
|
||||
def getMusicVideos(self, parentId, basic=False, dialog=None):
|
||||
|
||||
items = self.getSection(parentId, "MusicVideo", basic=basic, dialog=dialog)
|
||||
|
||||
return items
|
||||
return self.getSection(parentId, "MusicVideo", basic=basic, dialog=dialog)
|
||||
|
||||
def getHomeVideos(self, parentId):
|
||||
|
||||
items = self.getSection(parentId, "Video")
|
||||
|
||||
return items
|
||||
return self.getSection(parentId, "Video")
|
||||
|
||||
def getShows(self, parentId, basic=False, dialog=None):
|
||||
|
||||
items = self.getSection(parentId, "Series", basic=basic, dialog=dialog)
|
||||
|
||||
return items
|
||||
return self.getSection(parentId, "Series", basic=basic, dialog=dialog)
|
||||
|
||||
def getSeasons(self, showId):
|
||||
|
||||
|
@ -426,13 +388,12 @@ class Read_EmbyServer():
|
|||
'TotalRecordCount': 0
|
||||
}
|
||||
|
||||
url = "{server}/emby/Shows/%s/Seasons?UserId={UserId}&format=json" % showId
|
||||
params = {
|
||||
|
||||
'IsVirtualUnaired': False,
|
||||
'Fields': "Etag"
|
||||
}
|
||||
result = self.doUtils(url, parameters=params)
|
||||
result = self.doUtils("{server}/emby/Shows/%s/Seasons?UserId={UserId}&format=json" % showId, parameters=params)
|
||||
if result:
|
||||
items = result
|
||||
|
||||
|
@ -440,25 +401,19 @@ class Read_EmbyServer():
|
|||
|
||||
def getEpisodes(self, parentId, basic=False, dialog=None):
|
||||
|
||||
items = self.getSection(parentId, "Episode", basic=basic, dialog=dialog)
|
||||
|
||||
return items
|
||||
return self.getSection(parentId, "Episode", basic=basic, dialog=dialog)
|
||||
|
||||
def getEpisodesbyShow(self, showId):
|
||||
|
||||
items = self.getSection(showId, "Episode")
|
||||
|
||||
return items
|
||||
return self.getSection(showId, "Episode")
|
||||
|
||||
def getEpisodesbySeason(self, seasonId):
|
||||
|
||||
items = self.getSection(seasonId, "Episode")
|
||||
return self.getSection(seasonId, "Episode")
|
||||
|
||||
return items
|
||||
|
||||
def getArtists(self, dialog=None):
|
||||
|
||||
doUtils = self.doUtils
|
||||
items = {
|
||||
|
||||
'Items': [],
|
||||
|
@ -472,7 +427,7 @@ class Read_EmbyServer():
|
|||
'Recursive': True,
|
||||
'Limit': 1
|
||||
}
|
||||
result = doUtils(url, parameters=params)
|
||||
result = self.doUtils(url, parameters=params)
|
||||
try:
|
||||
total = result['TotalRecordCount']
|
||||
items['TotalRecordCount'] = total
|
||||
|
@ -502,7 +457,7 @@ class Read_EmbyServer():
|
|||
"AirTime,DateCreated,MediaStreams,People,ProviderIds,Overview"
|
||||
)
|
||||
}
|
||||
result = doUtils(url, parameters=params)
|
||||
result = self.doUtils(url, parameters=params)
|
||||
items['Items'].extend(result['Items'])
|
||||
|
||||
index += jump
|
||||
|
@ -512,28 +467,17 @@ class Read_EmbyServer():
|
|||
return items
|
||||
|
||||
def getAlbums(self, basic=False, dialog=None):
|
||||
|
||||
items = self.getSection(None, "MusicAlbum", sortby="DateCreated", basic=basic, dialog=dialog)
|
||||
|
||||
return items
|
||||
return self.getSection(None, "MusicAlbum", sortby="DateCreated", basic=basic, dialog=dialog)
|
||||
|
||||
def getAlbumsbyArtist(self, artistId):
|
||||
|
||||
items = self.getSection(artistId, "MusicAlbum", sortby="DateCreated")
|
||||
|
||||
return items
|
||||
return self.getSection(artistId, "MusicAlbum", sortby="DateCreated")
|
||||
|
||||
def getSongs(self, basic=False, dialog=None):
|
||||
|
||||
items = self.getSection(None, "Audio", basic=basic, dialog=dialog)
|
||||
|
||||
return items
|
||||
return self.getSection(None, "Audio", basic=basic, dialog=dialog)
|
||||
|
||||
def getSongsbyAlbum(self, albumId):
|
||||
return self.getSection(albumId, "Audio")
|
||||
|
||||
items = self.getSection(albumId, "Audio")
|
||||
|
||||
return items
|
||||
|
||||
def getAdditionalParts(self, itemId):
|
||||
|
||||
|
@ -543,8 +487,7 @@ class Read_EmbyServer():
|
|||
'TotalRecordCount': 0
|
||||
}
|
||||
|
||||
url = "{server}/emby/Videos/%s/AdditionalParts?UserId={UserId}&format=json" % itemId
|
||||
result = self.doUtils(url)
|
||||
result = self.doUtils("{server}/emby/Videos/%s/AdditionalParts?UserId={UserId}&format=json" % itemId)
|
||||
if result:
|
||||
items = result
|
||||
|
||||
|
@ -566,24 +509,20 @@ class Read_EmbyServer():
|
|||
|
||||
def updateUserRating(self, itemid, like=None, favourite=None, deletelike=False):
|
||||
# Updates the user rating to Emby
|
||||
doUtils = self.doUtils
|
||||
|
||||
if favourite:
|
||||
url = "{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid
|
||||
doUtils(url, type="POST")
|
||||
self.doUtils("{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid, action_type="POST")
|
||||
elif favourite == False:
|
||||
url = "{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid
|
||||
doUtils(url, type="DELETE")
|
||||
self.doUtils("{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid, action_type="DELETE")
|
||||
|
||||
if not deletelike and like:
|
||||
url = "{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=true&format=json" % itemid
|
||||
doUtils(url, type="POST")
|
||||
elif not deletelike and like == False:
|
||||
url = "{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=false&format=json" % itemid
|
||||
doUtil(url, type="POST")
|
||||
self.doUtils("{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=true&format=json" % itemid, action_type="POST")
|
||||
elif not deletelike and like is False:
|
||||
self.doUtils("{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=false&format=json" % itemid, action_type="POST")
|
||||
elif deletelike:
|
||||
url = "{server}/emby/Users/{UserId}/Items/%s/Rating?format=json" % itemid
|
||||
doUtils(url, type="DELETE")
|
||||
self.doUtils("{server}/emby/Users/{UserId}/Items/%s/Rating?format=json" % itemid, action_type="DELETE")
|
||||
else:
|
||||
self.logMsg("Error processing user rating.", 1)
|
||||
|
||||
self.logMsg("Update user rating to emby for itemid: %s "
|
||||
"| like: %s | favourite: %s | deletelike: %s"
|
||||
|
|
|
@ -334,13 +334,13 @@ def language(stringid):
|
|||
string = addon.getLocalizedString(stringid) #returns unicode object
|
||||
return string
|
||||
|
||||
def kodiSQL(type="video"):
|
||||
def kodiSQL(media_type="video"):
|
||||
|
||||
if type == "emby":
|
||||
if media_type == "emby":
|
||||
dbPath = xbmc.translatePath("special://database/emby.db").decode('utf-8')
|
||||
elif type == "music":
|
||||
elif media_type == "music":
|
||||
dbPath = getKodiMusicDBPath()
|
||||
elif type == "texture":
|
||||
elif media_type == "texture":
|
||||
dbPath = xbmc.translatePath("special://database/Textures13.db").decode('utf-8')
|
||||
else:
|
||||
dbPath = getKodiVideoDBPath()
|
||||
|
@ -350,7 +350,6 @@ def kodiSQL(type="video"):
|
|||
|
||||
def getKodiVideoDBPath():
|
||||
|
||||
kodibuild = xbmc.getInfoLabel('System.BuildVersion')[:2]
|
||||
dbVersion = {
|
||||
|
||||
"13": 78, # Gotham
|
||||
|
@ -358,16 +357,16 @@ def getKodiVideoDBPath():
|
|||
"15": 93, # Isengard
|
||||
"16": 99, # Jarvis
|
||||
"17":104 # Krypton
|
||||
"17": 104 # Krypton
|
||||
}
|
||||
|
||||
dbPath = xbmc.translatePath(
|
||||
"special://database/MyVideos%s.db"
|
||||
% dbVersion.get(kodibuild, "")).decode('utf-8')
|
||||
% dbVersion.get(xbmc.getInfoLabel('System.BuildVersion')[:2], "")).decode('utf-8')
|
||||
return dbPath
|
||||
|
||||
def getKodiMusicDBPath():
|
||||
|
||||
kodibuild = xbmc.getInfoLabel('System.BuildVersion')[:2]
|
||||
dbVersion = {
|
||||
|
||||
"13": 46, # Gotham
|
||||
|
@ -379,7 +378,7 @@ def getKodiMusicDBPath():
|
|||
|
||||
dbPath = xbmc.translatePath(
|
||||
"special://database/MyMusic%s.db"
|
||||
% dbVersion.get(kodibuild, "")).decode('utf-8')
|
||||
% dbVersion.get(xbmc.getInfoLabel('System.BuildVersion')[:2], "")).decode('utf-8')
|
||||
return dbPath
|
||||
|
||||
def getScreensaver():
|
||||
|
@ -394,11 +393,7 @@ def getScreensaver():
|
|||
'setting': "screensaver.mode"
|
||||
}
|
||||
}
|
||||
result = xbmc.executeJSONRPC(json.dumps(query))
|
||||
result = json.loads(result)
|
||||
screensaver = result['result']['value']
|
||||
|
||||
return screensaver
|
||||
return json.loads(xbmc.executeJSONRPC(json.dumps(query)))['result']['value']
|
||||
|
||||
def setScreensaver(value):
|
||||
# Toggle the screensaver
|
||||
|
@ -413,15 +408,13 @@ def setScreensaver(value):
|
|||
'value': value
|
||||
}
|
||||
}
|
||||
result = xbmc.executeJSONRPC(json.dumps(query))
|
||||
logMsg("PLEX", "Toggling screensaver: %s %s" % (value, result), 1)
|
||||
logMsg("PLEX", "Toggling screensaver: %s %s" % (value, xbmc.executeJSONRPC(json.dumps(query))), 1)
|
||||
|
||||
def reset():
|
||||
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
resp = dialog.yesno("Warning", "Are you sure you want to reset your local Kodi database?")
|
||||
if resp == 0:
|
||||
if dialog.yesno("Warning", "Are you sure you want to reset your local Kodi database?") == 0:
|
||||
return
|
||||
|
||||
# first stop any db sync
|
||||
|
@ -483,7 +476,7 @@ def reset():
|
|||
cursor.close()
|
||||
|
||||
# Offer to wipe cached thumbnails
|
||||
resp = dialog.yesno("Warning", "Removed all cached artwork?")
|
||||
resp = dialog.yesno("Warning", "Remove all cached artwork?")
|
||||
if resp:
|
||||
logMsg("EMBY", "Resetting all cached artwork.", 0)
|
||||
# Remove all existing textures first
|
||||
|
@ -775,9 +768,7 @@ def passwordsXML():
|
|||
|
||||
elif option == 1:
|
||||
# User selected remove
|
||||
iterator = root.getiterator('passwords')
|
||||
|
||||
for paths in iterator:
|
||||
for paths in root.getiterator('passwords'):
|
||||
for path in paths:
|
||||
if path.find('.//from').text == "smb://%s/" % credentials:
|
||||
paths.remove(path)
|
||||
|
|
|
@ -54,7 +54,6 @@ class VideoNodes(object):
|
|||
mediatype = mediatypes[mediatype]
|
||||
|
||||
window = utils.window
|
||||
kodiversion = self.kodiversion
|
||||
|
||||
if viewtype == "mixed":
|
||||
dirname = "%s-%s" % (viewid, mediatype)
|
||||
|
@ -231,7 +230,7 @@ class VideoNodes(object):
|
|||
# Custom query
|
||||
path = ("plugin://plugin.video.plexkodiconnect/?id=%s&mode=recentepisodes&type=%s&tagname=%s&limit=%s"
|
||||
% (viewid, mediatype, tagname, limit))
|
||||
elif kodiversion == 14 and nodetype == "inprogressepisodes":
|
||||
elif self.kodiversion == 14 and nodetype == "inprogressepisodes":
|
||||
# Custom query
|
||||
path = "plugin://plugin.video.plexkodiconnect/?id=%s&mode=inprogressepisodes&limit=%s" % (tagname, limit)
|
||||
elif nodetype == 'ondeck':
|
||||
|
|
|
@ -455,7 +455,6 @@ class WebSocket(object):
|
|||
self._handshake(hostname, port, resource, **options)
|
||||
|
||||
def _handshake(self, host, port, resource, **options):
|
||||
sock = self.sock
|
||||
headers = []
|
||||
headers.append("GET %s HTTP/1.1" % resource)
|
||||
headers.append("Upgrade: websocket")
|
||||
|
|
Loading…
Reference in a new issue