fix for the merge that didn't follow
Media path fix, and clean up of writeKodiVideoDB. Fix for library sync for deletes.
This commit is contained in:
parent
3589c4b05d
commit
0c54257de6
4 changed files with 1281 additions and 1300 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
# -- coding: utf-8 --
|
||||||
# API.py
|
# API.py
|
||||||
# This class helps translate more complex cases from the MediaBrowser API to the XBMC API
|
# This class helps translate more complex cases from the MediaBrowser API to the XBMC API
|
||||||
|
|
||||||
|
@ -14,140 +15,155 @@ class API():
|
||||||
director = []
|
director = []
|
||||||
writer = []
|
writer = []
|
||||||
cast = []
|
cast = []
|
||||||
people = item.get("People")
|
|
||||||
if(people != None):
|
try:
|
||||||
|
people = item['People']
|
||||||
|
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
for person in people:
|
for person in people:
|
||||||
if(person.get("Type") == "Director"):
|
|
||||||
director.append(person.get("Name"))
|
type = person['Type']
|
||||||
if(person.get("Type") == "Writing"):
|
Name = person['Name']
|
||||||
writer.append(person.get("Name"))
|
|
||||||
if(person.get("Type") == "Writer"):
|
if "Director" in type:
|
||||||
writer.append(person.get("Name"))
|
director.append(Name)
|
||||||
if(person.get("Type") == "Actor"):
|
elif "Writing" in type:
|
||||||
Name = person.get("Name")
|
writer.append(Name)
|
||||||
Role = person.get("Role")
|
elif "Writer" in type:
|
||||||
if Role == None:
|
writer.append(Name)
|
||||||
Role = ''
|
elif "Actor" in type:
|
||||||
cast.append(Name)
|
cast.append(Name)
|
||||||
return {'Director' : director,
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
'Director': director,
|
||||||
'Writer': writer,
|
'Writer': writer,
|
||||||
'Cast': cast
|
'Cast': cast
|
||||||
}
|
}
|
||||||
|
|
||||||
def getTimeInfo(self, item):
|
def getTimeInfo(self, item):
|
||||||
resumeTime = ''
|
# Runtime and Resume point
|
||||||
userData = item.get("UserData")
|
tempRuntime = 0
|
||||||
PlaybackPositionTicks = '100'
|
runtime = 0
|
||||||
if userData.get("PlaybackPositionTicks") != None:
|
|
||||||
PlaybackPositionTicks = str(userData.get("PlaybackPositionTicks"))
|
|
||||||
reasonableTicks = int(userData.get("PlaybackPositionTicks")) / 1000
|
|
||||||
resumeTime = reasonableTicks / 10000
|
|
||||||
|
|
||||||
try:
|
|
||||||
tempDuration = str(int(item.get("RunTimeTicks", "0"))/(10000000*60))
|
|
||||||
except TypeError:
|
|
||||||
try:
|
|
||||||
tempDuration = str(int(item.get("CumulativeRunTimeTicks"))/(10000000*60))
|
|
||||||
except TypeError:
|
|
||||||
tempDuration = "0"
|
|
||||||
cappedPercentage = None
|
|
||||||
resume = 0
|
resume = 0
|
||||||
percentage=0
|
|
||||||
if (resumeTime != "" and int(resumeTime) > 0):
|
try: # Get resume point
|
||||||
duration = float(tempDuration)
|
userdata = item['UserData']
|
||||||
if(duration > 0):
|
playbackPosition = userdata['PlaybackPositionTicks']
|
||||||
resume = float(resumeTime) / 60
|
resume = playbackPosition / 10000000.0
|
||||||
percentage = int((resume / duration) * 100.0)
|
except: pass
|
||||||
return {'Duration' : tempDuration,
|
|
||||||
'TotalTime' : tempDuration,
|
try: # Get total runtime
|
||||||
'Percent' : str(percentage),
|
tempRuntime = item['RunTimeTicks']
|
||||||
'ResumeTime' : str(resume)
|
|
||||||
|
except:
|
||||||
|
try: tempRuntime = item['CumulativeRunTimeTicks']
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
finally:
|
||||||
|
runtime = tempRuntime / 10000000.0
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
'ResumeTime': resume,
|
||||||
|
'TotalTime': runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
def getStudios(self, item):
|
def getStudios(self, item):
|
||||||
# Process Studio
|
# Process Studio
|
||||||
studios = []
|
studios = []
|
||||||
if item.get("SeriesStudio") != None and item.get("SeriesStudio") != '':
|
|
||||||
studios.append(item.get("SeriesStudio"))
|
try:
|
||||||
else:
|
studio = item['SeriesStudio']
|
||||||
if(item.get("Studios") != []):
|
studios.append(studio)
|
||||||
for studio_string in item.get("Studios"):
|
except:
|
||||||
temp=studio_string.get("Name")
|
try:
|
||||||
studios.append(temp)
|
studioArray = item['Studios']
|
||||||
|
for studio in studioArray:
|
||||||
|
studios.append(studio['Name'])
|
||||||
|
except: pass
|
||||||
|
|
||||||
return studios
|
return studios
|
||||||
|
|
||||||
def getMediaStreams(self, item, mediaSources=False):
|
def getGenre(self,item):
|
||||||
# Process MediaStreams
|
genre = ""
|
||||||
channels = ''
|
genres = item.get("Genres")
|
||||||
videocodec = ''
|
if genres != None and genres != []:
|
||||||
audiocodec = ''
|
for genre_string in genres:
|
||||||
audiolanguage = ''
|
if genre == "": #Just take the first genre
|
||||||
subtitlelanguage = ''
|
genre = genre_string
|
||||||
height = ''
|
|
||||||
width = ''
|
|
||||||
aspectratio = '1:1'
|
|
||||||
aspectfloat = 1.85
|
|
||||||
Video3DFormat = ''
|
|
||||||
|
|
||||||
if mediaSources == True:
|
|
||||||
mediaSources = item.get("MediaSources")
|
|
||||||
if(mediaSources != None):
|
|
||||||
MediaStreams = mediaSources[0].get("MediaStreams")
|
|
||||||
else:
|
else:
|
||||||
|
genre = genre + " / " + genre_string
|
||||||
|
elif item.get("SeriesGenres") != None and item.get("SeriesGenres") != '':
|
||||||
|
genres = item.get("SeriesGenres")
|
||||||
|
if genres != None and genres != []:
|
||||||
|
for genre_string in genres:
|
||||||
|
if genre == "": #Just take the first genre
|
||||||
|
genre = genre_string
|
||||||
|
else:
|
||||||
|
genre = genre + " / " + genre_string
|
||||||
|
return genre
|
||||||
|
|
||||||
|
def getMediaStreams(self, item, mediaSources = False):
|
||||||
|
|
||||||
|
videotracks = [] # Height, Width, Codec, AspectRatio, AspectFloat, 3D
|
||||||
|
audiotracks = [] # Codec, Channels, language
|
||||||
|
subtitlelanguages = [] # Language
|
||||||
|
|
||||||
|
if mediaSources:
|
||||||
|
try:
|
||||||
|
MediaStreams = item['MediaSources'][0]['MediaStreams']
|
||||||
|
except:
|
||||||
MediaStreams = None
|
MediaStreams = None
|
||||||
else:
|
else:
|
||||||
MediaStreams = item.get("MediaStreams")
|
MediaStreams = item.get('MediaStreams')
|
||||||
if(MediaStreams != None):
|
|
||||||
#mediaStreams = MediaStreams[0].get("MediaStreams")
|
if MediaStreams:
|
||||||
if(MediaStreams != None):
|
# Sort through the Video, Audio, Subtitle tracks
|
||||||
for mediaStream in MediaStreams:
|
for mediaStream in MediaStreams:
|
||||||
if(mediaStream.get("Type") == "Video"):
|
|
||||||
videocodec = mediaStream.get("Codec")
|
type = mediaStream.get("Type", "")
|
||||||
if mediaStream.get("Height"):
|
|
||||||
height = int(mediaStream.get("Height"))
|
if "Video" in type:
|
||||||
if mediaStream.get("Width"):
|
videotrack = {}
|
||||||
width = int(mediaStream.get("Width"))
|
videotrack['videocodec'] = mediaStream.get('Codec')
|
||||||
aspectratio = mediaStream.get("AspectRatio")
|
videotrack['height'] = mediaStream.get('Height')
|
||||||
Video3DFormat = item.get("Video3DFormat")
|
videotrack['width'] = mediaStream.get('Width')
|
||||||
if aspectratio != None and len(aspectratio) >= 3:
|
videotrack['aspectratio'] = mediaStream.get('AspectRatio')
|
||||||
|
videotrack['Video3DFormat'] = item.get('Video3DFormat')
|
||||||
|
if len(videotrack['aspectratio']) >= 3:
|
||||||
try:
|
try:
|
||||||
aspectwidth, aspectheight = aspectratio.split(':')
|
aspectwidth, aspectheight = aspectratio.split(':')
|
||||||
aspectfloat = float(aspectwidth) / float(aspectheight)
|
videotrack['aspectfloat'] = float(aspectwidth) / float(aspectheight)
|
||||||
except:
|
except:
|
||||||
aspectfloat = 1.85
|
videotrack['aspectfloat'] = 1.85
|
||||||
if(mediaStream.get("Type") == "Audio"):
|
videotracks.append(videotrack)
|
||||||
isdefault = mediaStream.get("IsDefault") == "true"
|
|
||||||
if audiocodec == '':
|
|
||||||
audiocodec = mediaStream.get("Codec")
|
|
||||||
if channels == '':
|
|
||||||
channels = mediaStream.get("Channels")
|
|
||||||
if audiolanguage == '':
|
|
||||||
audiolanguage = mediaStream.get("Language")
|
|
||||||
# only overwrite if default
|
|
||||||
if isdefault:
|
|
||||||
audiocodec = mediaStream.get("Codec")
|
|
||||||
channels = mediaStream.get("Channels")
|
|
||||||
audiolanguage = mediaStream.get("Language")
|
|
||||||
if(mediaStream.get("Type") == "Subtitle"):
|
|
||||||
isdefault = mediaStream.get("IsDefault") == "true"
|
|
||||||
if subtitlelanguage == '':
|
|
||||||
subtitlelanguage = mediaStream.get("Language")
|
|
||||||
# only overwrite if default
|
|
||||||
if isdefault:
|
|
||||||
subtitlelanguage = mediaStream.get("Language")
|
|
||||||
|
|
||||||
|
elif "Audio" in type:
|
||||||
|
audiotrack = {}
|
||||||
|
audiotrack['audiocodec'] = mediaStream.get('Codec')
|
||||||
|
audiotrack['channels'] = mediaStream.get('Channels')
|
||||||
|
audiotrack['audiolanguage'] = mediaStream.get('Language')
|
||||||
|
audiotracks.append(audiotrack)
|
||||||
|
|
||||||
return {'channels' : str(channels),
|
elif "Subtitle" in type:
|
||||||
'videocodec' : videocodec,
|
try:
|
||||||
'audiocodec' : audiocodec,
|
subtitlelanguages.append(mediaStream['Language'])
|
||||||
'audiolanguage' : audiolanguage,
|
except:
|
||||||
'subtitlelanguage' : subtitlelanguage,
|
subtitlelanguages.append("Unknown")
|
||||||
'height' : height,
|
|
||||||
'width' : width,
|
return {
|
||||||
'aspectratio' : aspectfloat,
|
|
||||||
'3dformat' : Video3DFormat
|
'videocodec' : videotracks,
|
||||||
|
'audiocodec' : audiotracks,
|
||||||
|
'subtitlelanguage' : subtitlelanguages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def getChecksum(self, item):
|
def getChecksum(self, item):
|
||||||
# use the etags checksum for this if available
|
# use the etags checksum for this if available
|
||||||
# AND the userdata
|
# AND the userdata
|
||||||
|
@ -169,75 +185,43 @@ class API():
|
||||||
return checksum
|
return checksum
|
||||||
|
|
||||||
def getUserData(self, item):
|
def getUserData(self, item):
|
||||||
userData = item.get("UserData")
|
# Default
|
||||||
resumeTime = 0
|
|
||||||
if(userData != None):
|
|
||||||
if userData.get("Played") != True:
|
|
||||||
watched="True"
|
|
||||||
else:
|
|
||||||
watched="False"
|
|
||||||
if userData.get("IsFavorite") == True:
|
|
||||||
favorite=True
|
|
||||||
else:
|
|
||||||
favorite = False
|
favorite = False
|
||||||
if(userData.get("Played") == True):
|
playcount = None
|
||||||
# Cover the Emby scenario where item is played but playcount is 0.
|
lastPlayedDate = None
|
||||||
playcount = userData.get('PlayCount')
|
userKey = ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
userdata = item['UserData']
|
||||||
|
|
||||||
|
except: # No userdata found.
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
favorite = userdata['IsFavorite']
|
||||||
|
userKey = userdata.get('Key', "")
|
||||||
|
|
||||||
|
watched = userdata['Played']
|
||||||
|
if watched:
|
||||||
|
# Playcount is tied to the watch status
|
||||||
|
playcount = userdata['PlayCount']
|
||||||
if playcount == 0:
|
if playcount == 0:
|
||||||
playcount = 1
|
playcount = 1
|
||||||
else:
|
else:
|
||||||
playcount="0"
|
playcount = None
|
||||||
if userData.get('UnplayedItemCount') != None:
|
|
||||||
UnplayedItemCount = userData.get('UnplayedItemCount')
|
lastPlayedDate = userdata.get('LastPlayedDate', None)
|
||||||
else:
|
if lastPlayedDate:
|
||||||
UnplayedItemCount = "0"
|
lastPlayedDate = lastPlayedDate.split('.')[0].replace('T', " ")
|
||||||
if userData.get('LastPlayedDate') != None:
|
|
||||||
#TODO--> is there some other way to do this ?
|
return {
|
||||||
datestring = userData.get('LastPlayedDate').split('T')[0]
|
|
||||||
timestring = userData.get('LastPlayedDate').split('T')[1]
|
|
||||||
timestring = timestring.split('.')[0]
|
|
||||||
LastPlayedDate = datestring + " " + timestring
|
|
||||||
else:
|
|
||||||
LastPlayedDate = None
|
|
||||||
if userData.get('PlaybackPositionTicks') != None:
|
|
||||||
PlaybackPositionTicks = userData.get('PlaybackPositionTicks')
|
|
||||||
else:
|
|
||||||
PlaybackPositionTicks = ''
|
|
||||||
userKey = userData.get("Key", "")
|
|
||||||
return {'Watched' : watched,
|
|
||||||
'Favorite': favorite,
|
'Favorite': favorite,
|
||||||
'PlayCount': playcount,
|
'PlayCount': playcount,
|
||||||
'LastPlayedDate': LastPlayedDate,
|
'LastPlayedDate': lastPlayedDate,
|
||||||
'UnplayedItemCount' : UnplayedItemCount,
|
|
||||||
'PlaybackPositionTicks' : str(PlaybackPositionTicks),
|
|
||||||
'Key': userKey
|
'Key': userKey
|
||||||
}
|
}
|
||||||
|
|
||||||
def getGenre(self,item):
|
|
||||||
genre = ""
|
|
||||||
genres = item.get("Genres")
|
|
||||||
if genres != None and genres != []:
|
|
||||||
for genre_string in genres:
|
|
||||||
if genre == "": #Just take the first genre
|
|
||||||
genre = genre_string
|
|
||||||
else:
|
|
||||||
genre = genre + " / " + genre_string
|
|
||||||
elif item.get("SeriesGenres") != None and item.get("SeriesGenres") != '':
|
|
||||||
genres = item.get("SeriesGenres")
|
|
||||||
if genres != None and genres != []:
|
|
||||||
for genre_string in genres:
|
|
||||||
if genre == "": #Just take the first genre
|
|
||||||
genre = genre_string
|
|
||||||
else:
|
|
||||||
genre = genre + " / " + genre_string
|
|
||||||
return genre
|
|
||||||
|
|
||||||
def getName(self, item):
|
|
||||||
Temp = item.get("Name")
|
|
||||||
if Temp == None:
|
|
||||||
Temp = ""
|
|
||||||
Name=Temp.encode('utf-8')
|
|
||||||
return Name
|
|
||||||
|
|
||||||
def getRecursiveItemCount(self, item):
|
def getRecursiveItemCount(self, item):
|
||||||
if item.get("RecursiveItemCount") != None:
|
if item.get("RecursiveItemCount") != None:
|
||||||
|
@ -245,33 +229,18 @@ class API():
|
||||||
else:
|
else:
|
||||||
return "0"
|
return "0"
|
||||||
|
|
||||||
def getSeriesName(self, item):
|
|
||||||
Temp = item.get("SeriesName")
|
|
||||||
if Temp == None:
|
|
||||||
Temp = ""
|
|
||||||
Name=Temp.encode('utf-8')
|
|
||||||
return Name
|
|
||||||
|
|
||||||
def getOverview(self, item):
|
def getOverview(self, item):
|
||||||
Temp = item.get("Overview")
|
|
||||||
if Temp == None:
|
|
||||||
Temp=''
|
|
||||||
Overview1=Temp.encode('utf-8')
|
|
||||||
Overview=str(Overview1)
|
|
||||||
Overview=Overview.replace("\"", "\'")
|
|
||||||
Overview=Overview.replace("\n", " ")
|
|
||||||
Overview=Overview.replace("\r", " ")
|
|
||||||
return Overview
|
|
||||||
|
|
||||||
def getPremiereDate(self, item):
|
overview = ""
|
||||||
if(item.get("PremiereDate") != None):
|
|
||||||
premieredatelist = (item.get("PremiereDate")).split("T")
|
try:
|
||||||
premieredate = premieredatelist[0]
|
overview = item['Overview']
|
||||||
else:
|
overview = overview.replace("\"", "\'")
|
||||||
premieredate = ""
|
overview = overview.replace("\n", " ")
|
||||||
Temp = premieredate
|
overview = overview.replace("\r", " ")
|
||||||
premieredate = Temp.encode('utf-8')
|
except: pass
|
||||||
return premieredate
|
|
||||||
|
return overview
|
||||||
|
|
||||||
def getTVInfo(self, item, userData):
|
def getTVInfo(self, item, userData):
|
||||||
TotalSeasons = 0 if item.get("ChildCount")==None else item.get("ChildCount")
|
TotalSeasons = 0 if item.get("ChildCount")==None else item.get("ChildCount")
|
||||||
|
@ -306,15 +275,105 @@ class API():
|
||||||
'Episode' : tempEpisode,
|
'Episode' : tempEpisode,
|
||||||
'SeriesName' : SeriesName
|
'SeriesName' : SeriesName
|
||||||
}
|
}
|
||||||
|
|
||||||
def getDateCreated(self, item):
|
def getDateCreated(self, item):
|
||||||
tempDate = item.get("DateCreated")
|
|
||||||
if tempDate != None:
|
dateadded = None
|
||||||
tempDate = tempDate.split("T")[0]
|
|
||||||
date = tempDate.split("-")
|
try:
|
||||||
tempDate = date[2] + "." + date[1] + "." +date[0]
|
dateadded = item['DateCreated']
|
||||||
else:
|
dateadded = dateadded.split('.')[0].replace('T', " ")
|
||||||
tempDate = "01.01.2000"
|
except: pass
|
||||||
return tempDate
|
|
||||||
|
return dateadded
|
||||||
|
|
||||||
|
def getPremiereDate(self, item):
|
||||||
|
|
||||||
|
premiere = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
premiere = item['PremiereDate']
|
||||||
|
premiere = premiere.split('.')[0].replace('T', " ")
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
return premiere
|
||||||
|
|
||||||
|
def getTagline(self, item):
|
||||||
|
|
||||||
|
tagline = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
tagline = item['Taglines'][0]
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
return tagline
|
||||||
|
|
||||||
|
def getProvider(self, item, providername):
|
||||||
|
# Provider Name: imdb or tvdb
|
||||||
|
provider = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
if "imdb" in providername:
|
||||||
|
provider = item['ProviderIds']['Imdb']
|
||||||
|
elif "tvdb" in providername:
|
||||||
|
provider = item['ProviderIds']['Tvdb']
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
return provider
|
||||||
|
|
||||||
|
def getCountry(self, item):
|
||||||
|
|
||||||
|
country = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
country = item['ProductionLocations'][0]
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
return country
|
||||||
|
|
||||||
|
def getArtworks(self, data, type, mediaType = "", index = "0", getAll = False):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Get all artwork, it will return an empty string
|
||||||
|
for the artwork type not found.
|
||||||
|
|
||||||
|
Index only matters when getAll is False.
|
||||||
|
|
||||||
|
mediaType: movie, boxset, tvshow, episode, season
|
||||||
|
|
||||||
|
Artwork type: Primary, Banner, Logo, Art, Thumb,
|
||||||
|
Disc Backdrop
|
||||||
|
"""
|
||||||
|
id = data['Id']
|
||||||
|
|
||||||
|
maxHeight = 10000
|
||||||
|
maxWidth = 10000
|
||||||
|
imageTag = "e3ab56fe27d389446754d0fb04910a34" # Place holder tag
|
||||||
|
|
||||||
|
|
||||||
|
if getAll:
|
||||||
|
|
||||||
|
allartworks = {
|
||||||
|
|
||||||
|
'Primary': "",
|
||||||
|
'Banner': "",
|
||||||
|
'Logo': "",
|
||||||
|
'Art': "",
|
||||||
|
'Thumb': "",
|
||||||
|
'Disc': "",
|
||||||
|
'Backdrop': ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for keytype in allartworks:
|
||||||
|
type = keytype
|
||||||
|
url = ""
|
||||||
|
|
||||||
|
allartworks[keytype] = url
|
||||||
|
|
||||||
|
|
||||||
|
return allartworks
|
||||||
|
|
||||||
|
else: pass
|
||||||
|
|
||||||
def getArtwork(self, data, type, mediaType = "", index = "0", userParentInfo = False):
|
def getArtwork(self, data, type, mediaType = "", index = "0", userParentInfo = False):
|
||||||
|
|
||||||
|
@ -417,6 +476,14 @@ class API():
|
||||||
|
|
||||||
return artwork
|
return artwork
|
||||||
|
|
||||||
|
def imageUrl(self, id, type, index, width, height):
|
||||||
|
|
||||||
|
WINDOW = xbmcgui.Window(10000)
|
||||||
|
username = WINDOW.getProperty('currUser')
|
||||||
|
server = WINDOW.getProperty('server%s' % username)
|
||||||
|
# For people image - actors, directors, writers
|
||||||
|
return "%s/mediabrowser/Items/%s/Images/%s?MaxWidth=%s&MaxHeight=%s&Index=%s" % (server, id, type, width, height, index)
|
||||||
|
|
||||||
def getUserArtwork(self, data, type, index = "0"):
|
def getUserArtwork(self, data, type, index = "0"):
|
||||||
|
|
||||||
# Load user information set by UserClient
|
# Load user information set by UserClient
|
||||||
|
|
|
@ -659,61 +659,67 @@ class LibrarySync(threading.Thread):
|
||||||
# Delete from Kodi before Emby
|
# Delete from Kodi before Emby
|
||||||
# To be able to get mediaType
|
# To be able to get mediaType
|
||||||
doUtils = DownloadUtils()
|
doUtils = DownloadUtils()
|
||||||
|
video = {}
|
||||||
video = []
|
|
||||||
music = []
|
music = []
|
||||||
|
|
||||||
itemIds = ','.join(itemList)
|
# Database connection to myVideosXX.db
|
||||||
url = "{server}/mediabrowser/Users/{UserId}/Items?Ids=%s&format=json" % itemIds
|
connectionvideo = utils.KodiSQL()
|
||||||
result = doUtils.downloadUrl(url)
|
cursorvideo = connectionvideo.cursor()
|
||||||
|
# Database connection to myMusicXX.db
|
||||||
|
connectionmusic = utils.KodiSQL("music")
|
||||||
|
cursormusic = connectionmusic.cursor()
|
||||||
|
|
||||||
if result is "":
|
for item in itemList:
|
||||||
# Websocket feedback
|
|
||||||
self.logMsg("Item %s is removed." % itemIds)
|
|
||||||
return
|
|
||||||
|
|
||||||
for item in result[u'Items']:
|
|
||||||
# Sort by type for database deletion
|
# Sort by type for database deletion
|
||||||
itemId = item["Id"]
|
try: # Search video database
|
||||||
mediaType = item["MediaType"]
|
self.logMsg("Check video database.", 1)
|
||||||
|
cursorvideo.execute("SELECT media_type FROM emby WHERE emby_id = ?", (item,))
|
||||||
if "Video" in mediaType:
|
mediatype = cursorvideo.fetchone()[0]
|
||||||
video.append(itemId)
|
video[item] = mediatype
|
||||||
elif "Audio" in mediaType:
|
#video.append(itemtype)
|
||||||
music.append(itemId)
|
except:
|
||||||
|
self.logMsg("Check music database.", 1)
|
||||||
|
try: # Search music database
|
||||||
|
cursormusic.execute("SELECT media_type FROM emby WHERE emby_id = ?", (item,))
|
||||||
|
cursormusic.fetchone()[0]
|
||||||
|
music.append(item)
|
||||||
|
except: self.logMsg("Item %s is not found in Kodi database." % item, 1)
|
||||||
|
|
||||||
if len(video) > 0:
|
if len(video) > 0:
|
||||||
|
connection = connectionvideo
|
||||||
|
cursor = cursorvideo
|
||||||
# Process video library
|
# Process video library
|
||||||
connection = utils.KodiSQL("video")
|
|
||||||
cursor = connection.cursor()
|
|
||||||
|
|
||||||
for item in video:
|
for item in video:
|
||||||
type = ReadKodiDB().getTypeByEmbyId(item, connection, cursor)
|
|
||||||
self.logMsg("Type: %s" % type)
|
type = video[item]
|
||||||
self.logMsg("Message: Doing LibraryChanged: Items Removed: Calling deleteItemFromKodiLibrary: %s" % item, 0)
|
self.logMsg("Doing LibraryChanged: Items Removed: Calling deleteItemFromKodiLibrary: %s" % item, 1)
|
||||||
|
|
||||||
if "episode" in type:
|
if "episode" in type:
|
||||||
# Get the TV Show Id for reference later
|
# Get the TV Show Id for reference later
|
||||||
showId = ReadKodiDB().getShowIdByEmbyId(item, connection, cursor)
|
showId = ReadKodiDB().getShowIdByEmbyId(item, connection, cursor)
|
||||||
self.logMsg("ShowId: %s" % showId, 0)
|
self.logMsg("ShowId: %s" % showId, 1)
|
||||||
WriteKodiVideoDB().deleteItemFromKodiLibrary(item, connection, cursor)
|
WriteKodiVideoDB().deleteItemFromKodiLibrary(item, connection, cursor)
|
||||||
# Verification
|
# Verification
|
||||||
if "episode" in type:
|
if "episode" in type:
|
||||||
showTotalCount = ReadKodiDB().getShowTotalCount(showId, connection, cursor)
|
showTotalCount = ReadKodiDB().getShowTotalCount(showId, connection, cursor)
|
||||||
self.logMsg("ShowTotalCount: %s" % showTotalCount, 0)
|
self.logMsg("ShowTotalCount: %s" % showTotalCount, 1)
|
||||||
# If there are no episodes left
|
# If there are no episodes left
|
||||||
if showTotalCount == 0 or showTotalCount == None:
|
if showTotalCount == 0 or showTotalCount == None:
|
||||||
# Delete show
|
# Delete show
|
||||||
embyId = ReadKodiDB().getEmbyIdByKodiId(showId, "tvshow", connection, cursor)
|
embyId = ReadKodiDB().getEmbyIdByKodiId(showId, "tvshow", connection, cursor)
|
||||||
self.logMsg("Message: Doing LibraryChanged: Deleting show: %s" % embyId, 0)
|
self.logMsg("Message: Doing LibraryChanged: Deleting show: %s" % embyId, 1)
|
||||||
WriteKodiVideoDB().deleteItemFromKodiLibrary(embyId, connection, cursor)
|
WriteKodiVideoDB().deleteItemFromKodiLibrary(embyId, connection, cursor)
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
cursor.close()
|
# Close connection
|
||||||
|
cursorvideo.close()
|
||||||
|
|
||||||
if len(music) > 0:
|
if len(music) > 0:
|
||||||
|
connection = connectionmusic
|
||||||
|
cursor = cursormusic
|
||||||
#Process music library
|
#Process music library
|
||||||
addon = xbmcaddon.Addon(id='plugin.video.emby')
|
addon = xbmcaddon.Addon()
|
||||||
if addon.getSetting("enableMusicSync") is "true":
|
if addon.getSetting('enableMusicSync') == "true":
|
||||||
connection = utils.KodiSQL("music")
|
connection = utils.KodiSQL("music")
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
@ -722,7 +728,8 @@ class LibrarySync(threading.Thread):
|
||||||
WriteKodiMusicDB().deleteItemFromKodiLibrary(item, connection, cursor)
|
WriteKodiMusicDB().deleteItemFromKodiLibrary(item, connection, cursor)
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
cursor.close()
|
# Close connection
|
||||||
|
cursormusic.close()
|
||||||
|
|
||||||
if deleteEmbyItem:
|
if deleteEmbyItem:
|
||||||
for item in itemList:
|
for item in itemList:
|
||||||
|
@ -732,25 +739,23 @@ class LibrarySync(threading.Thread):
|
||||||
xbmc.executebuiltin("Container.Refresh")
|
xbmc.executebuiltin("Container.Refresh")
|
||||||
|
|
||||||
def remove_items(self, itemsRemoved):
|
def remove_items(self, itemsRemoved):
|
||||||
|
# websocket client
|
||||||
self.removeItems.extend(itemsRemoved)
|
self.removeItems.extend(itemsRemoved)
|
||||||
|
|
||||||
def update_items(self, itemsToUpdate):
|
def update_items(self, itemsToUpdate):
|
||||||
# doing adds and updates
|
# websocket client
|
||||||
if(len(itemsToUpdate) > 0):
|
if(len(itemsToUpdate) > 0):
|
||||||
self.logMsg("Message : Doing LibraryChanged : Processing Added and Updated : " + str(itemsToUpdate), 0)
|
self.logMsg("Doing LibraryChanged : Processing Added and Updated : " + str(itemsToUpdate), 0)
|
||||||
self.updateItems.extend(itemsToUpdate)
|
self.updateItems.extend(itemsToUpdate)
|
||||||
self.doIncrementalSync = True
|
|
||||||
|
|
||||||
def user_data_update(self, userDataList):
|
def user_data_update(self, userDataList):
|
||||||
# do full playcount update for now
|
# websocket client
|
||||||
for userData in userDataList:
|
for userData in userDataList:
|
||||||
itemId = userData.get("ItemId")
|
itemId = userData.get("ItemId")
|
||||||
if(itemId != None):
|
if(itemId != None):
|
||||||
self.updateItems.append(itemId)
|
self.updateItems.append(itemId)
|
||||||
if(len(self.updateItems) > 0):
|
if(len(self.updateItems) > 0):
|
||||||
self.logMsg("Message : Doing UserDataChanged : Processing Updated : " + str(self.updateItems), 0)
|
self.logMsg("Doing UserDataChanged : Processing Updated : " + str(self.updateItems), 0)
|
||||||
self.doIncrementalSync = True
|
|
||||||
|
|
||||||
def ShouldStop(self):
|
def ShouldStop(self):
|
||||||
|
|
||||||
|
@ -770,32 +775,44 @@ class LibrarySync(threading.Thread):
|
||||||
|
|
||||||
while not self.KodiMonitor.abortRequested():
|
while not self.KodiMonitor.abortRequested():
|
||||||
|
|
||||||
|
# In the event the server goes offline after
|
||||||
|
# the thread has already been started.
|
||||||
|
while self.suspendClient == True:
|
||||||
|
# The service.py will change self.suspendClient to False
|
||||||
|
if self.KodiMonitor.waitForAbort(5):
|
||||||
|
# Abort was requested while waiting. We should exit
|
||||||
|
break
|
||||||
|
|
||||||
# Library sync
|
# Library sync
|
||||||
if not startupComplete:
|
if not startupComplete:
|
||||||
# Run full sync
|
# Run full sync
|
||||||
self.logMsg("Doing_Db_Sync: syncDatabase (Started)", 1)
|
self.logMsg("Doing_Db_Sync: syncDatabase (Started)", 1)
|
||||||
|
startTime = datetime.now()
|
||||||
libSync = self.FullLibrarySync()
|
libSync = self.FullLibrarySync()
|
||||||
self.logMsg("Doing_Db_Sync: syncDatabase (Finished) %s" % libSync, 1)
|
elapsedTime = datetime.now() - startTime
|
||||||
|
self.logMsg("Doing_Db_Sync: syncDatabase (Finished in: %s) %s" % (str(elapsedTime).split('.')[0], libSync), 1)
|
||||||
|
|
||||||
if libSync:
|
if libSync:
|
||||||
startupComplete = True
|
startupComplete = True
|
||||||
|
|
||||||
if WINDOW.getProperty("OnWakeSync") == "true":
|
# Set via Kodi Monitor event
|
||||||
|
if WINDOW.getProperty("OnWakeSync") == "true" and WINDOW.getProperty('Server_online') == "true":
|
||||||
WINDOW.clearProperty("OnWakeSync")
|
WINDOW.clearProperty("OnWakeSync")
|
||||||
if WINDOW.getProperty("SyncDatabaseRunning") != "true":
|
if WINDOW.getProperty("SyncDatabaseRunning") != "true":
|
||||||
utils.logMsg("Doing_Db_Sync Post Resume: syncDatabase (Started)",0)
|
self.logMsg("Doing_Db_Sync Post Resume: syncDatabase (Started)", 0)
|
||||||
libSync = self.FullLibrarySync()
|
libSync = self.FullLibrarySync()
|
||||||
utils.logMsg("Doing_Db_Sync Post Resume: syncDatabase (Finished) " + str(libSync),0)
|
self.logMsg("Doing_Db_Sync Post Resume: syncDatabase (Finished) " + str(libSync), 0)
|
||||||
|
|
||||||
if self.doIncrementalSync:
|
if len(self.updateItems) > 0:
|
||||||
# Add or update item to Kodi library
|
# Add or update items
|
||||||
|
self.logMsg("Processing items: %s" % (str(self.updateItems)), 1)
|
||||||
listItems = self.updateItems
|
listItems = self.updateItems
|
||||||
self.updateItems = []
|
self.updateItems = []
|
||||||
self.doIncrementalSync = False
|
|
||||||
self.IncrementalSync(listItems)
|
self.IncrementalSync(listItems)
|
||||||
|
|
||||||
if len(self.removeItems) > 0:
|
if len(self.removeItems) > 0:
|
||||||
# Remove item from Kodi library
|
# Remove item from Kodi library
|
||||||
|
self.logMsg("Removing items: %s" % self.removeItems, 1)
|
||||||
listItems = self.removeItems
|
listItems = self.removeItems
|
||||||
self.removeItems = []
|
self.removeItems = []
|
||||||
self.removefromDB(listItems)
|
self.removefromDB(listItems)
|
||||||
|
@ -805,3 +822,11 @@ class LibrarySync(threading.Thread):
|
||||||
break
|
break
|
||||||
|
|
||||||
self.logMsg("--- Library Sync Thread stopped ---", 0)
|
self.logMsg("--- Library Sync Thread stopped ---", 0)
|
||||||
|
|
||||||
|
def suspendClient(self):
|
||||||
|
self.suspendClient = True
|
||||||
|
self.logMsg("--- Library Sync Thread paused ---", 0)
|
||||||
|
|
||||||
|
def resumeClient(self):
|
||||||
|
self.suspendClient = False
|
||||||
|
self.logMsg("--- Library Sync Thread resumed ---", 0)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
# utils class
|
# utils class
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
@ -19,8 +21,7 @@ class PlayUtils():
|
||||||
clientInfo = ClientInformation()
|
clientInfo = ClientInformation()
|
||||||
|
|
||||||
addonName = clientInfo.getAddonName()
|
addonName = clientInfo.getAddonName()
|
||||||
addonId = clientInfo.getAddonId()
|
addon = xbmcaddon.Addon()
|
||||||
addon = xbmcaddon.Addon(id=addonId)
|
|
||||||
|
|
||||||
audioPref = addon.getSetting('Audiopref')
|
audioPref = addon.getSetting('Audiopref')
|
||||||
subsPref = addon.getSetting('Subspref')
|
subsPref = addon.getSetting('Subspref')
|
||||||
|
@ -35,76 +36,36 @@ class PlayUtils():
|
||||||
|
|
||||||
def getPlayUrl(self, server, id, result):
|
def getPlayUrl(self, server, id, result):
|
||||||
|
|
||||||
addon = self.addon
|
|
||||||
WINDOW = xbmcgui.Window(10000)
|
WINDOW = xbmcgui.Window(10000)
|
||||||
username = WINDOW.getProperty('currUser')
|
username = WINDOW.getProperty('currUser')
|
||||||
server = WINDOW.getProperty('server%s' % username)
|
server = WINDOW.getProperty('server%s' % username)
|
||||||
|
|
||||||
if self.isDirectPlay(result):
|
if self.isDirectPlay(result,True):
|
||||||
try:
|
|
||||||
# Try direct play
|
# Try direct play
|
||||||
playurl = self.directPlay(result)
|
playurl = self.directPlay(result)
|
||||||
if not playurl:
|
if playurl:
|
||||||
# Let user know that direct play failed
|
|
||||||
resp = xbmcgui.Dialog().select('Warning: Unable to direct play.', ['Play from HTTP', 'Play from HTTP and remember next time.'])
|
|
||||||
if resp > -1:
|
|
||||||
# Play from HTTP
|
|
||||||
playurl = self.directStream(result, server, id)
|
|
||||||
if resp == 1:
|
|
||||||
# Remember next time
|
|
||||||
addon.setSetting('playFromStream', "true")
|
|
||||||
if not playurl:
|
|
||||||
# Try transcoding
|
|
||||||
playurl = self.transcoding(result, server, id)
|
|
||||||
WINDOW.setProperty("transcoding%s" % id, "true")
|
|
||||||
self.logMsg("File is transcoding.", 1)
|
|
||||||
WINDOW.setProperty("%splaymethod" % playurl, "Transcode")
|
|
||||||
else:
|
|
||||||
self.logMsg("File is direct streaming.", 1)
|
|
||||||
WINDOW.setProperty("%splaymethod" % playurl, "DirectStream")
|
|
||||||
else:
|
|
||||||
# User decided not to proceed.
|
|
||||||
self.logMsg("Unable to direct play. Verify the following path is accessible by the device: %s. You might also need to add SMB credentials in the addon settings." % result[u'MediaSources'][0][u'Path'])
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
self.logMsg("File is direct playing.", 1)
|
self.logMsg("File is direct playing.", 1)
|
||||||
WINDOW.setProperty("%splaymethod" % playurl.encode('utf-8'), "DirectPlay")
|
WINDOW.setProperty("%splaymethod" % playurl.encode('utf-8'), "DirectPlay")
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
elif self.isDirectStream(result):
|
elif self.isDirectStream(result):
|
||||||
try:
|
|
||||||
# Try direct stream
|
# Try direct stream
|
||||||
playurl = self.directStream(result, server, id)
|
playurl = self.directStream(result, server, id)
|
||||||
if not playurl:
|
if playurl:
|
||||||
# Try transcoding
|
|
||||||
playurl = self.transcoding(result, server, id)
|
|
||||||
WINDOW.setProperty("transcoding%s" % id, "true")
|
|
||||||
self.logMsg("File is transcoding.", 1)
|
|
||||||
WINDOW.setProperty("%splaymethod" % playurl, "Transcode")
|
|
||||||
else:
|
|
||||||
self.logMsg("File is direct streaming.", 1)
|
self.logMsg("File is direct streaming.", 1)
|
||||||
WINDOW.setProperty("%splaymethod" % playurl, "DirectStream")
|
WINDOW.setProperty("%splaymethod" % playurl, "DirectStream")
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
elif self.isTranscoding(result):
|
else:# Try transcoding
|
||||||
try:
|
|
||||||
# Try transcoding
|
|
||||||
playurl = self.transcoding(result, server, id)
|
playurl = self.transcoding(result, server, id)
|
||||||
WINDOW.setProperty("transcoding%s" % id, "true")
|
if playurl:
|
||||||
self.logMsg("File is transcoding.", 1)
|
self.logMsg("File is transcoding.", 1)
|
||||||
WINDOW.setProperty("%splaymethod" % playurl, "Transcode")
|
WINDOW.setProperty("%splaymethod" % playurl, "Transcode")
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return playurl.encode('utf-8')
|
return playurl.encode('utf-8')
|
||||||
|
|
||||||
|
def isDirectPlay(self, result, dialog=False):
|
||||||
def isDirectPlay(self, result):
|
|
||||||
# Requirements for Direct play:
|
# Requirements for Direct play:
|
||||||
# FileSystem, Accessible path
|
# FileSystem, Accessible path
|
||||||
self.addon = xbmcaddon.Addon(id=self.addonId)
|
self.addon = xbmcaddon.Addon()
|
||||||
|
|
||||||
playhttp = self.addon.getSetting('playFromStream')
|
playhttp = self.addon.getSetting('playFromStream')
|
||||||
# User forcing to play via HTTP instead of SMB
|
# User forcing to play via HTTP instead of SMB
|
||||||
|
@ -126,17 +87,30 @@ class PlayUtils():
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
self.logMsg("Can't direct play: Unable to locate the content.", 1)
|
self.logMsg("Can't direct play: Unable to locate the content.", 1)
|
||||||
|
if dialog:
|
||||||
|
# Let user know that direct play failed
|
||||||
|
resp = xbmcgui.Dialog().select('Warning: Unable to direct play.', ['Play from HTTP', 'Play from HTTP and remember next time.'])
|
||||||
|
if resp == 1:
|
||||||
|
# Remember next time
|
||||||
|
addon.setSetting('playFromStream', "true")
|
||||||
|
else:
|
||||||
|
# User decided not to proceed.
|
||||||
|
self.logMsg("Unable to direct play. Verify the following path is accessible by the device: %s. You might also need to add SMB credentials in the addon settings." % result[u'MediaSources'][0][u'Path'])
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def directPlay(self, result):
|
def directPlay(self, result):
|
||||||
|
|
||||||
addon = self.addon
|
addon = self.addon
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Item can be played directly
|
try:
|
||||||
playurl = result[u'MediaSources'][0][u'Path']
|
playurl = result[u'MediaSources'][0][u'Path']
|
||||||
|
except:
|
||||||
|
playurl = result[u'Path']
|
||||||
|
except:
|
||||||
|
self.logMsg("Direct play failed. Trying Direct stream.", 1)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
if u'VideoType' in result:
|
if u'VideoType' in result:
|
||||||
# Specific format modification
|
# Specific format modification
|
||||||
if u'Dvd' in result[u'VideoType']:
|
if u'Dvd' in result[u'VideoType']:
|
||||||
|
@ -156,19 +130,15 @@ class PlayUtils():
|
||||||
playurl = playurl.replace("\\", "/")
|
playurl = playurl.replace("\\", "/")
|
||||||
|
|
||||||
if "apple.com" in playurl:
|
if "apple.com" in playurl:
|
||||||
USER_AGENT = 'QuickTime/7.7.4'
|
USER_AGENT = "QuickTime/7.7.4"
|
||||||
playurl += "?|User-Agent=%s" % USER_AGENT
|
playurl += "?|User-Agent=%s" % USER_AGENT
|
||||||
|
|
||||||
if ":" not in playurl:
|
if ":" not in playurl:
|
||||||
self.logMsg("Path seems invalid: %s" % playurl)
|
self.logMsg("Path seems invalid: %s" % playurl, 1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return playurl
|
return playurl
|
||||||
|
|
||||||
except:
|
|
||||||
self.logMsg("Direct play failed. Trying Direct stream.", 1)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def isDirectStream(self, result):
|
def isDirectStream(self, result):
|
||||||
# Requirements for Direct stream:
|
# Requirements for Direct stream:
|
||||||
# FileSystem or Remote, BitRate, supported encoding
|
# FileSystem or Remote, BitRate, supported encoding
|
||||||
|
@ -191,20 +161,21 @@ class PlayUtils():
|
||||||
def directStream(self, result, server, id, type = "Video"):
|
def directStream(self, result, server, id, type = "Video"):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if type == "Video":
|
if "ThemeVideo" in type:
|
||||||
# Play with Direct Stream
|
|
||||||
playurl ="%s/mediabrowser/Videos/%s/stream?static=true" % (server, id)
|
playurl ="%s/mediabrowser/Videos/%s/stream?static=true" % (server, id)
|
||||||
elif type == "Audio":
|
|
||||||
playurl = "%s/mediabrowser/Audio/%s/stream.mp3" % (server, id)
|
|
||||||
return playurl
|
|
||||||
|
|
||||||
|
elif "Video" in type:
|
||||||
|
playurl = "%s/mediabrowser/Videos/%s/stream?static=true" % (server, id)
|
||||||
|
# Verify audio and subtitles
|
||||||
mediaSources = result[u'MediaSources']
|
mediaSources = result[u'MediaSources']
|
||||||
if mediaSources[0].get('DefaultAudioStreamIndex') != None:
|
if mediaSources[0].get('DefaultAudioStreamIndex') != None:
|
||||||
playurl = "%s&AudioStreamIndex=%s" % (playurl, mediaSources[0].get('DefaultAudioStreamIndex'))
|
playurl = "%s&AudioStreamIndex=%s" % (playurl, mediaSources[0].get('DefaultAudioStreamIndex'))
|
||||||
if mediaSources[0].get('DefaultSubtitleStreamIndex') != None:
|
if mediaSources[0].get('DefaultSubtitleStreamIndex') != None:
|
||||||
playurl = "%s&SubtitleStreamIndex=%s" % (playurl, mediaSources[0].get('DefaultSubtitleStreamIndex'))
|
playurl = "%s&SubtitleStreamIndex=%s" % (playurl, mediaSources[0].get('DefaultSubtitleStreamIndex'))
|
||||||
|
|
||||||
self.logMsg("Playurl: %s" % playurl)
|
elif "Audio" in type:
|
||||||
|
playurl = "%s/mediabrowser/Audio/%s/stream.mp3" % (server, id)
|
||||||
|
|
||||||
return playurl
|
return playurl
|
||||||
|
|
||||||
except:
|
except:
|
||||||
|
@ -329,7 +300,7 @@ class PlayUtils():
|
||||||
# Local or Network path
|
# Local or Network path
|
||||||
self.logMsg("Path exists.", 2)
|
self.logMsg("Path exists.", 2)
|
||||||
return True
|
return True
|
||||||
elif ":\\" not in path:
|
elif "nfs:" in path.lower():
|
||||||
# Give benefit of the doubt.
|
# Give benefit of the doubt.
|
||||||
self.logMsg("Can't verify path. Still try direct play.", 2)
|
self.logMsg("Can't verify path. Still try direct play.", 2)
|
||||||
return True
|
return True
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue