commit
b44dfd73c5
30 changed files with 1469 additions and 1592 deletions
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<addon id="plugin.video.emby"
|
||||
name="Emby"
|
||||
version="2.2.10"
|
||||
version="2.2.11"
|
||||
provider-name="Emby.media">
|
||||
<requires>
|
||||
<import addon="xbmc.python" version="2.1.0"/>
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
version 2.2.11
|
||||
- Preparation for feature requests
|
||||
- Add option to refresh Emby items via context menu
|
||||
- Minor fixes
|
||||
|
||||
version 2.2.10
|
||||
- Add keymap action for delete content: RunPlugin(plugin://plugin.video.emby?mode=delete)
|
||||
- Fix various bugs
|
||||
|
|
210
contextmenu.py
210
contextmenu.py
|
@ -10,149 +10,159 @@ import xbmc
|
|||
import xbmcaddon
|
||||
import xbmcgui
|
||||
|
||||
addon_ = xbmcaddon.Addon(id='plugin.video.emby')
|
||||
addon_path = addon_.getAddonInfo('path').decode('utf-8')
|
||||
base_resource = xbmc.translatePath(os.path.join(addon_path, 'resources', 'lib')).decode('utf-8')
|
||||
sys.path.append(base_resource)
|
||||
#################################################################################################
|
||||
|
||||
_addon = xbmcaddon.Addon(id='plugin.video.emby')
|
||||
_addon_path = _addon.getAddonInfo('path').decode('utf-8')
|
||||
_base_resource = xbmc.translatePath(os.path.join(_addon_path, 'resources', 'lib')).decode('utf-8')
|
||||
sys.path.append(_base_resource)
|
||||
|
||||
#################################################################################################
|
||||
|
||||
import api
|
||||
import artwork
|
||||
import utils
|
||||
import clientinfo
|
||||
import downloadutils
|
||||
import librarysync
|
||||
import read_embyserver as embyserver
|
||||
import embydb_functions as embydb
|
||||
import kodidb_functions as kodidb
|
||||
import musicutils as musicutils
|
||||
import api
|
||||
|
||||
def logMsg(msg, lvl=1):
|
||||
utils.logMsg("%s %s" % ("EMBY", "Contextmenu"), msg, lvl)
|
||||
from utils import Logging, settings, language as lang, kodiSQL
|
||||
log = Logging('ContextMenu').log
|
||||
|
||||
#################################################################################################
|
||||
|
||||
# Kodi contextmenu item to configure the emby settings
|
||||
#for now used to set ratings but can later be used to sync individual items etc.
|
||||
if __name__ == '__main__':
|
||||
itemid = xbmc.getInfoLabel("ListItem.DBID").decode("utf-8")
|
||||
itemtype = xbmc.getInfoLabel("ListItem.DBTYPE").decode("utf-8")
|
||||
|
||||
emby = embyserver.Read_EmbyServer()
|
||||
kodiId = xbmc.getInfoLabel('ListItem.DBID').decode('utf-8')
|
||||
itemType = xbmc.getInfoLabel('ListItem.DBTYPE').decode('utf-8')
|
||||
itemId = ""
|
||||
|
||||
embyid = ""
|
||||
if not itemtype and xbmc.getCondVisibility("Container.Content(albums)"): itemtype = "album"
|
||||
if not itemtype and xbmc.getCondVisibility("Container.Content(artists)"): itemtype = "artist"
|
||||
if not itemtype and xbmc.getCondVisibility("Container.Content(songs)"): itemtype = "song"
|
||||
if not itemtype and xbmc.getCondVisibility("Container.Content(pictures)"): itemtype = "picture"
|
||||
if not itemType:
|
||||
|
||||
if (not itemid or itemid == "-1") and xbmc.getInfoLabel("ListItem.Property(embyid)"):
|
||||
embyid = xbmc.getInfoLabel("ListItem.Property(embyid)")
|
||||
if xbmc.getCondVisibility("Container.Content(albums)"):
|
||||
itemType = "album"
|
||||
elif xbmc.getCondVisibility("Container.Content(artists)"):
|
||||
itemType = "artist"
|
||||
elif xbmc.getCondVisibility("Container.Content(songs)"):
|
||||
itemType = "song"
|
||||
elif xbmc.getCondVisibility("Container.Content(pictures)"):
|
||||
itemType = "picture"
|
||||
else:
|
||||
embyconn = utils.kodiSQL('emby')
|
||||
log("ItemType is unknown.")
|
||||
|
||||
if (not kodiId or kodiId == "-1") and xbmc.getInfoLabel("ListItem.Property(embyid)"):
|
||||
itemId = xbmc.getInfoLabel("ListItem.Property(embyid)")
|
||||
|
||||
elif kodiId and itemType:
|
||||
embyconn = kodiSQL('emby')
|
||||
embycursor = embyconn.cursor()
|
||||
emby_db = embydb.Embydb_Functions(embycursor)
|
||||
item = emby_db.getItem_byKodiId(itemid, itemtype)
|
||||
item = emby_db.getItem_byKodiId(kodiId, itemType)
|
||||
embycursor.close()
|
||||
if item: embyid = item[0]
|
||||
try:
|
||||
itemId = item[0]
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
logMsg("Contextmenu opened for embyid: %s - itemtype: %s" %(embyid,itemtype))
|
||||
|
||||
if embyid:
|
||||
item = emby.getItem(embyid)
|
||||
log("Found ItemId: %s ItemType: %s" % (itemId, itemType), 1)
|
||||
if itemId:
|
||||
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
emby = embyserver.Read_EmbyServer()
|
||||
item = emby.getItem(itemId)
|
||||
API = api.API(item)
|
||||
userdata = API.getUserData()
|
||||
likes = userdata['Likes']
|
||||
favourite = userdata['Favorite']
|
||||
|
||||
options = []
|
||||
if likes == True:
|
||||
#clear like for the item
|
||||
options.append(utils.language(30402))
|
||||
if likes == False or likes == None:
|
||||
#Like the item
|
||||
options.append(utils.language(30403))
|
||||
if likes == True or likes == None:
|
||||
#Dislike the item
|
||||
options.append(utils.language(30404))
|
||||
if favourite == False:
|
||||
#Add to emby favourites
|
||||
options.append(utils.language(30405))
|
||||
if favourite == True:
|
||||
|
||||
if favourite:
|
||||
# Remove from emby favourites
|
||||
options.append(utils.language(30406))
|
||||
if itemtype == "song":
|
||||
options.append(lang(30406))
|
||||
else:
|
||||
# Add to emby favourites
|
||||
options.append(lang(30405))
|
||||
|
||||
if itemType == "song":
|
||||
# Set custom song rating
|
||||
options.append(utils.language(30407))
|
||||
options.append(lang(30407))
|
||||
|
||||
#delete item
|
||||
options.append(utils.language(30409))
|
||||
# Refresh item
|
||||
options.append(lang(30410))
|
||||
# Delete item
|
||||
options.append(lang(30409))
|
||||
# Addon settings
|
||||
options.append(lang(30408))
|
||||
|
||||
#addon settings
|
||||
options.append(utils.language(30408))
|
||||
# Display select dialog and process results
|
||||
resp = xbmcgui.Dialog().select(lang(30401), options)
|
||||
if resp > -1:
|
||||
selected = options[resp]
|
||||
|
||||
#display select dialog and process results
|
||||
header = utils.language(30401)
|
||||
ret = xbmcgui.Dialog().select(header, options)
|
||||
if ret != -1:
|
||||
if options[ret] == utils.language(30402):
|
||||
emby.updateUserRating(embyid, deletelike=True)
|
||||
if options[ret] == utils.language(30403):
|
||||
emby.updateUserRating(embyid, like=True)
|
||||
if options[ret] == utils.language(30404):
|
||||
emby.updateUserRating(embyid, like=False)
|
||||
if options[ret] == utils.language(30405):
|
||||
emby.updateUserRating(embyid, favourite=True)
|
||||
if options[ret] == utils.language(30406):
|
||||
emby.updateUserRating(embyid, favourite=False)
|
||||
if options[ret] == utils.language(30407):
|
||||
kodiconn = utils.kodiSQL('music')
|
||||
if selected == lang(30410):
|
||||
# Refresh item
|
||||
emby.refreshItem(itemId)
|
||||
elif selected == lang(30405):
|
||||
# Add favourite
|
||||
emby.updateUserRating(itemId, favourite=True)
|
||||
elif selected == lang(30406):
|
||||
# Delete favourite
|
||||
emby.updateUserRating(itemId, favourite=False)
|
||||
elif selected == lang(30407):
|
||||
# Update song rating
|
||||
kodiconn = kodiSQL('music')
|
||||
kodicursor = kodiconn.cursor()
|
||||
query = ' '.join(("SELECT rating", "FROM song", "WHERE idSong = ?" ))
|
||||
kodicursor.execute(query, (itemid,))
|
||||
currentvalue = int(round(float(kodicursor.fetchone()[0]),0))
|
||||
newvalue = xbmcgui.Dialog().numeric(0, "Set custom song rating (0-5)", str(currentvalue))
|
||||
if newvalue:
|
||||
newvalue = int(newvalue)
|
||||
if newvalue > 5: newvalue = "5"
|
||||
if utils.settings('enableUpdateSongRating') == "true":
|
||||
musicutils.updateRatingToFile(newvalue, API.getFilePath())
|
||||
if utils.settings('enableExportSongRating') == "true":
|
||||
like, favourite, deletelike = musicutils.getEmbyRatingFromKodiRating(newvalue)
|
||||
emby.updateUserRating(embyid, like, favourite, deletelike)
|
||||
query = ' '.join(( "UPDATE song","SET rating = ?", "WHERE idSong = ?" ))
|
||||
kodicursor.execute(query, (newvalue,itemid,))
|
||||
query = "SELECT rating FROM song WHERE idSong = ?"
|
||||
kodicursor.execute(query, (kodiId,))
|
||||
try:
|
||||
value = kodicursor.fetchone()[0]
|
||||
current_value = int(round(float(value),0))
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
new_value = dialog.numeric(0, lang(30411), str(current_value))
|
||||
if new_value > -1:
|
||||
|
||||
new_value = int(new_value)
|
||||
if new_value > 5:
|
||||
new_value = 5
|
||||
|
||||
if settings('enableUpdateSongRating') == "true":
|
||||
musicutils.updateRatingToFile(new_value, API.getFilePath())
|
||||
|
||||
query = "UPDATE song SET rating = ? WHERE idSong = ?"
|
||||
kodicursor.execute(query, (new_value, kodiId,))
|
||||
kodiconn.commit()
|
||||
|
||||
if options[ret] == utils.language(30408):
|
||||
'''if settings('enableExportSongRating') == "true":
|
||||
like, favourite, deletelike = musicutils.getEmbyRatingFromKodiRating(new_value)
|
||||
emby.updateUserRating(itemId, like, favourite, deletelike)'''
|
||||
finally:
|
||||
kodicursor.close()
|
||||
|
||||
elif selected == lang(30408):
|
||||
# Open addon settings
|
||||
xbmc.executebuiltin("Addon.OpenSettings(plugin.video.emby)")
|
||||
|
||||
if options[ret] == utils.language(30409):
|
||||
elif selected == lang(30409):
|
||||
# delete item from the server
|
||||
delete = True
|
||||
if utils.settings('skipContextMenu') != "true":
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
heading="Confirm delete",
|
||||
line1=("Delete file from Emby Server? This will "
|
||||
"also delete the file(s) from disk!"))
|
||||
if settings('skipContextMenu') != "true":
|
||||
resp = dialog.yesno(
|
||||
heading=lang(29999),
|
||||
line1=lang(33041))
|
||||
if not resp:
|
||||
logMsg("User skipped deletion for: %s." % embyid, 1)
|
||||
log("User skipped deletion for: %s." % itemId, 1)
|
||||
delete = False
|
||||
|
||||
if delete:
|
||||
import downloadutils
|
||||
doUtils = downloadutils.DownloadUtils()
|
||||
url = "{server}/emby/Items/%s?format=json" % embyid
|
||||
logMsg("Deleting request: %s" % embyid, 0)
|
||||
doUtils.downloadUrl(url, action_type="DELETE")
|
||||
|
||||
'''if utils.settings('skipContextMenu') != "true":
|
||||
if xbmcgui.Dialog().yesno(
|
||||
heading="Confirm delete",
|
||||
line1=("Delete file on Emby Server? This will "
|
||||
"also delete the file(s) from disk!")):
|
||||
import downloadutils
|
||||
doUtils = downloadutils.DownloadUtils()
|
||||
doUtils.downloadUrl("{server}/emby/Items/%s?format=json" % embyid, action_type="DELETE")'''
|
||||
log("Deleting request: %s" % itemId, 0)
|
||||
emby.deleteItem(itemId)
|
||||
|
||||
xbmc.sleep(500)
|
||||
xbmc.executebuiltin("Container.Update")
|
||||
xbmc.executebuiltin('Container.Refresh')
|
63
default.py
63
default.py
|
@ -12,30 +12,32 @@ import xbmcgui
|
|||
|
||||
#################################################################################################
|
||||
|
||||
addon_ = xbmcaddon.Addon(id='plugin.video.emby')
|
||||
addon_path = addon_.getAddonInfo('path').decode('utf-8')
|
||||
base_resource = xbmc.translatePath(os.path.join(addon_path, 'resources', 'lib')).decode('utf-8')
|
||||
sys.path.append(base_resource)
|
||||
_addon = xbmcaddon.Addon(id='plugin.video.emby')
|
||||
_addon_path = _addon.getAddonInfo('path').decode('utf-8')
|
||||
_base_resource = xbmc.translatePath(os.path.join(_addon_path, 'resources', 'lib')).decode('utf-8')
|
||||
sys.path.append(_base_resource)
|
||||
|
||||
#################################################################################################
|
||||
|
||||
import entrypoint
|
||||
import utils
|
||||
from utils import Logging, window, language as lang
|
||||
log = Logging('Default').log
|
||||
|
||||
#################################################################################################
|
||||
|
||||
enableProfiling = False
|
||||
|
||||
class Main:
|
||||
class Main():
|
||||
|
||||
|
||||
# MAIN ENTRY POINT
|
||||
#@utils.profiling()
|
||||
def __init__(self):
|
||||
|
||||
# Parse parameters
|
||||
base_url = sys.argv[0]
|
||||
params = urlparse.parse_qs(sys.argv[2][1:])
|
||||
xbmc.log("Parameter string: %s" % sys.argv[2])
|
||||
log("Parameter string: %s" % sys.argv[2], 0)
|
||||
try:
|
||||
mode = params['mode'][0]
|
||||
itemid = params.get('id')
|
||||
|
@ -70,11 +72,13 @@ class Main:
|
|||
embypath = sys.argv[2][1:]
|
||||
embyid = params.get('id',[""])[0]
|
||||
entrypoint.getExtraFanArt(embyid,embypath)
|
||||
return
|
||||
|
||||
if "/Extras" in sys.argv[0] or "/VideoFiles" in sys.argv[0]:
|
||||
embypath = sys.argv[2][1:]
|
||||
embyid = params.get('id',[""])[0]
|
||||
entrypoint.getVideoFiles(embyid, embypath)
|
||||
return
|
||||
|
||||
if modes.get(mode):
|
||||
# Simple functions
|
||||
|
@ -86,7 +90,7 @@ class Main:
|
|||
limit = int(params['limit'][0])
|
||||
modes[mode](itemid, limit)
|
||||
|
||||
elif mode in ["channels","getsubfolders"]:
|
||||
elif mode in ("channels","getsubfolders"):
|
||||
modes[mode](itemid)
|
||||
|
||||
elif mode == "browsecontent":
|
||||
|
@ -102,16 +106,17 @@ class Main:
|
|||
# Other functions
|
||||
if mode == "settings":
|
||||
xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)')
|
||||
|
||||
elif mode in ("manualsync", "fastsync", "repair"):
|
||||
if utils.window('emby_online') != "true":
|
||||
|
||||
if window('emby_online') != "true":
|
||||
# Server is not online, do not run the sync
|
||||
xbmcgui.Dialog().ok(heading="Emby for Kodi",
|
||||
line1=("Unable to run the sync, the add-on is not "
|
||||
"connected to the Emby server."))
|
||||
utils.logMsg("EMBY", "Not connected to the emby server.", 1)
|
||||
xbmcgui.Dialog().ok(heading=lang(29999),
|
||||
line1=lang(33034))
|
||||
log("Not connected to the emby server.", 1)
|
||||
return
|
||||
|
||||
if utils.window('emby_dbScan') != "true":
|
||||
if window('emby_dbScan') != "true":
|
||||
import librarysync
|
||||
lib = librarysync.LibrarySync()
|
||||
if mode == "manualsync":
|
||||
|
@ -121,35 +126,17 @@ class Main:
|
|||
else:
|
||||
lib.fullSync(repair=True)
|
||||
else:
|
||||
utils.logMsg("EMBY", "Database scan is already running.", 1)
|
||||
log("Database scan is already running.", 1)
|
||||
|
||||
elif mode == "texturecache":
|
||||
import artwork
|
||||
artwork.Artwork().FullTextureCacheSync()
|
||||
artwork.Artwork().fullTextureCacheSync()
|
||||
|
||||
else:
|
||||
entrypoint.doMainListing()
|
||||
|
||||
|
||||
if ( __name__ == "__main__" ):
|
||||
xbmc.log('plugin.video.emby started')
|
||||
|
||||
if enableProfiling:
|
||||
import cProfile
|
||||
import pstats
|
||||
import random
|
||||
from time import gmtime, strftime
|
||||
addonid = addon_.getAddonInfo('id').decode( 'utf-8' )
|
||||
datapath = os.path.join( xbmc.translatePath( "special://profile/" ).decode( 'utf-8' ), "addon_data", addonid )
|
||||
|
||||
filename = os.path.join( datapath, strftime( "%Y%m%d%H%M%S",gmtime() ) + "-" + str( random.randrange(0,100000) ) + ".log" )
|
||||
cProfile.run( 'Main()', filename )
|
||||
|
||||
stream = open( filename + ".txt", 'w')
|
||||
p = pstats.Stats( filename, stream = stream )
|
||||
p.sort_stats( "cumulative" )
|
||||
p.print_stats()
|
||||
|
||||
else:
|
||||
if __name__ == "__main__":
|
||||
log('plugin.video.emby started', 1)
|
||||
Main()
|
||||
|
||||
xbmc.log('plugin.video.emby stopped')
|
||||
log('plugin.video.emby stopped', 1)
|
|
@ -1,56 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<strings>
|
||||
|
||||
<!-- Add-on settings -->
|
||||
<string id="29999">Emby for Kodi</string>
|
||||
<string id="30000">Primary Server Address</string><!-- Verified -->
|
||||
<string id="30002">Play from HTTP instead of SMB</string><!-- Verified -->
|
||||
<string id="30004">Log level</string><!-- Verified -->
|
||||
<string id="30005">Username: </string>
|
||||
<string id="30006">Password: </string>
|
||||
<string id="30007">Network Username: </string>
|
||||
<string id="30008">Network Password: </string>
|
||||
<string id="30009">Transcode: </string>
|
||||
<string id="30010">Enable Performance Profiling</string>
|
||||
<string id="30011">Local caching system</string>
|
||||
|
||||
<string id="30014">Emby</string>
|
||||
<string id="30015">Network</string>
|
||||
<string id="30016">Device Name</string>
|
||||
|
||||
<string id="30016">Device Name</string><!-- Verified -->
|
||||
<string id="30022">Advanced</string>
|
||||
<string id="30024">Username</string><!-- Verified -->
|
||||
|
||||
<string id="30030">Port Number</string><!-- Verified -->
|
||||
|
||||
<string id="30035">Number of recent Music Albums to show:</string>
|
||||
<string id="30036">Number of recent Movies to show:</string>
|
||||
<string id="30037">Number of recent TV episodes to show:</string>
|
||||
<string id="30035">Number of recent Music Albums to show:</string>
|
||||
<string id="30038">Mark watched at start of playback:</string>
|
||||
<string id="30039">Set Season poster for episodes</string>
|
||||
|
||||
<string id="30040">Genre Filter ...</string>
|
||||
<string id="30041">Play All from Here</string>
|
||||
<string id="30042">Refresh</string>
|
||||
<string id="30043">Delete</string>
|
||||
<string id="30046">Add Movie to CouchPotato</string>
|
||||
|
||||
<string id="30044">Incorrect Username/Password</string>
|
||||
<string id="30045">Username not found</string>
|
||||
|
||||
<string id="30052">Deleting</string>
|
||||
<string id="30053">Waiting for server to delete</string>
|
||||
|
||||
<string id="30059">Server Default</string>
|
||||
<string id="30060">Title</string>
|
||||
<string id="30061">Year</string>
|
||||
<string id="30062">Premiere Date</string>
|
||||
<string id="30063">Date Created</string>
|
||||
<string id="30064">Critic Rating</string>
|
||||
<string id="30065">Community Rating</string>
|
||||
<string id="30066">Play Count</string>
|
||||
<string id="30067">Budget</string>
|
||||
<!-- Runtime added as 30226 below -->
|
||||
|
||||
<string id="30068">Sort By</string>
|
||||
|
||||
<string id="30069">None</string>
|
||||
<string id="30070">Action</string>
|
||||
<string id="30071">Adventure</string>
|
||||
|
@ -75,69 +47,40 @@
|
|||
|
||||
<string id="30090">Genre Filter</string>
|
||||
<string id="30091">Confirm file deletion</string><!-- Verified -->
|
||||
<string id="30092">Delete this item? This action will delete media and associated data files.</string>
|
||||
|
||||
<string id="30093">Mark Watched</string>
|
||||
<string id="30094">Mark Unwatched</string>
|
||||
<string id="30095">Add to Favorites</string>
|
||||
<string id="30096">Remove from Favorites</string>
|
||||
<string id="30097">Sort By ...</string>
|
||||
<string id="30093">Mark watched</string>
|
||||
<string id="30094">Mark unwatched</string>
|
||||
|
||||
<string id="30097">Sort by</string>
|
||||
<string id="30098">Sort Order Descending</string>
|
||||
<string id="30099">Sort Order Ascending</string>
|
||||
<string id="30100">Show People</string>
|
||||
|
||||
<!-- resume dialog -->
|
||||
<string id="30105">Resume</string>
|
||||
<string id="30106">Resume from</string>
|
||||
<string id="30107">Start from beginning</string>
|
||||
|
||||
<string id="30110">Interface</string>
|
||||
<string id="30111">Include Stream Info</string>
|
||||
<string id="30112">Include People</string>
|
||||
<string id="30113">Include Overview</string>
|
||||
<string id="30114">Offer delete after playback</string><!-- Verified -->
|
||||
<string id="30115">For Episodes</string><!-- Verified -->
|
||||
<string id="30116">For Movies</string><!-- Verified -->
|
||||
<string id="30117">Background Art Refresh Rate (seconds)</string>
|
||||
|
||||
<string id="30118">Add Resume Percent</string>
|
||||
<string id="30119">Add Episode Number</string>
|
||||
<string id="30120">Show Load Progress</string>
|
||||
<string id="30121">Loading Content</string>
|
||||
<string id="30122">Retrieving Data</string>
|
||||
|
||||
<string id="30125">Done</string>
|
||||
<string id="30126">Processing Item : </string>
|
||||
<string id="30128">Play Error</string>
|
||||
<string id="30129">This item is not playable</string>
|
||||
<string id="30130">Local path detected</string>
|
||||
<string id="30131">Your MB3 Server contains local paths. Please change server paths to UNC or change XBMB3C setting 'Play from Stream' to true. Path: </string>
|
||||
<string id="30132">Warning</string>
|
||||
<string id="30133">Debug logging enabled.</string>
|
||||
<string id="30134">This will affect performance.</string>
|
||||
<string id="30132">Warning</string><!-- Verified -->
|
||||
<string id="30135">Error</string>
|
||||
<string id="30136">Monitoring service is not running</string>
|
||||
<string id="30137">If you have just installed please restart Kodi</string>
|
||||
<string id="30138">Search</string>
|
||||
|
||||
<string id="30139">Enable Theme Music (Requires Restart)</string>
|
||||
<string id="30140"> - Loop Theme Music</string>
|
||||
<string id="30141">Enable Background Image (Requires Restart)</string>
|
||||
<string id="30142">Services</string>
|
||||
|
||||
<string id="30150">Skin does not support setting views</string>
|
||||
<string id="30151">Select item action (Requires Restart)</string>
|
||||
|
||||
<string id="30156">Sort NextUp by Show Title</string>
|
||||
<string id="30157">Enable Enhanced Images (eg CoverArt)</string><!-- Verified -->
|
||||
<string id="30158">Metadata</string>
|
||||
<string id="30159">Artwork</string>
|
||||
<string id="30160">Video Quality</string><!-- Verified -->
|
||||
|
||||
<string id="30161">Enable Suggested Loader (Requires Restart)</string>
|
||||
<string id="30162">Add Season Number</string>
|
||||
<string id="30163">Flatten Seasons</string>
|
||||
|
||||
<string id="30164">Direct Play - HTTP</string>
|
||||
<string id="30165">Direct Play</string>
|
||||
<string id="30165">Direct Play</string><!-- Verified -->
|
||||
<string id="30166">Transcoding</string>
|
||||
<string id="30167">Server Detection Succeeded</string>
|
||||
<string id="30168">Found server</string>
|
||||
|
@ -172,17 +115,10 @@
|
|||
<string id="30195">TV Networks</string>
|
||||
<string id="30196">TV Actors</string>
|
||||
<string id="30197">Playlists</string>
|
||||
<string id="30198">Search</string>
|
||||
|
||||
<string id="30199">Set Views</string>
|
||||
|
||||
<string id="30200">Select User</string>
|
||||
<string id="30201">Profiling enabled.</string>
|
||||
<string id="30202">Please remember to turn off when finished testing.</string>
|
||||
<string id="30203">Error in ArtworkRotationThread</string>
|
||||
<string id="30200">Select User</string><!-- Verified -->
|
||||
<string id="30204">Unable to connect to server</string>
|
||||
<string id="30205">Error in LoadMenuOptionsThread</string>
|
||||
|
||||
<string id="30206">Enable Playlists Loader (Requires Restart)</string>
|
||||
|
||||
<string id="30207">Songs</string>
|
||||
<string id="30208">Albums</string>
|
||||
|
@ -190,18 +126,11 @@
|
|||
<string id="30210">Artists</string>
|
||||
<string id="30211">Music Genres</string>
|
||||
|
||||
<string id="30212">Enable Theme Videos (Requires Restart)</string>
|
||||
<string id="30213"> - Loop Theme Videos</string>
|
||||
|
||||
<string id="30216">AutoPlay remaining episodes in a season</string>
|
||||
<string id="30218">Compress Artwork</string>
|
||||
<string id="30220">Latest</string>
|
||||
<string id="30221">In Progress</string>
|
||||
<string id="30222">NextUp</string>
|
||||
<string id="30223">User Views</string>
|
||||
<string id="30224">Report Metrics</string>
|
||||
<string id="30225">Use Kodi Sorting</string>
|
||||
<string id="30226">Runtime</string>
|
||||
|
||||
<string id="30227">Random Movies</string>
|
||||
<string id="30228">Random Episodes</string>
|
||||
|
@ -214,15 +143,9 @@
|
|||
<string id="30238">Sync Movie BoxSets</string>
|
||||
|
||||
<string id="30239">Reset local Kodi database</string><!-- Verified -->
|
||||
<string id="30240">Enable watched/resume status sync</string>
|
||||
<string id="30241">DB Sync Indication:</string>
|
||||
<string id="30242">Play Count Sync Indication:</string>
|
||||
<string id="30243">Enable HTTPS</string><!-- Verified -->
|
||||
<string id="30245">Force Transcoding Codecs</string>
|
||||
|
||||
<string id="30246">Enable Netflix style next up notification</string>
|
||||
<string id="30247"> - The number of seconds before the end to show the notification</string>
|
||||
<string id="30248">Show Emby Info dialog on play/select action</string>
|
||||
<string id="30249">Enable server connection message on startup</string><!-- Verified -->
|
||||
|
||||
<string id="30251">Recently added Home Videos</string><!-- Verified -->
|
||||
|
@ -252,14 +175,13 @@
|
|||
|
||||
<!-- contextmenu -->
|
||||
<string id="30401">Emby options</string>
|
||||
<string id="30402">Clear like for this item</string>
|
||||
<string id="30403">Like this item</string>
|
||||
<string id="30404">Dislike this item</string>
|
||||
<string id="30405">Add to Emby favorites</string>
|
||||
<string id="30406">Remove from Emby favorites</string>
|
||||
<string id="30407">Set custom song rating</string>
|
||||
<string id="30408">Emby addon settings</string>
|
||||
<string id="30409">Delete item from the server</string>
|
||||
<string id="30410">Refresh this item</string>
|
||||
<string id="30411">Set custom song rating (0-5)</string>
|
||||
|
||||
<!-- add-on settings -->
|
||||
<string id="30500">Verify Host SSL Certificate</string>
|
||||
|
@ -300,6 +222,7 @@
|
|||
<string id="30535">Generate a new device Id</string>
|
||||
<string id="30536">Sync when screensaver is deactivated</string>
|
||||
<string id="30537">Force Transcode Hi10P</string>
|
||||
<string id="30538">Disabled</string>
|
||||
|
||||
<!-- service add-on -->
|
||||
<string id="33000">Welcome</string>
|
||||
|
@ -337,4 +260,60 @@
|
|||
<string id="33032">Failed to generate a new device Id. See your logs for more information.</string>
|
||||
<string id="33033">A new device Id has been generated. Kodi will now restart.</string>
|
||||
|
||||
<string id="33034">Proceed with the following server?</string>
|
||||
<string id="33035">Caution! If you choose Native mode, certain Emby features will be missing, such as: Emby cinema mode, direct stream/transcode options and parental access schedule.</string>
|
||||
<string id="33036">Addon (Default)</string>
|
||||
<string id="33037">Native (Direct Paths)</string>
|
||||
<string id="33038">Add network credentials to allow Kodi access to your content? Important: Kodi will need to be restarted to see the credentials. They can also be added at a later time.</string>
|
||||
<string id="33039">Disable Emby music library?</string>
|
||||
<string id="33040">Direct stream the music library? Select this option if the music library will be remotely accessed.</string>
|
||||
<string id="33041">Delete file(s) from Emby Server? This will also delete the file(s) from disk!</string>
|
||||
<string id="33042">Running the caching process may take some time. Continue anyway?</string>
|
||||
<string id="33043">Artwork cache sync</string>
|
||||
<string id="33044">Reset existing artwork cache?</string>
|
||||
<string id="33045">Updating artwork cache:</string>
|
||||
<string id="33046">Waiting for all threads to exit:</string>
|
||||
<string id="33047">Kodi can't locate file:</string>
|
||||
<string id="33048">You may need to verify your network credentials in the add-on settings or use the Emby path substitution to format your path correctly (Emby dashboard > library). Stop syncing?</string>
|
||||
<string id="33049">Added:</string>
|
||||
<string id="33050">If you fail to log in too many times, the Emby server might lock your account. Proceed anyway?</string>
|
||||
<string id="33051">Live TV Channels (experimental)</string>
|
||||
<string id="33052">Live TV Recordings (experimental)</string>
|
||||
<string id="33053">Settings</string>
|
||||
<string id="33054">Add user to session</string>
|
||||
<string id="33055">Refresh Emby playlists/Video nodes</string>
|
||||
<string id="33056">Perform manual sync</string>
|
||||
<string id="33057">Repair local database (force update all content)</string>
|
||||
<string id="33058">Perform local database reset</string>
|
||||
<string id="33059">Cache all artwork</string>
|
||||
<string id="33060">Sync Emby Theme Media to Kodi</string>
|
||||
<string id="33061">Add/Remove user from the session</string>
|
||||
<string id="33062">Add user</string>
|
||||
<string id="33063">Remove user</string>
|
||||
<string id="33064">Remove user from the session</string>
|
||||
<string id="33065">Success!</string>
|
||||
<string id="33066">Removed from viewing session:</string>
|
||||
<string id="33067">Added to viewing session:</string>
|
||||
<string id="33068">Unable to add/remove user from the session.</string>
|
||||
<string id="33069">The task succeeded</string>
|
||||
<string id="33070">The task failed</string>
|
||||
<string id="33071">Direct Stream</string>
|
||||
<string id="33072">Playback method for your themes</string>
|
||||
<string id="33073">The settings file does not exist in TV Tunes. Change a setting and run the task again.</string>
|
||||
<string id="33074">Are you sure you want to reset your local Kodi database?</string>
|
||||
<string id="33075">Modify/Remove network credentials</string>
|
||||
<string id="33076">Modify</string>
|
||||
<string id="33077">Remove</string>
|
||||
<string id="33078">Removed:</string>
|
||||
<string id="33079">Enter the network username</string>
|
||||
<string id="33080">Enter the network password</string>
|
||||
<string id="33081">Added network credentials for:</string>
|
||||
<string id="33082">Input the server name or IP address as indicated in your emby library paths. For example, the server name: \\\\SERVER-PC\\path\\ is "SERVER-PC"</string>
|
||||
<string id="33083">Modify the server name or IP address</string>
|
||||
<string id="33084">Enter the server name or IP address</string>
|
||||
<string id="33085">Could not reset the database. Try again.</string>
|
||||
<string id="33086">Remove all cached artwork?</string>
|
||||
<string id="33087">Reset all Emby add-on settings?</string>
|
||||
<string id="33088">Database reset has completed, Kodi will now restart to apply the changes.</string>
|
||||
|
||||
</strings>
|
|
@ -5,7 +5,7 @@
|
|||
##################################################################################################
|
||||
|
||||
import clientinfo
|
||||
import utils
|
||||
from utils import Logging, settings
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
@ -13,17 +13,16 @@ import utils
|
|||
class API():
|
||||
|
||||
def __init__(self, item):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
# item is the api response
|
||||
self.item = item
|
||||
|
||||
self.clientinfo = clientinfo.ClientInfo()
|
||||
self.addonName = self.clientinfo.getAddonName()
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
|
||||
def getUserData(self):
|
||||
# Default
|
||||
|
@ -223,7 +222,7 @@ class API():
|
|||
resume = 0
|
||||
if resume_seconds:
|
||||
resume = round(float(resume_seconds), 6)
|
||||
jumpback = int(utils.settings('resumeJumpBack'))
|
||||
jumpback = int(settings('resumeJumpBack'))
|
||||
if resume > jumpback:
|
||||
# To avoid negative bookmark
|
||||
resume = resume - jumpback
|
||||
|
|
|
@ -12,9 +12,9 @@ import xbmc
|
|||
import xbmcgui
|
||||
import xbmcvfs
|
||||
|
||||
import utils
|
||||
import clientinfo
|
||||
import image_cache_thread
|
||||
from utils import Logging, window, settings, language as lang, kodiSQL
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -29,24 +29,25 @@ class Artwork():
|
|||
imageCacheThreads = []
|
||||
imageCacheLimitThreads = 0
|
||||
|
||||
|
||||
def __init__(self):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.clientinfo = clientinfo.ClientInfo()
|
||||
self.addonName = self.clientinfo.getAddonName()
|
||||
|
||||
self.enableTextureCache = utils.settings('enableTextureCache') == "true"
|
||||
self.imageCacheLimitThreads = int(utils.settings("imageCacheLimit"))
|
||||
self.enableTextureCache = settings('enableTextureCache') == "true"
|
||||
self.imageCacheLimitThreads = int(settings('imageCacheLimit'))
|
||||
self.imageCacheLimitThreads = int(self.imageCacheLimitThreads * 5)
|
||||
utils.logMsg("Using Image Cache Thread Count: " + str(self.imageCacheLimitThreads), 1)
|
||||
log("Using Image Cache Thread Count: %s" % self.imageCacheLimitThreads, 1)
|
||||
|
||||
if not self.xbmc_port and self.enableTextureCache:
|
||||
self.setKodiWebServerDetails()
|
||||
|
||||
self.userId = utils.window('emby_currUser')
|
||||
self.server = utils.window('emby_server%s' % self.userId)
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
self.userId = window('emby_currUser')
|
||||
self.server = window('emby_server%s' % self.userId)
|
||||
|
||||
|
||||
def double_urlencode(self, text):
|
||||
|
@ -56,8 +57,8 @@ class Artwork():
|
|||
return text
|
||||
|
||||
def single_urlencode(self, text):
|
||||
|
||||
text = urllib.urlencode({'blahblahblah':text.encode("utf-8")}) #urlencode needs a utf- string
|
||||
# urlencode needs a utf- string
|
||||
text = urllib.urlencode({'blahblahblah':text.encode("utf-8")})
|
||||
text = text[13:]
|
||||
|
||||
return text.decode("utf-8") #return the result again as unicode
|
||||
|
@ -164,130 +165,138 @@ class Artwork():
|
|||
except TypeError:
|
||||
pass
|
||||
|
||||
def FullTextureCacheSync(self):
|
||||
def fullTextureCacheSync(self):
|
||||
# This method will sync all Kodi artwork to textures13.db
|
||||
# and cache them locally. This takes diskspace!
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
if not xbmcgui.Dialog().yesno("Image Texture Cache", "Running the image cache process can take some time.", "Are you sure you want continue?"):
|
||||
if not dialog.yesno(
|
||||
heading=lang(29999),
|
||||
line1=lang(33042)):
|
||||
return
|
||||
|
||||
self.logMsg("Doing Image Cache Sync", 1)
|
||||
log("Doing Image Cache Sync", 1)
|
||||
|
||||
dialog = xbmcgui.DialogProgress()
|
||||
dialog.create("Emby for Kodi", "Image Cache Sync")
|
||||
pdialog = xbmcgui.DialogProgress()
|
||||
pdialog.create(lang(29999), lang(33043))
|
||||
|
||||
# ask to rest all existing or not
|
||||
if xbmcgui.Dialog().yesno("Image Texture Cache", "Reset all existing cache data first?", ""):
|
||||
self.logMsg("Resetting all cache data first", 1)
|
||||
if dialog.yesno(lang(29999), lang(33044)):
|
||||
log("Resetting all cache data first.", 1)
|
||||
|
||||
# Remove all existing textures first
|
||||
path = xbmc.translatePath("special://thumbnails/").decode('utf-8')
|
||||
path = xbmc.translatePath('special://thumbnails/').decode('utf-8')
|
||||
if xbmcvfs.exists(path):
|
||||
allDirs, allFiles = xbmcvfs.listdir(path)
|
||||
for dir in allDirs:
|
||||
allDirs, allFiles = xbmcvfs.listdir(path+dir)
|
||||
for file in allFiles:
|
||||
if os.path.supports_unicode_filenames:
|
||||
xbmcvfs.delete(os.path.join(path+dir.decode('utf-8'),file.decode('utf-8')))
|
||||
path = os.path.join(path+dir.decode('utf-8'),file.decode('utf-8'))
|
||||
xbmcvfs.delete(path)
|
||||
else:
|
||||
xbmcvfs.delete(os.path.join(path.encode('utf-8')+dir,file))
|
||||
|
||||
# remove all existing data from texture DB
|
||||
textureconnection = utils.kodiSQL('texture')
|
||||
texturecursor = textureconnection.cursor()
|
||||
texturecursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||
rows = texturecursor.fetchall()
|
||||
connection = kodiSQL('texture')
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
tableName = row[0]
|
||||
if(tableName != "version"):
|
||||
texturecursor.execute("DELETE FROM " + tableName)
|
||||
textureconnection.commit()
|
||||
texturecursor.close()
|
||||
if tableName != "version":
|
||||
cursor.execute("DELETE FROM " + tableName)
|
||||
connection.commit()
|
||||
cursor.close()
|
||||
|
||||
# Cache all entries in video DB
|
||||
connection = utils.kodiSQL('video')
|
||||
connection = kodiSQL('video')
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT url FROM art WHERE media_type != 'actor'") # dont include actors
|
||||
result = cursor.fetchall()
|
||||
total = len(result)
|
||||
count = 1
|
||||
percentage = 0
|
||||
self.logMsg("Image cache sync about to process " + str(total) + " images", 1)
|
||||
for url in result:
|
||||
if dialog.iscanceled():
|
||||
break
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
textMessage = str(count) + " of " + str(total) + " (" + str(len(self.imageCacheThreads)) + ")"
|
||||
dialog.update(percentage, "Updating Image Cache: " + textMessage)
|
||||
self.CacheTexture(url[0])
|
||||
count += 1
|
||||
log("Image cache sync about to process %s images" % total, 1)
|
||||
cursor.close()
|
||||
|
||||
count = 0
|
||||
for url in result:
|
||||
|
||||
if pdialog.iscanceled():
|
||||
break
|
||||
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
message = "%s of %s (%s)" % (count, total, self.imageCacheThreads)
|
||||
pdialog.update(percentage, "%s %s" % (lang(33045), message))
|
||||
self.cacheTexture(url[0])
|
||||
count += 1
|
||||
|
||||
# Cache all entries in music DB
|
||||
connection = utils.kodiSQL('music')
|
||||
connection = kodiSQL('music')
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT url FROM art")
|
||||
result = cursor.fetchall()
|
||||
total = len(result)
|
||||
count = 1
|
||||
percentage = 0
|
||||
self.logMsg("Image cache sync about to process " + str(total) + " images", 1)
|
||||
for url in result:
|
||||
if dialog.iscanceled():
|
||||
break
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
textMessage = str(count) + " of " + str(total)
|
||||
dialog.update(percentage, "Updating Image Cache: " + textMessage)
|
||||
self.CacheTexture(url[0])
|
||||
count += 1
|
||||
log("Image cache sync about to process %s images" % total, 1)
|
||||
cursor.close()
|
||||
|
||||
dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads)))
|
||||
self.logMsg("Waiting for all threads to exit", 1)
|
||||
while len(self.imageCacheThreads) > 0:
|
||||
count = 0
|
||||
for url in result:
|
||||
|
||||
if pdialog.iscanceled():
|
||||
break
|
||||
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
message = "%s of %s" % (count, total)
|
||||
pdialog.update(percentage, "%s %s" % (lang(33045), message))
|
||||
self.cacheTexture(url[0])
|
||||
count += 1
|
||||
|
||||
pdialog.update(100, "%s %s" % (lang(33046), len(self.imageCacheThreads)))
|
||||
log("Waiting for all threads to exit", 1)
|
||||
|
||||
while len(self.imageCacheThreads):
|
||||
for thread in self.imageCacheThreads:
|
||||
if thread.isFinished:
|
||||
self.imageCacheThreads.remove(thread)
|
||||
dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads)))
|
||||
self.logMsg("Waiting for all threads to exit: " + str(len(self.imageCacheThreads)), 1)
|
||||
pdialog.update(100, "%s %s" % (lang(33046), len(self.imageCacheThreads)))
|
||||
log("Waiting for all threads to exit: %s" % len(self.imageCacheThreads), 1)
|
||||
xbmc.sleep(500)
|
||||
|
||||
dialog.close()
|
||||
pdialog.close()
|
||||
|
||||
def addWorkerImageCacheThread(self, urlToAdd):
|
||||
def addWorkerImageCacheThread(self, url):
|
||||
|
||||
while(True):
|
||||
while True:
|
||||
# removed finished
|
||||
for thread in self.imageCacheThreads:
|
||||
if thread.isFinished:
|
||||
self.imageCacheThreads.remove(thread)
|
||||
|
||||
# add a new thread or wait and retry if we hit our limit
|
||||
if(len(self.imageCacheThreads) < self.imageCacheLimitThreads):
|
||||
if len(self.imageCacheThreads) < self.imageCacheLimitThreads:
|
||||
newThread = image_cache_thread.image_cache_thread()
|
||||
newThread.setUrl(self.double_urlencode(urlToAdd))
|
||||
newThread.setUrl(self.double_urlencode(url))
|
||||
newThread.setHost(self.xbmc_host, self.xbmc_port)
|
||||
newThread.setAuth(self.xbmc_username, self.xbmc_password)
|
||||
newThread.start()
|
||||
self.imageCacheThreads.append(newThread)
|
||||
return
|
||||
else:
|
||||
self.logMsg("Waiting for empty queue spot: " + str(len(self.imageCacheThreads)), 2)
|
||||
log("Waiting for empty queue spot: %s" % len(self.imageCacheThreads), 2)
|
||||
xbmc.sleep(50)
|
||||
|
||||
|
||||
def CacheTexture(self, url):
|
||||
def cacheTexture(self, url):
|
||||
# Cache a single image url to the texture cache
|
||||
if url and self.enableTextureCache:
|
||||
self.logMsg("Processing: %s" % url, 2)
|
||||
log("Processing: %s" % url, 2)
|
||||
|
||||
if(self.imageCacheLimitThreads == 0 or self.imageCacheLimitThreads == None):
|
||||
if not self.imageCacheLimitThreads:
|
||||
# Add image to texture cache by simply calling it at the http endpoint
|
||||
|
||||
url = self.double_urlencode(url)
|
||||
try: # Extreme short timeouts so we will have a exception.
|
||||
response = requests.head(
|
||||
url=(
|
||||
"http://%s:%s/image/image://%s"
|
||||
url=("http://%s:%s/image/image://%s"
|
||||
% (self.xbmc_host, self.xbmc_port, url)),
|
||||
auth=(self.xbmc_username, self.xbmc_password),
|
||||
timeout=(0.01, 0.01))
|
||||
|
@ -397,7 +406,7 @@ class Artwork():
|
|||
|
||||
except TypeError: # Add the artwork
|
||||
cacheimage = True
|
||||
self.logMsg("Adding Art Link for kodiId: %s (%s)" % (kodiId, imageUrl), 2)
|
||||
log("Adding Art Link for kodiId: %s (%s)" % (kodiId, imageUrl), 2)
|
||||
|
||||
query = (
|
||||
'''
|
||||
|
@ -413,13 +422,12 @@ class Artwork():
|
|||
cacheimage = True
|
||||
|
||||
# Only for the main backdrop, poster
|
||||
if (utils.window('emby_initialScan') != "true" and
|
||||
if (window('emby_initialScan') != "true" and
|
||||
imageType in ("fanart", "poster")):
|
||||
# Delete current entry before updating with the new one
|
||||
self.deleteCachedArtwork(url)
|
||||
|
||||
self.logMsg(
|
||||
"Updating Art url for %s kodiId: %s (%s) -> (%s)"
|
||||
log("Updating Art url for %s kodiId: %s (%s) -> (%s)"
|
||||
% (imageType, kodiId, url, imageUrl), 1)
|
||||
|
||||
query = ' '.join((
|
||||
|
@ -434,9 +442,9 @@ class Artwork():
|
|||
|
||||
# Cache fanart and poster in Kodi texture cache
|
||||
if cacheimage and imageType in ("fanart", "poster"):
|
||||
self.CacheTexture(imageUrl)
|
||||
self.cacheTexture(imageUrl)
|
||||
|
||||
def deleteArtwork(self, kodiid, mediatype, cursor):
|
||||
def deleteArtwork(self, kodiId, mediaType, cursor):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
|
@ -445,18 +453,18 @@ class Artwork():
|
|||
"WHERE media_id = ?",
|
||||
"AND media_type = ?"
|
||||
))
|
||||
cursor.execute(query, (kodiid, mediatype,))
|
||||
cursor.execute(query, (kodiId, mediaType,))
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
|
||||
url = row[0]
|
||||
imagetype = row[1]
|
||||
if imagetype in ("poster", "fanart"):
|
||||
imageType = row[1]
|
||||
if imageType in ("poster", "fanart"):
|
||||
self.deleteCachedArtwork(url)
|
||||
|
||||
def deleteCachedArtwork(self, url):
|
||||
# Only necessary to remove and apply a new backdrop or poster
|
||||
connection = utils.kodiSQL('texture')
|
||||
connection = kodiSQL('texture')
|
||||
cursor = connection.cursor()
|
||||
|
||||
try:
|
||||
|
@ -464,21 +472,21 @@ class Artwork():
|
|||
cachedurl = cursor.fetchone()[0]
|
||||
|
||||
except TypeError:
|
||||
self.logMsg("Could not find cached url.", 1)
|
||||
log("Could not find cached url.", 1)
|
||||
|
||||
except OperationalError:
|
||||
self.logMsg("Database is locked. Skip deletion process.", 1)
|
||||
log("Database is locked. Skip deletion process.", 1)
|
||||
|
||||
else: # Delete thumbnail as well as the entry
|
||||
thumbnails = xbmc.translatePath("special://thumbnails/%s" % cachedurl).decode('utf-8')
|
||||
self.logMsg("Deleting cached thumbnail: %s" % thumbnails, 1)
|
||||
log("Deleting cached thumbnail: %s" % thumbnails, 1)
|
||||
xbmcvfs.delete(thumbnails)
|
||||
|
||||
try:
|
||||
cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
|
||||
connection.commit()
|
||||
except OperationalError:
|
||||
self.logMsg("Issue deleting url from cache. Skipping.", 2)
|
||||
log("Issue deleting url from cache. Skipping.", 2)
|
||||
|
||||
finally:
|
||||
cursor.close()
|
||||
|
@ -501,10 +509,10 @@ class Artwork():
|
|||
|
||||
return people
|
||||
|
||||
def getUserArtwork(self, itemid, itemtype):
|
||||
def getUserArtwork(self, itemId, itemType):
|
||||
# Load user information set by UserClient
|
||||
image = ("%s/emby/Users/%s/Images/%s?Format=original"
|
||||
% (self.server, itemid, itemtype))
|
||||
% (self.server, itemId, itemType))
|
||||
return image
|
||||
|
||||
def getAllArtwork(self, item, parentInfo=False):
|
||||
|
@ -517,10 +525,10 @@ class Artwork():
|
|||
maxWidth = 10000
|
||||
customquery = ""
|
||||
|
||||
if utils.settings('compressArt') == "true":
|
||||
if settings('compressArt') == "true":
|
||||
customquery = "&Quality=90"
|
||||
|
||||
if utils.settings('enableCoverArt') == "false":
|
||||
if settings('enableCoverArt') == "false":
|
||||
customquery += "&EnableImageEnhancers=false"
|
||||
|
||||
allartworks = {
|
||||
|
|
|
@ -9,7 +9,7 @@ import xbmc
|
|||
import xbmcaddon
|
||||
import xbmcvfs
|
||||
|
||||
import utils
|
||||
from utils import Logging, window, settings
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -19,14 +19,12 @@ class ClientInfo():
|
|||
|
||||
def __init__(self):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.addon = xbmcaddon.Addon()
|
||||
self.addonName = self.getAddonName()
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
|
||||
def getAddonName(self):
|
||||
# Used for logging
|
||||
|
@ -42,11 +40,11 @@ class ClientInfo():
|
|||
|
||||
def getDeviceName(self):
|
||||
|
||||
if utils.settings('deviceNameOpt') == "false":
|
||||
if settings('deviceNameOpt') == "false":
|
||||
# Use Kodi's deviceName
|
||||
deviceName = xbmc.getInfoLabel('System.FriendlyName').decode('utf-8')
|
||||
else:
|
||||
deviceName = utils.settings('deviceName')
|
||||
deviceName = settings('deviceName')
|
||||
deviceName = deviceName.replace("\"", "_")
|
||||
deviceName = deviceName.replace("/", "_")
|
||||
|
||||
|
@ -71,15 +69,17 @@ class ClientInfo():
|
|||
|
||||
def getDeviceId(self, reset=False):
|
||||
|
||||
clientId = utils.window('emby_deviceId')
|
||||
clientId = window('emby_deviceId')
|
||||
if clientId:
|
||||
return clientId
|
||||
|
||||
addon_path = self.addon.getAddonInfo('path').decode('utf-8')
|
||||
if os.path.supports_unicode_filenames:
|
||||
GUID_file = xbmc.translatePath(os.path.join(addon_path, "machine_guid")).decode('utf-8')
|
||||
path = os.path.join(addon_path, "machine_guid")
|
||||
else:
|
||||
GUID_file = xbmc.translatePath(os.path.join(addon_path.encode("utf-8"), "machine_guid")).decode('utf-8')
|
||||
path = os.path.join(addon_path.encode('utf-8'), "machine_guid")
|
||||
|
||||
GUID_file = xbmc.translatePath(path).decode('utf-8')
|
||||
|
||||
if reset and xbmcvfs.exists(GUID_file):
|
||||
# Reset the file
|
||||
|
@ -88,14 +88,14 @@ class ClientInfo():
|
|||
GUID = xbmcvfs.File(GUID_file)
|
||||
clientId = GUID.read()
|
||||
if not clientId:
|
||||
self.logMsg("Generating a new deviceid...", 1)
|
||||
log("Generating a new deviceid...", 1)
|
||||
clientId = str("%012X" % uuid4())
|
||||
GUID = xbmcvfs.File(GUID_file, 'w')
|
||||
GUID.write(clientId)
|
||||
|
||||
GUID.close()
|
||||
|
||||
self.logMsg("DeviceId loaded: %s" % clientId, 1)
|
||||
utils.window('emby_deviceId', value=clientId)
|
||||
log("DeviceId loaded: %s" % clientId, 1)
|
||||
window('emby_deviceId', value=clientId)
|
||||
|
||||
return clientId
|
|
@ -6,8 +6,8 @@ import json
|
|||
import requests
|
||||
import logging
|
||||
|
||||
import utils
|
||||
import clientinfo
|
||||
from utils import Logging, window
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
@ -34,28 +34,26 @@ class ConnectUtils():
|
|||
|
||||
def __init__(self):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.__dict__ = self._shared_state
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
|
||||
def setUserId(self, userId):
|
||||
# Reserved for userclient only
|
||||
self.userId = userId
|
||||
self.logMsg("Set connect userId: %s" % userId, 2)
|
||||
log("Set connect userId: %s" % userId, 2)
|
||||
|
||||
def setServer(self, server):
|
||||
# Reserved for userclient only
|
||||
self.server = server
|
||||
self.logMsg("Set connect server: %s" % server, 2)
|
||||
log("Set connect server: %s" % server, 2)
|
||||
|
||||
def setToken(self, token):
|
||||
# Reserved for userclient only
|
||||
self.token = token
|
||||
self.logMsg("Set connect token: %s" % token, 2)
|
||||
log("Set connect token: %s" % token, 2)
|
||||
|
||||
|
||||
def startSession(self):
|
||||
|
@ -73,7 +71,7 @@ class ConnectUtils():
|
|||
if self.sslclient is not None:
|
||||
verify = self.sslclient
|
||||
except:
|
||||
self.logMsg("Could not load SSL settings.", 1)
|
||||
log("Could not load SSL settings.", 1)
|
||||
|
||||
# Start session
|
||||
self.c = requests.Session()
|
||||
|
@ -83,13 +81,13 @@ class ConnectUtils():
|
|||
self.c.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||
self.c.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||
|
||||
self.logMsg("Requests session started on: %s" % self.server, 1)
|
||||
log("Requests session started on: %s" % self.server, 1)
|
||||
|
||||
def stopSession(self):
|
||||
try:
|
||||
self.c.close()
|
||||
except Exception as e:
|
||||
self.logMsg("Requests session could not be terminated: %s" % e, 1)
|
||||
log("Requests session could not be terminated: %s" % e, 1)
|
||||
|
||||
def getHeader(self, authenticate=True):
|
||||
|
||||
|
@ -103,7 +101,7 @@ class ConnectUtils():
|
|||
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
'Accept': "application/json"
|
||||
}
|
||||
self.logMsg("Header: %s" % header, 1)
|
||||
log("Header: %s" % header, 1)
|
||||
|
||||
else:
|
||||
token = self.token
|
||||
|
@ -115,17 +113,17 @@ class ConnectUtils():
|
|||
'X-Application': "Kodi/%s" % version,
|
||||
'X-Connect-UserToken': token
|
||||
}
|
||||
self.logMsg("Header: %s" % header, 1)
|
||||
log("Header: %s" % header, 1)
|
||||
|
||||
return header
|
||||
|
||||
def doUrl(self, url, data=None, postBody=None, rtype="GET",
|
||||
parameters=None, authenticate=True, timeout=None):
|
||||
|
||||
window = utils.window
|
||||
log("=== ENTER connectUrl ===", 2)
|
||||
|
||||
self.logMsg("=== ENTER connectUrl ===", 2)
|
||||
default_link = ""
|
||||
|
||||
if timeout is None:
|
||||
timeout = self.timeout
|
||||
|
||||
|
@ -209,25 +207,25 @@ class ConnectUtils():
|
|||
verify=verifyssl)
|
||||
|
||||
##### THE RESPONSE #####
|
||||
self.logMsg(r.url, 1)
|
||||
self.logMsg(r, 1)
|
||||
log(r.url, 1)
|
||||
log(r, 1)
|
||||
|
||||
if r.status_code == 204:
|
||||
# No body in the response
|
||||
self.logMsg("====== 204 Success ======", 1)
|
||||
log("====== 204 Success ======", 1)
|
||||
|
||||
elif r.status_code == requests.codes.ok:
|
||||
|
||||
try:
|
||||
# UNICODE - JSON object
|
||||
r = r.json()
|
||||
self.logMsg("====== 200 Success ======", 1)
|
||||
self.logMsg("Response: %s" % r, 1)
|
||||
log("====== 200 Success ======", 1)
|
||||
log("Response: %s" % r, 1)
|
||||
return r
|
||||
|
||||
except:
|
||||
if r.headers.get('content-type') != "text/html":
|
||||
self.logMsg("Unable to convert the response for: %s" % url, 1)
|
||||
log("Unable to convert the response for: %s" % url, 1)
|
||||
else:
|
||||
r.raise_for_status()
|
||||
|
||||
|
@ -238,8 +236,8 @@ class ConnectUtils():
|
|||
pass
|
||||
|
||||
except requests.exceptions.ConnectTimeout as e:
|
||||
self.logMsg("Server timeout at: %s" % url, 0)
|
||||
self.logMsg(e, 1)
|
||||
log("Server timeout at: %s" % url, 0)
|
||||
log(e, 1)
|
||||
|
||||
except requests.exceptions.HTTPError as e:
|
||||
|
||||
|
@ -255,11 +253,11 @@ class ConnectUtils():
|
|||
pass
|
||||
|
||||
except requests.exceptions.SSLError as e:
|
||||
self.logMsg("Invalid SSL certificate for: %s" % url, 0)
|
||||
self.logMsg(e, 1)
|
||||
log("Invalid SSL certificate for: %s" % url, 0)
|
||||
log(e, 1)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
self.logMsg("Unknown error connecting to: %s" % url, 0)
|
||||
self.logMsg(e, 1)
|
||||
log("Unknown error connecting to: %s" % url, 0)
|
||||
log(e, 1)
|
||||
|
||||
return default_link
|
|
@ -9,14 +9,15 @@ import logging
|
|||
import xbmc
|
||||
import xbmcgui
|
||||
|
||||
import utils
|
||||
import clientinfo
|
||||
from utils import Logging, window, settings
|
||||
|
||||
##################################################################################################
|
||||
|
||||
# Disable requests logging
|
||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning, InsecurePlatformWarning
|
||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||
requests.packages.urllib3.disable_warnings(InsecurePlatformWarning)
|
||||
#logging.getLogger('requests').setLevel(logging.WARNING)
|
||||
|
||||
##################################################################################################
|
||||
|
@ -36,40 +37,38 @@ class DownloadUtils():
|
|||
|
||||
def __init__(self):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.__dict__ = self._shared_state
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
|
||||
def setUsername(self, username):
|
||||
# Reserved for userclient only
|
||||
self.username = username
|
||||
self.logMsg("Set username: %s" % username, 2)
|
||||
log("Set username: %s" % username, 2)
|
||||
|
||||
def setUserId(self, userId):
|
||||
# Reserved for userclient only
|
||||
self.userId = userId
|
||||
self.logMsg("Set userId: %s" % userId, 2)
|
||||
log("Set userId: %s" % userId, 2)
|
||||
|
||||
def setServer(self, server):
|
||||
# Reserved for userclient only
|
||||
self.server = server
|
||||
self.logMsg("Set server: %s" % server, 2)
|
||||
log("Set server: %s" % server, 2)
|
||||
|
||||
def setToken(self, token):
|
||||
# Reserved for userclient only
|
||||
self.token = token
|
||||
self.logMsg("Set token: %s" % token, 2)
|
||||
log("Set token: %s" % token, 2)
|
||||
|
||||
def setSSL(self, ssl, sslclient):
|
||||
# Reserved for userclient only
|
||||
self.sslverify = ssl
|
||||
self.sslclient = sslclient
|
||||
self.logMsg("Verify SSL host certificate: %s" % ssl, 2)
|
||||
self.logMsg("SSL client side certificate: %s" % sslclient, 2)
|
||||
log("Verify SSL host certificate: %s" % ssl, 2)
|
||||
log("SSL client side certificate: %s" % sslclient, 2)
|
||||
|
||||
|
||||
def postCapabilities(self, deviceId):
|
||||
|
@ -94,11 +93,11 @@ class DownloadUtils():
|
|||
)
|
||||
}
|
||||
|
||||
self.logMsg("Capabilities URL: %s" % url, 2)
|
||||
self.logMsg("Postdata: %s" % data, 2)
|
||||
log("Capabilities URL: %s" % url, 2)
|
||||
log("Postdata: %s" % data, 2)
|
||||
|
||||
self.downloadUrl(url, postBody=data, action_type="POST")
|
||||
self.logMsg("Posted capabilities to %s" % self.server, 2)
|
||||
log("Posted capabilities to %s" % self.server, 2)
|
||||
|
||||
# Attempt at getting sessionId
|
||||
url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId
|
||||
|
@ -107,20 +106,19 @@ class DownloadUtils():
|
|||
sessionId = result[0]['Id']
|
||||
|
||||
except (KeyError, TypeError):
|
||||
self.logMsg("Failed to retrieve sessionId.", 1)
|
||||
log("Failed to retrieve sessionId.", 1)
|
||||
|
||||
else:
|
||||
self.logMsg("Session: %s" % result, 2)
|
||||
self.logMsg("SessionId: %s" % sessionId, 1)
|
||||
utils.window('emby_sessionId', value=sessionId)
|
||||
log("Session: %s" % result, 2)
|
||||
log("SessionId: %s" % sessionId, 1)
|
||||
window('emby_sessionId', value=sessionId)
|
||||
|
||||
# Post any permanent additional users
|
||||
additionalUsers = utils.settings('additionalUsers')
|
||||
additionalUsers = settings('additionalUsers')
|
||||
if additionalUsers:
|
||||
|
||||
additionalUsers = additionalUsers.split(',')
|
||||
self.logMsg(
|
||||
"List of permanent users added to the session: %s"
|
||||
log("List of permanent users added to the session: %s"
|
||||
% additionalUsers, 1)
|
||||
|
||||
# Get the user list from server to get the userId
|
||||
|
@ -158,7 +156,7 @@ class DownloadUtils():
|
|||
if self.sslclient is not None:
|
||||
verify = self.sslclient
|
||||
except:
|
||||
self.logMsg("Could not load SSL settings.", 1)
|
||||
log("Could not load SSL settings.", 1)
|
||||
|
||||
# Start session
|
||||
self.s = requests.Session()
|
||||
|
@ -168,18 +166,18 @@ class DownloadUtils():
|
|||
self.s.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||
self.s.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
|
||||
|
||||
self.logMsg("Requests session started on: %s" % self.server, 1)
|
||||
log("Requests session started on: %s" % self.server, 1)
|
||||
|
||||
def stopSession(self):
|
||||
try:
|
||||
self.s.close()
|
||||
except:
|
||||
self.logMsg("Requests session could not be terminated.", 1)
|
||||
log("Requests session could not be terminated.", 1)
|
||||
|
||||
def getHeader(self, authenticate=True):
|
||||
|
||||
deviceName = self.clientInfo.getDeviceName()
|
||||
deviceName = utils.normalize_string(deviceName.encode('utf-8'))
|
||||
deviceName = deviceName.encode('utf-8')
|
||||
deviceId = self.clientInfo.getDeviceId()
|
||||
version = self.clientInfo.getVersion()
|
||||
|
||||
|
@ -195,7 +193,7 @@ class DownloadUtils():
|
|||
'Accept-Charset': 'UTF-8,*',
|
||||
'Authorization': auth
|
||||
}
|
||||
self.logMsg("Header: %s" % header, 2)
|
||||
log("Header: %s" % header, 2)
|
||||
|
||||
else:
|
||||
userId = self.userId
|
||||
|
@ -212,19 +210,20 @@ class DownloadUtils():
|
|||
'Authorization': auth,
|
||||
'X-MediaBrowser-Token': token
|
||||
}
|
||||
self.logMsg("Header: %s" % header, 2)
|
||||
log("Header: %s" % header, 2)
|
||||
|
||||
return header
|
||||
|
||||
def downloadUrl(self, url, postBody=None, action_type="GET", parameters=None, authenticate=True):
|
||||
def downloadUrl(self, url, postBody=None, action_type="GET", parameters=None,
|
||||
authenticate=True):
|
||||
|
||||
self.logMsg("=== ENTER downloadUrl ===", 2)
|
||||
log("=== ENTER downloadUrl ===", 2)
|
||||
|
||||
default_link = ""
|
||||
|
||||
try:
|
||||
# If user is authenticated
|
||||
if (authenticate):
|
||||
if authenticate:
|
||||
# Get requests session
|
||||
try:
|
||||
s = self.s
|
||||
|
@ -243,18 +242,18 @@ class DownloadUtils():
|
|||
except AttributeError:
|
||||
# request session does not exists
|
||||
# Get user information
|
||||
self.userId = utils.window('emby_currUser')
|
||||
self.server = utils.window('emby_server%s' % self.userId)
|
||||
self.token = utils.window('emby_accessToken%s' % self.userId)
|
||||
self.userId = window('emby_currUser')
|
||||
self.server = window('emby_server%s' % self.userId)
|
||||
self.token = window('emby_accessToken%s' % self.userId)
|
||||
header = self.getHeader()
|
||||
verifyssl = False
|
||||
cert = None
|
||||
|
||||
# IF user enables ssl verification
|
||||
if utils.settings('sslverify') == "true":
|
||||
if settings('sslverify') == "true":
|
||||
verifyssl = True
|
||||
if utils.settings('sslcert') != "None":
|
||||
verifyssl = utils.settings('sslcert')
|
||||
if settings('sslcert') != "None":
|
||||
verifyssl = settings('sslcert')
|
||||
|
||||
# Replace for the real values
|
||||
url = url.replace("{server}", self.server)
|
||||
|
@ -314,23 +313,23 @@ class DownloadUtils():
|
|||
verify=verifyssl)
|
||||
|
||||
##### THE RESPONSE #####
|
||||
self.logMsg(r.url, 2)
|
||||
log(r.url, 2)
|
||||
if r.status_code == 204:
|
||||
# No body in the response
|
||||
self.logMsg("====== 204 Success ======", 2)
|
||||
log("====== 204 Success ======", 2)
|
||||
|
||||
elif r.status_code == requests.codes.ok:
|
||||
|
||||
try:
|
||||
# UNICODE - JSON object
|
||||
r = r.json()
|
||||
self.logMsg("====== 200 Success ======", 2)
|
||||
self.logMsg("Response: %s" % r, 2)
|
||||
log("====== 200 Success ======", 2)
|
||||
log("Response: %s" % r, 2)
|
||||
return r
|
||||
|
||||
except:
|
||||
if r.headers.get('content-type') != "text/html":
|
||||
self.logMsg("Unable to convert the response for: %s" % url, 1)
|
||||
log("Unable to convert the response for: %s" % url, 1)
|
||||
else:
|
||||
r.raise_for_status()
|
||||
|
||||
|
@ -338,26 +337,26 @@ class DownloadUtils():
|
|||
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
# Make the addon aware of status
|
||||
if utils.window('emby_online') != "false":
|
||||
self.logMsg("Server unreachable at: %s" % url, 0)
|
||||
self.logMsg(e, 2)
|
||||
utils.window('emby_online', value="false")
|
||||
if window('emby_online') != "false":
|
||||
log("Server unreachable at: %s" % url, 0)
|
||||
log(e, 2)
|
||||
window('emby_online', value="false")
|
||||
|
||||
except requests.exceptions.ConnectTimeout as e:
|
||||
self.logMsg("Server timeout at: %s" % url, 0)
|
||||
self.logMsg(e, 1)
|
||||
log("Server timeout at: %s" % url, 0)
|
||||
log(e, 1)
|
||||
|
||||
except requests.exceptions.HTTPError as e:
|
||||
|
||||
if r.status_code == 401:
|
||||
# Unauthorized
|
||||
status = utils.window('emby_serverStatus')
|
||||
status = window('emby_serverStatus')
|
||||
|
||||
if 'X-Application-Error-Code' in r.headers:
|
||||
# Emby server errors
|
||||
if r.headers['X-Application-Error-Code'] == "ParentalControl":
|
||||
# Parental control - access restricted
|
||||
utils.window('emby_serverStatus', value="restricted")
|
||||
window('emby_serverStatus', value="restricted")
|
||||
xbmcgui.Dialog().notification(
|
||||
heading="Emby server",
|
||||
message="Access restricted.",
|
||||
|
@ -371,8 +370,8 @@ class DownloadUtils():
|
|||
|
||||
elif status not in ("401", "Auth"):
|
||||
# Tell userclient token has been revoked.
|
||||
utils.window('emby_serverStatus', value="401")
|
||||
self.logMsg("HTTP Error: %s" % e, 0)
|
||||
window('emby_serverStatus', value="401")
|
||||
log("HTTP Error: %s" % e, 0)
|
||||
xbmcgui.Dialog().notification(
|
||||
heading="Error connecting",
|
||||
message="Unauthorized.",
|
||||
|
@ -387,11 +386,11 @@ class DownloadUtils():
|
|||
pass
|
||||
|
||||
except requests.exceptions.SSLError as e:
|
||||
self.logMsg("Invalid SSL certificate for: %s" % url, 0)
|
||||
self.logMsg(e, 1)
|
||||
log("Invalid SSL certificate for: %s" % url, 0)
|
||||
log(e, 1)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
self.logMsg("Unknown error connecting to: %s" % url, 0)
|
||||
self.logMsg(e, 1)
|
||||
log("Unknown error connecting to: %s" % url, 0)
|
||||
log(e, 1)
|
||||
|
||||
return default_link
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
#################################################################################################
|
||||
|
||||
import utils
|
||||
from sqlite3 import OperationalError
|
||||
|
||||
import clientinfo
|
||||
from utils import Logging
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -13,15 +15,14 @@ class Embydb_Functions():
|
|||
|
||||
def __init__(self, embycursor):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.embycursor = embycursor
|
||||
|
||||
self.clientInfo = clientinfo.ClientInfo()
|
||||
self.addonName = self.clientInfo.getAddonName()
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
def getViews(self):
|
||||
|
||||
|
|
|
@ -24,28 +24,27 @@ import playlist
|
|||
import playbackutils as pbutils
|
||||
import playutils
|
||||
import api
|
||||
|
||||
from utils import Logging, window, settings, language as lang
|
||||
log = Logging('Entrypoint').log
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
||||
def doPlayback(itemid, dbid):
|
||||
def doPlayback(itemId, dbId):
|
||||
|
||||
emby = embyserver.Read_EmbyServer()
|
||||
item = emby.getItem(itemid)
|
||||
pbutils.PlaybackUtils(item).play(itemid, dbid)
|
||||
item = emby.getItem(itemId)
|
||||
pbutils.PlaybackUtils(item).play(itemId, dbId)
|
||||
|
||||
##### DO RESET AUTH #####
|
||||
def resetAuth():
|
||||
# User tried login and failed too many times
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
heading="Warning",
|
||||
line1=(
|
||||
"Emby might lock your account if you fail to log in too many times. "
|
||||
"Proceed anyway?"))
|
||||
if resp == 1:
|
||||
utils.logMsg("EMBY", "Reset login attempts.", 1)
|
||||
utils.window('emby_serverStatus', value="Auth")
|
||||
heading=lang(30132),
|
||||
line1=lang(33050))
|
||||
if resp:
|
||||
log("Reset login attempts.", 1)
|
||||
window('emby_serverStatus', value="Auth")
|
||||
else:
|
||||
xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)')
|
||||
|
||||
|
@ -57,65 +56,80 @@ def addDirectoryItem(label, path, folder=True):
|
|||
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=folder)
|
||||
|
||||
def doMainListing():
|
||||
|
||||
xbmcplugin.setContent(int(sys.argv[1]), 'files')
|
||||
# Get emby nodes from the window props
|
||||
embyprops = utils.window('Emby.nodes.total')
|
||||
embyprops = window('Emby.nodes.total')
|
||||
if embyprops:
|
||||
totalnodes = int(embyprops)
|
||||
for i in range(totalnodes):
|
||||
path = utils.window('Emby.nodes.%s.index' % i)
|
||||
path = window('Emby.nodes.%s.index' % i)
|
||||
if not path:
|
||||
path = utils.window('Emby.nodes.%s.content' % i)
|
||||
label = utils.window('Emby.nodes.%s.title' % 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 node_type == "photos":
|
||||
path = window('Emby.nodes.%s.content' % i)
|
||||
label = window('Emby.nodes.%s.title' % i)
|
||||
node = 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 node == "photos":
|
||||
addDirectoryItem(label, path)
|
||||
elif path and xbmc.getCondVisibility("Window.IsActive(VideoLibrary)") and node_type != "photos":
|
||||
elif path and xbmc.getCondVisibility("Window.IsActive(VideoLibrary)")
|
||||
and node != "photos":
|
||||
addDirectoryItem(label, path)
|
||||
elif path and not xbmc.getCondVisibility("Window.IsActive(VideoLibrary) | Window.IsActive(Pictures) | Window.IsActive(MusicLibrary)"):
|
||||
elif path and not xbmc.getCondVisibility("Window.IsActive(VideoLibrary) |
|
||||
Window.IsActive(Pictures) | Window.IsActive(MusicLibrary)"):
|
||||
addDirectoryItem(label, path)'''
|
||||
|
||||
if path:
|
||||
if xbmc.getCondVisibility("Window.IsActive(Pictures)") and node == "photos":
|
||||
addDirectoryItem(label, path)
|
||||
elif xbmc.getCondVisibility("Window.IsActive(VideoLibrary)") and node != "photos":
|
||||
addDirectoryItem(label, path)
|
||||
elif not xbmc.getCondVisibility("Window.IsActive(VideoLibrary) | Window.IsActive(Pictures) | Window.IsActive(MusicLibrary)"):
|
||||
addDirectoryItem(label, path)
|
||||
|
||||
# experimental live tv nodes
|
||||
addDirectoryItem("Live Tv Channels (experimental)", "plugin://plugin.video.emby/?mode=browsecontent&type=tvchannels&folderid=root")
|
||||
addDirectoryItem("Live Tv Recordings (experimental)", "plugin://plugin.video.emby/?mode=browsecontent&type=recordings&folderid=root")
|
||||
if not xbmc.getCondVisibility("Window.IsActive(Pictures)"):
|
||||
addDirectoryItem(lang(33051),
|
||||
"plugin://plugin.video.emby/?mode=browsecontent&type=tvchannels&folderid=root")
|
||||
addDirectoryItem(lang(33052),
|
||||
"plugin://plugin.video.emby/?mode=browsecontent&type=recordings&folderid=root")
|
||||
|
||||
# some extra entries for settings and stuff. TODO --> localize the labels
|
||||
addDirectoryItem("Network credentials", "plugin://plugin.video.emby/?mode=passwords")
|
||||
addDirectoryItem("Settings", "plugin://plugin.video.emby/?mode=settings")
|
||||
addDirectoryItem("Add user to session", "plugin://plugin.video.emby/?mode=adduser")
|
||||
addDirectoryItem("Refresh Emby playlists/nodes", "plugin://plugin.video.emby/?mode=refreshplaylist")
|
||||
addDirectoryItem("Perform manual sync", "plugin://plugin.video.emby/?mode=manualsync")
|
||||
addDirectoryItem("Repair local database (force update all content)", "plugin://plugin.video.emby/?mode=repair")
|
||||
addDirectoryItem("Perform local database reset (full resync)", "plugin://plugin.video.emby/?mode=reset")
|
||||
addDirectoryItem("Cache all images to Kodi texture cache", "plugin://plugin.video.emby/?mode=texturecache")
|
||||
addDirectoryItem("Sync Emby Theme Media to Kodi", "plugin://plugin.video.emby/?mode=thememedia")
|
||||
addDirectoryItem(lang(30517), "plugin://plugin.video.emby/?mode=passwords")
|
||||
addDirectoryItem(lang(33053), "plugin://plugin.video.emby/?mode=settings")
|
||||
addDirectoryItem(lang(33054), "plugin://plugin.video.emby/?mode=adduser")
|
||||
addDirectoryItem(lang(33055), "plugin://plugin.video.emby/?mode=refreshplaylist")
|
||||
addDirectoryItem(lang(33056), "plugin://plugin.video.emby/?mode=manualsync")
|
||||
addDirectoryItem(lang(33057), "plugin://plugin.video.emby/?mode=repair")
|
||||
addDirectoryItem(lang(33058), "plugin://plugin.video.emby/?mode=reset")
|
||||
addDirectoryItem(lang(33059), "plugin://plugin.video.emby/?mode=texturecache")
|
||||
addDirectoryItem(lang(33060), "plugin://plugin.video.emby/?mode=thememedia")
|
||||
|
||||
xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
||||
|
||||
|
||||
##### Generate a new deviceId
|
||||
def resetDeviceId():
|
||||
|
||||
dialog = xbmcgui.Dialog()
|
||||
language = utils.language
|
||||
|
||||
deviceId_old = utils.window('emby_deviceId')
|
||||
deviceId_old = window('emby_deviceId')
|
||||
try:
|
||||
utils.window('emby_deviceId', clear=True)
|
||||
window('emby_deviceId', clear=True)
|
||||
deviceId = clientinfo.ClientInfo().getDeviceId(reset=True)
|
||||
except Exception as e:
|
||||
utils.logMsg("EMBY", "Failed to generate a new device Id: %s" % e, 1)
|
||||
log("Failed to generate a new device Id: %s" % e, 1)
|
||||
dialog.ok(
|
||||
heading="Emby for Kodi",
|
||||
line1=language(33032))
|
||||
heading=lang(29999),
|
||||
line1=lang(33032))
|
||||
else:
|
||||
utils.logMsg("EMBY", "Successfully removed old deviceId: %s New deviceId: %s"
|
||||
% (deviceId_old, deviceId), 1)
|
||||
log("Successfully removed old deviceId: %s New deviceId: %s" % (deviceId_old, deviceId), 1)
|
||||
dialog.ok(
|
||||
heading="Emby for Kodi",
|
||||
line1=language(33033))
|
||||
heading=lang(29999),
|
||||
line1=lang(33033))
|
||||
xbmc.executebuiltin('RestartApp')
|
||||
|
||||
##### Delete Item
|
||||
|
@ -123,50 +137,46 @@ def deleteItem():
|
|||
|
||||
# Serves as a keymap action
|
||||
if xbmc.getInfoLabel('ListItem.Property(embyid)'): # If we already have the embyid
|
||||
embyid = xbmc.getInfoLabel('ListItem.Property(embyid)')
|
||||
itemId = xbmc.getInfoLabel('ListItem.Property(embyid)')
|
||||
else:
|
||||
dbid = xbmc.getInfoLabel('ListItem.DBID')
|
||||
itemtype = xbmc.getInfoLabel('ListItem.DBTYPE')
|
||||
dbId = xbmc.getInfoLabel('ListItem.DBID')
|
||||
itemType = xbmc.getInfoLabel('ListItem.DBTYPE')
|
||||
|
||||
if not itemtype:
|
||||
if not itemType:
|
||||
|
||||
if xbmc.getCondVisibility('Container.Content(albums)'):
|
||||
itemtype = "album"
|
||||
itemType = "album"
|
||||
elif xbmc.getCondVisibility('Container.Content(artists)'):
|
||||
itemtype = "artist"
|
||||
itemType = "artist"
|
||||
elif xbmc.getCondVisibility('Container.Content(songs)'):
|
||||
itemtype = "song"
|
||||
itemType = "song"
|
||||
elif xbmc.getCondVisibility('Container.Content(pictures)'):
|
||||
itemtype = "picture"
|
||||
itemType = "picture"
|
||||
else:
|
||||
utils.logMsg("EMBY delete", "Unknown type, unable to proceed.", 1)
|
||||
log("Unknown type, unable to proceed.", 1)
|
||||
return
|
||||
|
||||
embyconn = utils.kodiSQL('emby')
|
||||
embycursor = embyconn.cursor()
|
||||
emby_db = embydb.Embydb_Functions(embycursor)
|
||||
item = emby_db.getItem_byKodiId(dbid, itemtype)
|
||||
item = emby_db.getItem_byKodiId(dbId, itemType)
|
||||
embycursor.close()
|
||||
|
||||
try:
|
||||
embyid = item[0]
|
||||
except TypeError:
|
||||
utils.logMsg("EMBY delete", "Unknown embyId, unable to proceed.", 1)
|
||||
log("Unknown embyId, unable to proceed.", 1)
|
||||
return
|
||||
|
||||
if utils.settings('skipContextMenu') != "true":
|
||||
if settings('skipContextMenu') != "true":
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
heading="Confirm delete",
|
||||
line1=("Delete file from Emby Server? This will "
|
||||
"also delete the file(s) from disk!"))
|
||||
heading=lang(29999),
|
||||
line1=lang(33041))
|
||||
if not resp:
|
||||
utils.logMsg("EMBY delete", "User skipped deletion for: %s." % embyid, 1)
|
||||
log("User skipped deletion for: %s." % itemId, 1)
|
||||
return
|
||||
|
||||
doUtils = downloadutils.DownloadUtils()
|
||||
url = "{server}/emby/Items/%s?format=json" % embyid
|
||||
utils.logMsg("EMBY delete", "Deleting request: %s" % embyid, 0)
|
||||
doUtils.downloadUrl(url, action_type="DELETE")
|
||||
embyserver.Read_EmbyServer().deleteItem(itemId)
|
||||
|
||||
##### ADD ADDITIONAL USERS #####
|
||||
def addUser():
|
||||
|
@ -176,7 +186,7 @@ def addUser():
|
|||
clientInfo = clientinfo.ClientInfo()
|
||||
deviceId = clientInfo.getDeviceId()
|
||||
deviceName = clientInfo.getDeviceName()
|
||||
userid = utils.window('emby_currUser')
|
||||
userid = window('emby_currUser')
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
# Get session
|
||||
|
@ -203,7 +213,7 @@ def addUser():
|
|||
# Display dialog if there's additional users
|
||||
if additionalUsers:
|
||||
|
||||
option = dialog.select("Add/Remove user from the session", ["Add user", "Remove user"])
|
||||
option = dialog.select(lang(33061), [lang(33062), lang(33063)])
|
||||
# Users currently in the session
|
||||
additionalUserlist = {}
|
||||
additionalUsername = []
|
||||
|
@ -216,21 +226,21 @@ def addUser():
|
|||
|
||||
if option == 1:
|
||||
# User selected Remove user
|
||||
resp = dialog.select("Remove user from the session", additionalUsername)
|
||||
resp = dialog.select(lang(33064), additionalUsername)
|
||||
if resp > -1:
|
||||
selected = additionalUsername[resp]
|
||||
selected_userId = additionalUserlist[selected]
|
||||
url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId)
|
||||
doUtils.downloadUrl(url, postBody={}, action_type="DELETE")
|
||||
dialog.notification(
|
||||
heading="Success!",
|
||||
message="%s removed from viewing session" % selected,
|
||||
heading=lang(29999),
|
||||
message="%s %s" % (lang(33066), selected),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
time=1000)
|
||||
|
||||
# clear picture
|
||||
position = utils.window('EmbyAdditionalUserPosition.%s' % selected_userId)
|
||||
utils.window('EmbyAdditionalUserImage.%s' % position, clear=True)
|
||||
position = window('EmbyAdditionalUserPosition.%s' % selected_userId)
|
||||
window('EmbyAdditionalUserImage.%s' % position, clear=True)
|
||||
return
|
||||
else:
|
||||
return
|
||||
|
@ -247,7 +257,7 @@ def addUser():
|
|||
return
|
||||
|
||||
# Subtract any additional users
|
||||
utils.logMsg("EMBY", "Displaying list of users: %s" % users)
|
||||
log("Displaying list of users: %s" % users)
|
||||
resp = dialog.select("Add user to the session", users)
|
||||
# post additional user
|
||||
if resp > -1:
|
||||
|
@ -256,25 +266,25 @@ def addUser():
|
|||
url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId)
|
||||
doUtils.downloadUrl(url, postBody={}, action_type="POST")
|
||||
dialog.notification(
|
||||
heading="Success!",
|
||||
message="%s added to viewing session" % selected,
|
||||
heading=lang(29999),
|
||||
message="%s %s" % (lang(33067), selected),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
time=1000)
|
||||
|
||||
except:
|
||||
utils.logMsg("EMBY", "Failed to add user to session.")
|
||||
log("Failed to add user to session.")
|
||||
dialog.notification(
|
||||
heading="Error",
|
||||
message="Unable to add/remove user from the session.",
|
||||
heading=lang(29999),
|
||||
message=lang(33068),
|
||||
icon=xbmcgui.NOTIFICATION_ERROR)
|
||||
|
||||
# Add additional user images
|
||||
# always clear the individual items first
|
||||
totalNodes = 10
|
||||
for i in range(totalNodes):
|
||||
if not utils.window('EmbyAdditionalUserImage.%s' % i):
|
||||
if not window('EmbyAdditionalUserImage.%s' % i):
|
||||
break
|
||||
utils.window('EmbyAdditionalUserImage.%s' % i, clear=True)
|
||||
window('EmbyAdditionalUserImage.%s' % i, clear=True)
|
||||
|
||||
url = "{server}/emby/Sessions?DeviceId=%s" % deviceId
|
||||
result = doUtils.downloadUrl(url)
|
||||
|
@ -284,9 +294,9 @@ def addUser():
|
|||
userid = additionaluser['UserId']
|
||||
url = "{server}/emby/Users/%s?format=json" % userid
|
||||
result = doUtils.downloadUrl(url)
|
||||
utils.window('EmbyAdditionalUserImage.%s' % count,
|
||||
window('EmbyAdditionalUserImage.%s' % count,
|
||||
value=art.getUserArtwork(result['Id'], 'Primary'))
|
||||
utils.window('EmbyAdditionalUserPosition.%s' % userid, value=str(count))
|
||||
window('EmbyAdditionalUserPosition.%s' % userid, value=str(count))
|
||||
count +=1
|
||||
|
||||
##### THEME MUSIC/VIDEOS #####
|
||||
|
@ -297,7 +307,7 @@ def getThemeMedia():
|
|||
playback = None
|
||||
|
||||
# Choose playback method
|
||||
resp = dialog.select("Playback method for your themes", ["Direct Play", "Direct Stream"])
|
||||
resp = dialog.select(lang(33072), [lang(30165), lang(33071)])
|
||||
if resp == 0:
|
||||
playback = "DirectPlay"
|
||||
elif resp == 1:
|
||||
|
@ -318,15 +328,11 @@ def getThemeMedia():
|
|||
tvtunes = xbmcaddon.Addon(id="script.tvtunes")
|
||||
tvtunes.setSetting('custom_path_enable', "true")
|
||||
tvtunes.setSetting('custom_path', library)
|
||||
utils.logMsg("EMBY", "TV Tunes custom path is enabled and set.", 1)
|
||||
log("TV Tunes custom path is enabled and set.", 1)
|
||||
else:
|
||||
# if it does not exist this will not work so warn user
|
||||
# often they need to edit the settings first for it to be created.
|
||||
dialog.ok(
|
||||
heading="Warning",
|
||||
line1=(
|
||||
"The settings file does not exist in tvtunes. ",
|
||||
"Go to the tvtunes addon and change a setting, then come back and re-run."))
|
||||
dialog.ok(heading=lang(29999), line1=lang(33073))
|
||||
xbmc.executebuiltin('Addon.OpenSettings(script.tvtunes)')
|
||||
return
|
||||
|
||||
|
@ -442,8 +448,8 @@ def getThemeMedia():
|
|||
nfo_file.close()
|
||||
|
||||
dialog.notification(
|
||||
heading="Emby for Kodi",
|
||||
message="Themes added!",
|
||||
heading=lang(29999),
|
||||
message=lang(33069),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
time=1000,
|
||||
sound=False)
|
||||
|
@ -461,17 +467,17 @@ def refreshPlaylist():
|
|||
# Refresh views
|
||||
lib.refreshViews()
|
||||
dialog.notification(
|
||||
heading="Emby for Kodi",
|
||||
message="Emby playlists/nodes refreshed",
|
||||
heading=lang(29999),
|
||||
message=lang(33069),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
time=1000,
|
||||
sound=False)
|
||||
|
||||
except Exception as e:
|
||||
utils.logMsg("EMBY", "Refresh playlists/nodes failed: %s" % e, 1)
|
||||
log("Refresh playlists/nodes failed: %s" % e, 1)
|
||||
dialog.notification(
|
||||
heading="Emby for Kodi",
|
||||
message="Emby playlists/nodes refresh failed",
|
||||
heading=lang(29999),
|
||||
message=lang(33070),
|
||||
icon=xbmcgui.NOTIFICATION_ERROR,
|
||||
time=1000,
|
||||
sound=False)
|
||||
|
@ -480,9 +486,9 @@ def refreshPlaylist():
|
|||
def GetSubFolders(nodeindex):
|
||||
nodetypes = ["",".recent",".recentepisodes",".inprogress",".inprogressepisodes",".unwatched",".nextepisodes",".sets",".genres",".random",".recommended"]
|
||||
for node in nodetypes:
|
||||
title = utils.window('Emby.nodes.%s%s.title' %(nodeindex,node))
|
||||
title = window('Emby.nodes.%s%s.title' %(nodeindex,node))
|
||||
if title:
|
||||
path = utils.window('Emby.nodes.%s%s.content' %(nodeindex,node))
|
||||
path = window('Emby.nodes.%s%s.content' %(nodeindex,node))
|
||||
addDirectoryItem(title, path)
|
||||
xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
||||
|
||||
|
@ -510,7 +516,7 @@ def BrowseContent(viewname, browse_type="", folderid=""):
|
|||
break
|
||||
|
||||
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')))
|
||||
log("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:
|
||||
|
@ -795,7 +801,7 @@ def getNextUpEpisodes(tagname, limit):
|
|||
pass
|
||||
else:
|
||||
for item in items:
|
||||
if utils.settings('ignoreSpecialsNextEpisodes') == "true":
|
||||
if settings('ignoreSpecialsNextEpisodes') == "true":
|
||||
query = {
|
||||
|
||||
'jsonrpc': "2.0",
|
||||
|
@ -1043,7 +1049,7 @@ def getExtraFanArt(embyId,embyPath):
|
|||
|
||||
if embyId:
|
||||
#only proceed if we actually have a emby id
|
||||
utils.logMsg("EMBY", "Requesting extrafanart for Id: %s" % embyId, 0)
|
||||
log("Requesting extrafanart for Id: %s" % embyId, 0)
|
||||
|
||||
# We need to store the images locally for this to work
|
||||
# because of the caching system in xbmc
|
||||
|
@ -1072,7 +1078,7 @@ def getExtraFanArt(embyId,embyPath):
|
|||
xbmcvfs.copy(backdrop, fanartFile)
|
||||
count += 1
|
||||
else:
|
||||
utils.logMsg("EMBY", "Found cached backdrop.", 2)
|
||||
log("Found cached backdrop.", 2)
|
||||
# Use existing cached images
|
||||
dirs, files = xbmcvfs.listdir(fanartDir)
|
||||
for file in files:
|
||||
|
@ -1083,7 +1089,7 @@ def getExtraFanArt(embyId,embyPath):
|
|||
url=fanartFile,
|
||||
listitem=li)
|
||||
except Exception as e:
|
||||
utils.logMsg("EMBY", "Error getting extrafanart: %s" % e, 0)
|
||||
log("Error getting extrafanart: %s" % e, 0)
|
||||
|
||||
# Always do endofdirectory to prevent errors in the logs
|
||||
xbmcplugin.endOfDirectory(int(sys.argv[1]))
|
|
@ -1,8 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
#################################################################################################
|
||||
|
||||
import threading
|
||||
import utils
|
||||
import xbmc
|
||||
import requests
|
||||
|
||||
from utils import Logging
|
||||
|
||||
#################################################################################################
|
||||
|
||||
class image_cache_thread(threading.Thread):
|
||||
|
||||
urlToProcess = None
|
||||
|
@ -13,28 +19,32 @@ class image_cache_thread(threading.Thread):
|
|||
xbmc_username = ""
|
||||
xbmc_password = ""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.monitor = xbmc.Monitor()
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s" % className, msg, lvl)
|
||||
|
||||
def setUrl(self, url):
|
||||
|
||||
self.urlToProcess = url
|
||||
|
||||
def setHost(self, host, port):
|
||||
|
||||
self.xbmc_host = host
|
||||
self.xbmc_port = port
|
||||
|
||||
def setAuth(self, user, pwd):
|
||||
|
||||
self.xbmc_username = user
|
||||
self.xbmc_password = pwd
|
||||
|
||||
def run(self):
|
||||
|
||||
self.logMsg("Image Caching Thread Processing : " + self.urlToProcess, 2)
|
||||
log("Image Caching Thread Processing: %s" % self.urlToProcess, 2)
|
||||
|
||||
try:
|
||||
response = requests.head(
|
||||
|
@ -46,7 +56,5 @@ class image_cache_thread(threading.Thread):
|
|||
# We don't need the result
|
||||
except: pass
|
||||
|
||||
self.logMsg("Image Caching Thread Exited", 2)
|
||||
|
||||
log("Image Caching Thread Exited", 2)
|
||||
self.isFinished = True
|
||||
|
|
@ -9,10 +9,10 @@ import xbmc
|
|||
import xbmcgui
|
||||
import xbmcaddon
|
||||
|
||||
import utils
|
||||
import clientinfo
|
||||
import downloadutils
|
||||
import userclient
|
||||
from utils import Logging, settings, language as lang, passwordsXML
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -22,74 +22,66 @@ class InitialSetup():
|
|||
|
||||
def __init__(self):
|
||||
|
||||
self.addon = xbmcaddon.Addon()
|
||||
self.__language__ = self.addon.getLocalizedString
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.clientInfo = clientinfo.ClientInfo()
|
||||
self.addonName = self.clientInfo.getAddonName()
|
||||
self.addonId = self.clientInfo.getAddonId()
|
||||
self.doUtils = downloadutils.DownloadUtils()
|
||||
self.addonId = clientinfo.ClientInfo().getAddonId()
|
||||
self.doUtils = downloadutils.DownloadUtils().downloadUrl
|
||||
self.userClient = userclient.UserClient()
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
|
||||
def setup(self):
|
||||
# Check server, user, direct paths, music, direct stream if not direct path.
|
||||
string = self.__language__
|
||||
addonId = self.addonId
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
##### SERVER INFO #####
|
||||
|
||||
self.logMsg("Initial setup called.", 2)
|
||||
log("Initial setup called.", 2)
|
||||
server = self.userClient.getServer()
|
||||
|
||||
if server:
|
||||
self.logMsg("Server is already set.", 2)
|
||||
log("Server is already set.", 2)
|
||||
return
|
||||
|
||||
self.logMsg("Looking for server...", 2)
|
||||
log("Looking for server...", 2)
|
||||
server = self.getServerDetails()
|
||||
self.logMsg("Found: %s" % server, 2)
|
||||
log("Found: %s" % server, 2)
|
||||
try:
|
||||
prefix, ip, port = server.replace("/", "").split(":")
|
||||
except: # Failed to retrieve server information
|
||||
self.logMsg("getServerDetails failed.", 1)
|
||||
log("getServerDetails failed.", 1)
|
||||
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId)
|
||||
return
|
||||
else:
|
||||
server_confirm = xbmcgui.Dialog().yesno(
|
||||
heading="Emby for Kodi",
|
||||
line1="Proceed with the following server?",
|
||||
line2="%s %s" % (string(30169), server))
|
||||
server_confirm = dialog.yesno(
|
||||
heading=lang(29999),
|
||||
line1=lang(33034),
|
||||
line2="%s %s" % (lang(30169), server))
|
||||
if server_confirm:
|
||||
# Correct server found
|
||||
self.logMsg("Server is selected. Saving the information.", 1)
|
||||
utils.settings('ipaddress', value=ip)
|
||||
utils.settings('port', value=port)
|
||||
log("Server is selected. Saving the information.", 1)
|
||||
settings('ipaddress', value=ip)
|
||||
settings('port', value=port)
|
||||
|
||||
if prefix == "https":
|
||||
utils.settings('https', value="true")
|
||||
settings('https', value="true")
|
||||
else:
|
||||
# User selected no or cancelled the dialog
|
||||
self.logMsg("No server selected.", 1)
|
||||
log("No server selected.", 1)
|
||||
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId)
|
||||
return
|
||||
|
||||
##### USER INFO #####
|
||||
|
||||
self.logMsg("Getting user list.", 1)
|
||||
log("Getting user list.", 1)
|
||||
|
||||
url = "%s/emby/Users/Public?format=json" % server
|
||||
result = self.doUtils.downloadUrl(url, authenticate=False)
|
||||
result = self.doUtils("%s/emby/Users/Public?format=json" % server, authenticate=False)
|
||||
if result == "":
|
||||
self.logMsg("Unable to connect to %s" % server, 1)
|
||||
log("Unable to connect to %s" % server, 1)
|
||||
return
|
||||
|
||||
self.logMsg("Response: %s" % result, 2)
|
||||
log("Response: %s" % result, 2)
|
||||
# Process the list of users
|
||||
usernames = []
|
||||
users_hasPassword = []
|
||||
|
@ -103,66 +95,55 @@ class InitialSetup():
|
|||
name = "%s (secure)" % name
|
||||
users_hasPassword.append(name)
|
||||
|
||||
self.logMsg("Presenting user list: %s" % users_hasPassword, 1)
|
||||
user_select = xbmcgui.Dialog().select(string(30200), users_hasPassword)
|
||||
log("Presenting user list: %s" % users_hasPassword, 1)
|
||||
user_select = dialog.select(lang(30200), users_hasPassword)
|
||||
if user_select > -1:
|
||||
selected_user = usernames[user_select]
|
||||
self.logMsg("Selected user: %s" % selected_user, 1)
|
||||
utils.settings('username', value=selected_user)
|
||||
log("Selected user: %s" % selected_user, 1)
|
||||
settings('username', value=selected_user)
|
||||
else:
|
||||
self.logMsg("No user selected.", 1)
|
||||
log("No user selected.", 1)
|
||||
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId)
|
||||
return
|
||||
|
||||
##### ADDITIONAL PROMPTS #####
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
directPaths = dialog.yesno(
|
||||
heading="Playback Mode",
|
||||
line1=(
|
||||
"Caution! If you choose Native mode, you "
|
||||
"will lose access to certain Emby features such as: "
|
||||
"Emby cinema mode, direct stream/transcode options, "
|
||||
"parental access schedule."),
|
||||
nolabel="Addon (Default)",
|
||||
yeslabel="Native (Direct Paths)")
|
||||
heading=lang(30511),
|
||||
line1=lang(33035),
|
||||
nolabel=lang(33036),
|
||||
yeslabel=lang(33037))
|
||||
if directPaths:
|
||||
self.logMsg("User opted to use direct paths.", 1)
|
||||
utils.settings('useDirectPaths', value="1")
|
||||
log("User opted to use direct paths.", 1)
|
||||
settings('useDirectPaths', value="1")
|
||||
|
||||
# ask for credentials
|
||||
credentials = dialog.yesno(
|
||||
heading="Network credentials",
|
||||
line1= (
|
||||
"Add network credentials to allow Kodi access to your "
|
||||
"content? Note: Skipping this step may generate a message "
|
||||
"during the initial scan of your content if Kodi can't "
|
||||
"locate your content."))
|
||||
heading=lang(30517),
|
||||
line1= lang(33038))
|
||||
if credentials:
|
||||
self.logMsg("Presenting network credentials dialog.", 1)
|
||||
utils.passwordsXML()
|
||||
log("Presenting network credentials dialog.", 1)
|
||||
passwordsXML()
|
||||
|
||||
musicDisabled = dialog.yesno(
|
||||
heading="Music Library",
|
||||
line1="Disable Emby music library?")
|
||||
heading=lang(29999),
|
||||
line1=lang(33039))
|
||||
if musicDisabled:
|
||||
self.logMsg("User opted to disable Emby music library.", 1)
|
||||
utils.settings('enableMusic', value="false")
|
||||
log("User opted to disable Emby music library.", 1)
|
||||
settings('enableMusic', value="false")
|
||||
else:
|
||||
# Only prompt if the user didn't select direct paths for videos
|
||||
if not directPaths:
|
||||
musicAccess = dialog.yesno(
|
||||
heading="Music Library",
|
||||
line1=(
|
||||
"Direct stream the music library? Select "
|
||||
"this option only if you plan on listening "
|
||||
"to music outside of your network."))
|
||||
heading=lang(29999),
|
||||
line1=lang(33040))
|
||||
if musicAccess:
|
||||
self.logMsg("User opted to direct stream music.", 1)
|
||||
utils.settings('streamMusic', value="true")
|
||||
log("User opted to direct stream music.", 1)
|
||||
settings('streamMusic', value="true")
|
||||
|
||||
def getServerDetails(self):
|
||||
|
||||
self.logMsg("Getting Server Details from Network", 1)
|
||||
log("Getting Server Details from Network", 1)
|
||||
|
||||
MULTI_GROUP = ("<broadcast>", 7359)
|
||||
MESSAGE = "who is EmbyServer?"
|
||||
|
@ -176,15 +157,15 @@ class InitialSetup():
|
|||
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)
|
||||
sock.setsockopt(socket.IPPROTO_IP, socket.SO_REUSEADDR, 1)
|
||||
|
||||
self.logMsg("MultiGroup : %s" % str(MULTI_GROUP), 2)
|
||||
self.logMsg("Sending UDP Data: %s" % MESSAGE, 2)
|
||||
log("MultiGroup : %s" % str(MULTI_GROUP), 2)
|
||||
log("Sending UDP Data: %s" % MESSAGE, 2)
|
||||
sock.sendto(MESSAGE, MULTI_GROUP)
|
||||
|
||||
try:
|
||||
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
|
||||
self.logMsg("Received Response: %s" % data)
|
||||
log("Received Response: %s" % data)
|
||||
except:
|
||||
self.logMsg("No UDP Response")
|
||||
log("No UDP Response")
|
||||
return None
|
||||
else:
|
||||
# Get the address
|
||||
|
|
|
@ -14,11 +14,11 @@ import api
|
|||
import artwork
|
||||
import clientinfo
|
||||
import downloadutils
|
||||
import utils
|
||||
import embydb_functions as embydb
|
||||
import kodidb_functions as kodidb
|
||||
import read_embyserver as embyserver
|
||||
import musicutils
|
||||
from utils import Logging, window, settings, language as lang, kodiSQL
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
@ -28,6 +28,9 @@ class Items(object):
|
|||
|
||||
def __init__(self, embycursor, kodicursor):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.embycursor = embycursor
|
||||
self.kodicursor = kodicursor
|
||||
|
||||
|
@ -35,23 +38,18 @@ class Items(object):
|
|||
self.addonName = self.clientInfo.getAddonName()
|
||||
self.doUtils = downloadutils.DownloadUtils()
|
||||
|
||||
self.kodiversion = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
|
||||
self.directpath = utils.settings('useDirectPaths') == "1"
|
||||
self.music_enabled = utils.settings('enableMusic') == "true"
|
||||
self.contentmsg = utils.settings('newContent') == "true"
|
||||
self.newvideo_time = int(utils.settings('newvideotime'))*1000
|
||||
self.newmusic_time = int(utils.settings('newmusictime'))*1000
|
||||
self.kodiversion = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
|
||||
self.directpath = settings('useDirectPaths') == "1"
|
||||
self.music_enabled = settings('enableMusic') == "true"
|
||||
self.contentmsg = settings('newContent') == "true"
|
||||
self.newvideo_time = int(settings('newvideotime'))*1000
|
||||
self.newmusic_time = int(settings('newmusictime'))*1000
|
||||
|
||||
self.artwork = artwork.Artwork()
|
||||
self.emby = embyserver.Read_EmbyServer()
|
||||
self.emby_db = embydb.Embydb_Functions(embycursor)
|
||||
self.kodi_db = kodidb.Kodidb_Functions(kodicursor)
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
|
||||
def itemsbyId(self, items, process, pdialog=None):
|
||||
# Process items by itemid. Process can be added, update, userdata, remove
|
||||
|
@ -81,7 +79,7 @@ class Items(object):
|
|||
if total == 0:
|
||||
return False
|
||||
|
||||
self.logMsg("Processing %s: %s" % (process, items), 1)
|
||||
log("Processing %s: %s" % (process, items), 1)
|
||||
if pdialog:
|
||||
pdialog.update(heading="Processing %s: %s items" % (process, total))
|
||||
|
||||
|
@ -102,7 +100,7 @@ class Items(object):
|
|||
|
||||
if itemtype in ('MusicAlbum', 'MusicArtist', 'AlbumArtist', 'Audio'):
|
||||
if music_enabled:
|
||||
musicconn = utils.kodiSQL('music')
|
||||
musicconn = kodiSQL('music')
|
||||
musiccursor = musicconn.cursor()
|
||||
items_process = itemtypes[itemtype](embycursor, musiccursor)
|
||||
else:
|
||||
|
@ -173,7 +171,7 @@ class Items(object):
|
|||
'remove': items_process.remove
|
||||
}
|
||||
else:
|
||||
self.logMsg("Unsupported itemtype: %s." % itemtype, 1)
|
||||
log("Unsupported itemtype: %s." % itemtype, 1)
|
||||
actions = {}
|
||||
|
||||
if actions.get(process):
|
||||
|
@ -192,7 +190,7 @@ class Items(object):
|
|||
title = item['Name']
|
||||
|
||||
if itemtype == "Episode":
|
||||
title = "%s - %s" % (item['SeriesName'], title)
|
||||
title = "%s - %s" % (item.get('SeriesName', "Unknown"), title)
|
||||
|
||||
if pdialog:
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
|
@ -204,19 +202,31 @@ class Items(object):
|
|||
|
||||
if musicconn is not None:
|
||||
# close connection for special types
|
||||
self.logMsg("Updating music database.", 1)
|
||||
log("Updating music database.", 1)
|
||||
musicconn.commit()
|
||||
musiccursor.close()
|
||||
|
||||
return (True, update_videolibrary)
|
||||
|
||||
def pathValidation(self, path):
|
||||
# Verify if direct path is accessible or not
|
||||
if window('emby_pathverified') != "true" and not xbmcvfs.exists(path):
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
heading=lang(29999),
|
||||
line1="%s %s. %s" % (lang(33047), path, lang(33048)))
|
||||
if resp:
|
||||
window('emby_shouldStop', value="true")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def contentPop(self, name, time=5000):
|
||||
|
||||
if time:
|
||||
# It's possible for the time to be 0. It should be considered disabled in this case.
|
||||
xbmcgui.Dialog().notification(
|
||||
heading="Emby for Kodi",
|
||||
message="Added: %s" % name,
|
||||
heading=lang(29999),
|
||||
message="%s %s" % (lang(33049), name),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
time=time,
|
||||
sound=False)
|
||||
|
@ -272,11 +282,11 @@ class Movies(Items):
|
|||
movieid = emby_dbitem[0]
|
||||
fileid = emby_dbitem[1]
|
||||
pathid = emby_dbitem[2]
|
||||
self.logMsg("movieid: %s fileid: %s pathid: %s" % (movieid, fileid, pathid), 1)
|
||||
log("movieid: %s fileid: %s pathid: %s" % (movieid, fileid, pathid), 1)
|
||||
|
||||
except TypeError:
|
||||
update_item = False
|
||||
self.logMsg("movieid: %s not found." % itemid, 2)
|
||||
log("movieid: %s not found." % itemid, 2)
|
||||
# movieid
|
||||
kodicursor.execute("select coalesce(max(idMovie),0) from movie")
|
||||
movieid = kodicursor.fetchone()[0] + 1
|
||||
|
@ -290,12 +300,12 @@ class Movies(Items):
|
|||
except TypeError:
|
||||
# item is not found, let's recreate it.
|
||||
update_item = False
|
||||
self.logMsg("movieid: %s missing from Kodi, repairing the entry." % movieid, 1)
|
||||
log("movieid: %s missing from Kodi, repairing the entry." % movieid, 1)
|
||||
|
||||
if not viewtag or not viewid:
|
||||
# Get view tag from emby
|
||||
viewtag, viewid, mediatype = self.emby.getView_embyId(itemid)
|
||||
self.logMsg("View tag found: %s" % viewtag, 2)
|
||||
log("View tag found: %s" % viewtag, 2)
|
||||
|
||||
# fileId information
|
||||
checksum = API.getChecksum()
|
||||
|
@ -338,7 +348,7 @@ class Movies(Items):
|
|||
try:
|
||||
trailer = "plugin://plugin.video.emby/trailer/?id=%s&mode=play" % result[0]['Id']
|
||||
except IndexError:
|
||||
self.logMsg("Failed to process local trailer.", 1)
|
||||
log("Failed to process local trailer.", 1)
|
||||
trailer = None
|
||||
else:
|
||||
# Try to get the youtube trailer
|
||||
|
@ -350,7 +360,7 @@ class Movies(Items):
|
|||
try:
|
||||
trailerId = trailer.rsplit('=', 1)[1]
|
||||
except IndexError:
|
||||
self.logMsg("Failed to process trailer: %s" % trailer, 1)
|
||||
log("Failed to process trailer: %s" % trailer, 1)
|
||||
trailer = None
|
||||
else:
|
||||
trailer = "plugin://plugin.video.youtube/play/?video_id=%s" % trailerId
|
||||
|
@ -367,22 +377,11 @@ class Movies(Items):
|
|||
|
||||
if self.directpath:
|
||||
# Direct paths is set the Kodi way
|
||||
if utils.window('emby_pathverified') != "true" and not xbmcvfs.exists(playurl):
|
||||
# Validate the path is correct with user intervention
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
heading="Can't validate path",
|
||||
line1=(
|
||||
"Kodi can't locate file: %s. Verify the path. "
|
||||
"You may to verify your network credentials in the "
|
||||
"add-on settings or use the emby path substitution "
|
||||
"to format your path correctly. Stop syncing?"
|
||||
% playurl))
|
||||
if resp:
|
||||
utils.window('emby_shouldStop', value="true")
|
||||
if not self.pathValidation(playurl):
|
||||
return False
|
||||
|
||||
path = playurl.replace(filename, "")
|
||||
utils.window('emby_pathverified', value="true")
|
||||
window('emby_pathverified', value="true")
|
||||
else:
|
||||
# Set plugin path and media flags using real filename
|
||||
path = "plugin://plugin.video.emby.movies/"
|
||||
|
@ -398,7 +397,7 @@ class Movies(Items):
|
|||
|
||||
##### UPDATE THE MOVIE #####
|
||||
if update_item:
|
||||
self.logMsg("UPDATE movie itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
log("UPDATE movie itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
|
||||
# Update the movie entry
|
||||
query = ' '.join((
|
||||
|
@ -418,7 +417,7 @@ class Movies(Items):
|
|||
|
||||
##### OR ADD THE MOVIE #####
|
||||
else:
|
||||
self.logMsg("ADD movie itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
log("ADD movie itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
|
||||
# Add path
|
||||
pathid = self.kodi_db.addPath(path)
|
||||
|
@ -528,10 +527,10 @@ class Movies(Items):
|
|||
try:
|
||||
movieid = emby_dbitem[0]
|
||||
except TypeError:
|
||||
self.logMsg("Failed to add: %s to boxset." % movie['Name'], 1)
|
||||
log("Failed to add: %s to boxset." % movie['Name'], 1)
|
||||
continue
|
||||
|
||||
self.logMsg("New addition to boxset %s: %s" % (title, movie['Name']), 1)
|
||||
log("New addition to boxset %s: %s" % (title, movie['Name']), 1)
|
||||
self.kodi_db.assignBoxset(setid, movieid)
|
||||
# Update emby reference
|
||||
emby_db.updateParentId(itemid, setid)
|
||||
|
@ -542,7 +541,7 @@ class Movies(Items):
|
|||
# Process removals from boxset
|
||||
for movie in process:
|
||||
movieid = current[movie]
|
||||
self.logMsg("Remove from boxset %s: %s" % (title, movieid))
|
||||
log("Remove from boxset %s: %s" % (title, movieid))
|
||||
self.kodi_db.removefromBoxset(movieid)
|
||||
# Update emby reference
|
||||
emby_db.updateParentId(movie, None)
|
||||
|
@ -567,9 +566,7 @@ class Movies(Items):
|
|||
try:
|
||||
movieid = emby_dbitem[0]
|
||||
fileid = emby_dbitem[1]
|
||||
self.logMsg(
|
||||
"Update playstate for movie: %s fileid: %s"
|
||||
% (item['Name'], fileid), 1)
|
||||
log("Update playstate for movie: %s fileid: %s" % (item['Name'], fileid), 1)
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
|
@ -585,7 +582,7 @@ class Movies(Items):
|
|||
resume = API.adjustResume(userdata['Resume'])
|
||||
total = round(float(runtime), 6)
|
||||
|
||||
self.logMsg("%s New resume point: %s" % (itemid, resume))
|
||||
log("%s New resume point: %s" % (itemid, resume))
|
||||
|
||||
self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
|
@ -601,7 +598,7 @@ class Movies(Items):
|
|||
kodiid = emby_dbitem[0]
|
||||
fileid = emby_dbitem[1]
|
||||
mediatype = emby_dbitem[4]
|
||||
self.logMsg("Removing %sid: %s fileid: %s" % (mediatype, kodiid, fileid), 1)
|
||||
log("Removing %sid: %s fileid: %s" % (mediatype, kodiid, fileid), 1)
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
|
@ -627,7 +624,7 @@ class Movies(Items):
|
|||
|
||||
kodicursor.execute("DELETE FROM sets WHERE idSet = ?", (kodiid,))
|
||||
|
||||
self.logMsg("Deleted %s %s from kodi database" % (mediatype, itemid), 1)
|
||||
log("Deleted %s %s from kodi database" % (mediatype, itemid), 1)
|
||||
|
||||
class MusicVideos(Items):
|
||||
|
||||
|
@ -667,11 +664,11 @@ class MusicVideos(Items):
|
|||
mvideoid = emby_dbitem[0]
|
||||
fileid = emby_dbitem[1]
|
||||
pathid = emby_dbitem[2]
|
||||
self.logMsg("mvideoid: %s fileid: %s pathid: %s" % (mvideoid, fileid, pathid), 1)
|
||||
log("mvideoid: %s fileid: %s pathid: %s" % (mvideoid, fileid, pathid), 1)
|
||||
|
||||
except TypeError:
|
||||
update_item = False
|
||||
self.logMsg("mvideoid: %s not found." % itemid, 2)
|
||||
log("mvideoid: %s not found." % itemid, 2)
|
||||
# mvideoid
|
||||
kodicursor.execute("select coalesce(max(idMVideo),0) from musicvideo")
|
||||
mvideoid = kodicursor.fetchone()[0] + 1
|
||||
|
@ -685,12 +682,12 @@ class MusicVideos(Items):
|
|||
except TypeError:
|
||||
# item is not found, let's recreate it.
|
||||
update_item = False
|
||||
self.logMsg("mvideoid: %s missing from Kodi, repairing the entry." % mvideoid, 1)
|
||||
log("mvideoid: %s missing from Kodi, repairing the entry." % mvideoid, 1)
|
||||
|
||||
if not viewtag or not viewid:
|
||||
# Get view tag from emby
|
||||
viewtag, viewid, mediatype = self.emby.getView_embyId(itemid)
|
||||
self.logMsg("View tag found: %s" % viewtag, 2)
|
||||
log("View tag found: %s" % viewtag, 2)
|
||||
|
||||
# fileId information
|
||||
checksum = API.getChecksum()
|
||||
|
@ -726,22 +723,11 @@ class MusicVideos(Items):
|
|||
|
||||
if self.directpath:
|
||||
# Direct paths is set the Kodi way
|
||||
if utils.window('emby_pathverified') != "true" and not xbmcvfs.exists(playurl):
|
||||
# Validate the path is correct with user intervention
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
heading="Can't validate path",
|
||||
line1=(
|
||||
"Kodi can't locate file: %s. Verify the path. "
|
||||
"You may to verify your network credentials in the "
|
||||
"add-on settings or use the emby path substitution "
|
||||
"to format your path correctly. Stop syncing?"
|
||||
% playurl))
|
||||
if resp:
|
||||
utils.window('emby_shouldStop', value="true")
|
||||
if not self.pathValidation(playurl):
|
||||
return False
|
||||
|
||||
path = playurl.replace(filename, "")
|
||||
utils.window('emby_pathverified', value="true")
|
||||
window('emby_pathverified', value="true")
|
||||
else:
|
||||
# Set plugin path and media flags using real filename
|
||||
path = "plugin://plugin.video.emby.musicvideos/"
|
||||
|
@ -757,7 +743,7 @@ class MusicVideos(Items):
|
|||
|
||||
##### UPDATE THE MUSIC VIDEO #####
|
||||
if update_item:
|
||||
self.logMsg("UPDATE mvideo itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
log("UPDATE mvideo itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
|
||||
# Update path
|
||||
query = "UPDATE path SET strPath = ? WHERE idPath = ?"
|
||||
|
@ -783,7 +769,7 @@ class MusicVideos(Items):
|
|||
|
||||
##### OR ADD THE MUSIC VIDEO #####
|
||||
else:
|
||||
self.logMsg("ADD mvideo itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
log("ADD mvideo itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
|
||||
# Add path
|
||||
query = ' '.join((
|
||||
|
@ -883,7 +869,7 @@ class MusicVideos(Items):
|
|||
try:
|
||||
mvideoid = emby_dbitem[0]
|
||||
fileid = emby_dbitem[1]
|
||||
self.logMsg(
|
||||
log(
|
||||
"Update playstate for musicvideo: %s fileid: %s"
|
||||
% (item['Name'], fileid), 1)
|
||||
except TypeError:
|
||||
|
@ -915,7 +901,7 @@ class MusicVideos(Items):
|
|||
mvideoid = emby_dbitem[0]
|
||||
fileid = emby_dbitem[1]
|
||||
pathid = emby_dbitem[2]
|
||||
self.logMsg("Removing mvideoid: %s fileid: %s" % (mvideoid, fileid, pathid), 1)
|
||||
log("Removing mvideoid: %s fileid: %s" % (mvideoid, fileid, pathid), 1)
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
|
@ -941,7 +927,7 @@ class MusicVideos(Items):
|
|||
kodicursor.execute("DELETE FROM path WHERE idPath = ?", (pathid,))
|
||||
self.embycursor.execute("DELETE FROM emby WHERE emby_id = ?", (itemid,))
|
||||
|
||||
self.logMsg("Deleted musicvideo %s from kodi database" % itemid, 1)
|
||||
log("Deleted musicvideo %s from kodi database" % itemid, 1)
|
||||
|
||||
class TVShows(Items):
|
||||
|
||||
|
@ -1004,8 +990,8 @@ class TVShows(Items):
|
|||
artwork = self.artwork
|
||||
API = api.API(item)
|
||||
|
||||
if utils.settings('syncEmptyShows') == "false" and not item['RecursiveItemCount']:
|
||||
self.logMsg("Skipping empty show: %s" % item['Name'], 1)
|
||||
if settings('syncEmptyShows') == "false" and not item.get('RecursiveItemCount'):
|
||||
log("Skipping empty show: %s" % item['Name'], 1)
|
||||
return
|
||||
# If the item already exist in the local Kodi DB we'll perform a full item update
|
||||
# If the item doesn't exist, we'll add it to the database
|
||||
|
@ -1016,11 +1002,11 @@ class TVShows(Items):
|
|||
try:
|
||||
showid = emby_dbitem[0]
|
||||
pathid = emby_dbitem[2]
|
||||
self.logMsg("showid: %s pathid: %s" % (showid, pathid), 1)
|
||||
log("showid: %s pathid: %s" % (showid, pathid), 1)
|
||||
|
||||
except TypeError:
|
||||
update_item = False
|
||||
self.logMsg("showid: %s not found." % itemid, 2)
|
||||
log("showid: %s not found." % itemid, 2)
|
||||
kodicursor.execute("select coalesce(max(idShow),0) from tvshow")
|
||||
showid = kodicursor.fetchone()[0] + 1
|
||||
|
||||
|
@ -1033,7 +1019,7 @@ class TVShows(Items):
|
|||
except TypeError:
|
||||
# item is not found, let's recreate it.
|
||||
update_item = False
|
||||
self.logMsg("showid: %s missing from Kodi, repairing the entry." % showid, 1)
|
||||
log("showid: %s missing from Kodi, repairing the entry." % showid, 1)
|
||||
# Force re-add episodes after the show is re-created.
|
||||
force_episodes = True
|
||||
|
||||
|
@ -1041,7 +1027,7 @@ class TVShows(Items):
|
|||
if viewtag is None or viewid is None:
|
||||
# Get view tag from emby
|
||||
viewtag, viewid, mediatype = emby.getView_embyId(itemid)
|
||||
self.logMsg("View tag found: %s" % viewtag, 2)
|
||||
log("View tag found: %s" % viewtag, 2)
|
||||
|
||||
# fileId information
|
||||
checksum = API.getChecksum()
|
||||
|
@ -1078,21 +1064,10 @@ class TVShows(Items):
|
|||
path = "%s/" % playurl
|
||||
toplevelpath = "%s/" % dirname(dirname(path))
|
||||
|
||||
if utils.window('emby_pathverified') != "true" and not xbmcvfs.exists(path):
|
||||
# Validate the path is correct with user intervention
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
heading="Can't validate path",
|
||||
line1=(
|
||||
"Kodi can't locate file: %s. Verify the path. "
|
||||
"You may to verify your network credentials in the "
|
||||
"add-on settings or use the emby path substitution "
|
||||
"to format your path correctly. Stop syncing?"
|
||||
% playurl))
|
||||
if resp:
|
||||
utils.window('emby_shouldStop', value="true")
|
||||
if not self.pathValidation(playurl):
|
||||
return False
|
||||
|
||||
utils.window('emby_pathverified', value="true")
|
||||
window('emby_pathverified', value="true")
|
||||
else:
|
||||
# Set plugin path
|
||||
toplevelpath = "plugin://plugin.video.emby.tvshows/"
|
||||
|
@ -1101,7 +1076,7 @@ class TVShows(Items):
|
|||
|
||||
##### UPDATE THE TVSHOW #####
|
||||
if update_item:
|
||||
self.logMsg("UPDATE tvshow itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
log("UPDATE tvshow itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
|
||||
# Update the tvshow entry
|
||||
query = ' '.join((
|
||||
|
@ -1119,7 +1094,7 @@ class TVShows(Items):
|
|||
|
||||
##### OR ADD THE TVSHOW #####
|
||||
else:
|
||||
self.logMsg("ADD tvshow itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
log("ADD tvshow itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
|
||||
# Add top path
|
||||
toppathid = self.kodi_db.addPath(toplevelpath)
|
||||
|
@ -1190,7 +1165,7 @@ class TVShows(Items):
|
|||
|
||||
if force_episodes:
|
||||
# We needed to recreate the show entry. Re-add episodes now.
|
||||
self.logMsg("Repairing episodes for showid: %s %s" % (showid, title), 1)
|
||||
log("Repairing episodes for showid: %s %s" % (showid, title), 1)
|
||||
all_episodes = emby.getEpisodesbyShow(itemid)
|
||||
self.added_episode(all_episodes['Items'], None)
|
||||
|
||||
|
@ -1239,11 +1214,11 @@ class TVShows(Items):
|
|||
episodeid = emby_dbitem[0]
|
||||
fileid = emby_dbitem[1]
|
||||
pathid = emby_dbitem[2]
|
||||
self.logMsg("episodeid: %s fileid: %s pathid: %s" % (episodeid, fileid, pathid), 1)
|
||||
log("episodeid: %s fileid: %s pathid: %s" % (episodeid, fileid, pathid), 1)
|
||||
|
||||
except TypeError:
|
||||
update_item = False
|
||||
self.logMsg("episodeid: %s not found." % itemid, 2)
|
||||
log("episodeid: %s not found." % itemid, 2)
|
||||
# episodeid
|
||||
kodicursor.execute("select coalesce(max(idEpisode),0) from episode")
|
||||
episodeid = kodicursor.fetchone()[0] + 1
|
||||
|
@ -1257,7 +1232,7 @@ class TVShows(Items):
|
|||
except TypeError:
|
||||
# item is not found, let's recreate it.
|
||||
update_item = False
|
||||
self.logMsg("episodeid: %s missing from Kodi, repairing the entry." % episodeid, 1)
|
||||
log("episodeid: %s missing from Kodi, repairing the entry." % episodeid, 1)
|
||||
|
||||
# fileId information
|
||||
checksum = API.getChecksum()
|
||||
|
@ -1281,10 +1256,9 @@ class TVShows(Items):
|
|||
seriesId = item['SeriesId']
|
||||
except KeyError:
|
||||
# Missing seriesId, skip
|
||||
self.logMsg("Skipping: %s. SeriesId is missing." % itemid, 1)
|
||||
log("Skipping: %s. SeriesId is missing." % itemid, 1)
|
||||
return False
|
||||
|
||||
seriesName = item['SeriesName']
|
||||
season = item.get('ParentIndexNumber')
|
||||
episode = item.get('IndexNumber', -1)
|
||||
|
||||
|
@ -1320,7 +1294,7 @@ class TVShows(Items):
|
|||
try:
|
||||
showid = show[0]
|
||||
except TypeError:
|
||||
self.logMsg("Skipping: %s. Unable to add series: %s." % (itemid, seriesId))
|
||||
log("Skipping: %s. Unable to add series: %s." % (itemid, seriesId))
|
||||
return False
|
||||
|
||||
seasonid = self.kodi_db.addSeason(showid, season)
|
||||
|
@ -1337,22 +1311,11 @@ class TVShows(Items):
|
|||
|
||||
if self.directpath:
|
||||
# Direct paths is set the Kodi way
|
||||
if utils.window('emby_pathverified') != "true" and not xbmcvfs.exists(playurl):
|
||||
# Validate the path is correct with user intervention
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
heading="Can't validate path",
|
||||
line1=(
|
||||
"Kodi can't locate file: %s. Verify the path. "
|
||||
"You may to verify your network credentials in the "
|
||||
"add-on settings or use the emby path substitution "
|
||||
"to format your path correctly. Stop syncing?"
|
||||
% playurl))
|
||||
if resp:
|
||||
utils.window('emby_shouldStop', value="true")
|
||||
if not self.pathValidation(playurl):
|
||||
return False
|
||||
|
||||
path = playurl.replace(filename, "")
|
||||
utils.window('emby_pathverified', value="true")
|
||||
window('emby_pathverified', value="true")
|
||||
else:
|
||||
# Set plugin path and media flags using real filename
|
||||
path = "plugin://plugin.video.emby.tvshows/%s/" % seriesId
|
||||
|
@ -1368,7 +1331,7 @@ class TVShows(Items):
|
|||
|
||||
##### UPDATE THE EPISODE #####
|
||||
if update_item:
|
||||
self.logMsg("UPDATE episode itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
log("UPDATE episode itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
|
||||
# Update the movie entry
|
||||
if self.kodiversion in (16, 17):
|
||||
|
@ -1402,7 +1365,7 @@ class TVShows(Items):
|
|||
|
||||
##### OR ADD THE EPISODE #####
|
||||
else:
|
||||
self.logMsg("ADD episode itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
log("ADD episode itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
|
||||
# Add path
|
||||
pathid = self.kodi_db.addPath(path)
|
||||
|
@ -1505,7 +1468,7 @@ class TVShows(Items):
|
|||
kodiid = emby_dbitem[0]
|
||||
fileid = emby_dbitem[1]
|
||||
mediatype = emby_dbitem[4]
|
||||
self.logMsg(
|
||||
log(
|
||||
"Update playstate for %s: %s fileid: %s"
|
||||
% (mediatype, item['Name'], fileid), 1)
|
||||
except TypeError:
|
||||
|
@ -1524,7 +1487,7 @@ class TVShows(Items):
|
|||
resume = API.adjustResume(userdata['Resume'])
|
||||
total = round(float(runtime), 6)
|
||||
|
||||
self.logMsg("%s New resume point: %s" % (itemid, resume))
|
||||
log("%s New resume point: %s" % (itemid, resume))
|
||||
|
||||
self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
|
||||
if not self.directpath and not resume:
|
||||
|
@ -1562,7 +1525,7 @@ class TVShows(Items):
|
|||
pathid = emby_dbitem[2]
|
||||
parentid = emby_dbitem[3]
|
||||
mediatype = emby_dbitem[4]
|
||||
self.logMsg("Removing %s kodiid: %s fileid: %s" % (mediatype, kodiid, fileid), 1)
|
||||
log("Removing %s kodiid: %s fileid: %s" % (mediatype, kodiid, fileid), 1)
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
|
@ -1652,14 +1615,14 @@ class TVShows(Items):
|
|||
self.removeShow(parentid)
|
||||
emby_db.removeItem_byKodiId(parentid, "tvshow")
|
||||
|
||||
self.logMsg("Deleted %s: %s from kodi database" % (mediatype, itemid), 1)
|
||||
log("Deleted %s: %s from kodi database" % (mediatype, itemid), 1)
|
||||
|
||||
def removeShow(self, kodiid):
|
||||
|
||||
kodicursor = self.kodicursor
|
||||
self.artwork.deleteArtwork(kodiid, "tvshow", kodicursor)
|
||||
kodicursor.execute("DELETE FROM tvshow WHERE idShow = ?", (kodiid,))
|
||||
self.logMsg("Removed tvshow: %s." % kodiid, 2)
|
||||
log("Removed tvshow: %s." % kodiid, 2)
|
||||
|
||||
def removeSeason(self, kodiid):
|
||||
|
||||
|
@ -1667,7 +1630,7 @@ class TVShows(Items):
|
|||
|
||||
self.artwork.deleteArtwork(kodiid, "season", kodicursor)
|
||||
kodicursor.execute("DELETE FROM seasons WHERE idSeason = ?", (kodiid,))
|
||||
self.logMsg("Removed season: %s." % kodiid, 2)
|
||||
log("Removed season: %s." % kodiid, 2)
|
||||
|
||||
def removeEpisode(self, kodiid, fileid):
|
||||
|
||||
|
@ -1676,7 +1639,7 @@ class TVShows(Items):
|
|||
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)
|
||||
log("Removed episode: %s." % kodiid, 2)
|
||||
|
||||
class Music(Items):
|
||||
|
||||
|
@ -1685,12 +1648,12 @@ class Music(Items):
|
|||
|
||||
Items.__init__(self, embycursor, musiccursor)
|
||||
|
||||
self.directstream = utils.settings('streamMusic') == "true"
|
||||
self.enableimportsongrating = utils.settings('enableImportSongRating') == "true"
|
||||
self.enableexportsongrating = utils.settings('enableExportSongRating') == "true"
|
||||
self.enableupdatesongrating = utils.settings('enableUpdateSongRating') == "true"
|
||||
self.userid = utils.window('emby_currUser')
|
||||
self.server = utils.window('emby_server%s' % self.userid)
|
||||
self.directstream = settings('streamMusic') == "true"
|
||||
self.enableimportsongrating = settings('enableImportSongRating') == "true"
|
||||
self.enableexportsongrating = settings('enableExportSongRating') == "true"
|
||||
self.enableupdatesongrating = settings('enableUpdateSongRating') == "true"
|
||||
self.userid = window('emby_currUser')
|
||||
self.server = window('emby_server%s' % self.userid)
|
||||
|
||||
def added(self, items, pdialog):
|
||||
|
||||
|
@ -1750,7 +1713,7 @@ class Music(Items):
|
|||
artistid = emby_dbitem[0]
|
||||
except TypeError:
|
||||
update_item = False
|
||||
self.logMsg("artistid: %s not found." % itemid, 2)
|
||||
log("artistid: %s not found." % itemid, 2)
|
||||
|
||||
##### The artist details #####
|
||||
lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
@ -1777,13 +1740,13 @@ class Music(Items):
|
|||
|
||||
##### UPDATE THE ARTIST #####
|
||||
if update_item:
|
||||
self.logMsg("UPDATE artist itemid: %s - Name: %s" % (itemid, name), 1)
|
||||
log("UPDATE artist itemid: %s - Name: %s" % (itemid, name), 1)
|
||||
# Update the checksum in emby table
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
|
||||
##### OR ADD THE ARTIST #####
|
||||
else:
|
||||
self.logMsg("ADD artist itemid: %s - Name: %s" % (itemid, name), 1)
|
||||
log("ADD artist itemid: %s - Name: %s" % (itemid, name), 1)
|
||||
# safety checks: It looks like Emby supports the same artist multiple times.
|
||||
# Kodi doesn't allow that. In case that happens we just merge the artist entries.
|
||||
artistid = self.kodi_db.addArtist(name, musicBrainzId)
|
||||
|
@ -1831,7 +1794,7 @@ class Music(Items):
|
|||
albumid = emby_dbitem[0]
|
||||
except TypeError:
|
||||
update_item = False
|
||||
self.logMsg("albumid: %s not found." % itemid, 2)
|
||||
log("albumid: %s not found." % itemid, 2)
|
||||
|
||||
##### The album details #####
|
||||
lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
@ -1862,13 +1825,13 @@ class Music(Items):
|
|||
|
||||
##### UPDATE THE ALBUM #####
|
||||
if update_item:
|
||||
self.logMsg("UPDATE album itemid: %s - Name: %s" % (itemid, name), 1)
|
||||
log("UPDATE album itemid: %s - Name: %s" % (itemid, name), 1)
|
||||
# Update the checksum in emby table
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
|
||||
##### OR ADD THE ALBUM #####
|
||||
else:
|
||||
self.logMsg("ADD album itemid: %s - Name: %s" % (itemid, name), 1)
|
||||
log("ADD album itemid: %s - Name: %s" % (itemid, name), 1)
|
||||
# safety checks: It looks like Emby supports the same artist multiple times.
|
||||
# Kodi doesn't allow that. In case that happens we just merge the artist entries.
|
||||
albumid = self.kodi_db.addAlbum(name, musicBrainzId)
|
||||
|
@ -2001,7 +1964,7 @@ class Music(Items):
|
|||
albumid = emby_dbitem[3]
|
||||
except TypeError:
|
||||
update_item = False
|
||||
self.logMsg("songid: %s not found." % itemid, 2)
|
||||
log("songid: %s not found." % itemid, 2)
|
||||
|
||||
##### The song details #####
|
||||
checksum = API.getChecksum()
|
||||
|
@ -2048,27 +2011,15 @@ class Music(Items):
|
|||
filename = playurl.rsplit("/", 1)[1]
|
||||
|
||||
# Direct paths is set the Kodi way
|
||||
if utils.window('emby_pathverified') != "true" and not xbmcvfs.exists(playurl):
|
||||
# Validate the path is correct with user intervention
|
||||
utils.window('emby_directPath', clear=True)
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
heading="Can't validate path",
|
||||
line1=(
|
||||
"Kodi can't locate file: %s. Verify the path. "
|
||||
"You may to verify your network credentials in the "
|
||||
"add-on settings or use the emby path substitution "
|
||||
"to format your path correctly. Stop syncing?"
|
||||
% playurl))
|
||||
if resp:
|
||||
utils.window('emby_shouldStop', value="true")
|
||||
if not self.pathValidation(playurl):
|
||||
return False
|
||||
|
||||
path = playurl.replace(filename, "")
|
||||
utils.window('emby_pathverified', value="true")
|
||||
window('emby_pathverified', value="true")
|
||||
|
||||
##### UPDATE THE SONG #####
|
||||
if update_item:
|
||||
self.logMsg("UPDATE song itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
log("UPDATE song itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
|
||||
# Update path
|
||||
query = "UPDATE path SET strPath = ? WHERE idPath = ?"
|
||||
|
@ -2091,7 +2042,7 @@ class Music(Items):
|
|||
|
||||
##### OR ADD THE SONG #####
|
||||
else:
|
||||
self.logMsg("ADD song itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
log("ADD song itemid: %s - Title: %s" % (itemid, title), 1)
|
||||
|
||||
# Add path
|
||||
pathid = self.kodi_db.addPath(path)
|
||||
|
@ -2104,27 +2055,27 @@ class Music(Items):
|
|||
# Verify if there's an album associated.
|
||||
album_name = item.get('Album')
|
||||
if album_name:
|
||||
self.logMsg("Creating virtual music album for song: %s." % itemid, 1)
|
||||
log("Creating virtual music album for song: %s." % itemid, 1)
|
||||
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.
|
||||
self.logMsg("Song itemid: %s has no albumId associated." % itemid, 1)
|
||||
log("Song itemid: %s has no albumId associated." % itemid, 1)
|
||||
return False
|
||||
|
||||
except TypeError:
|
||||
# No album found. Let's create it
|
||||
self.logMsg("Album database entry missing.", 1)
|
||||
log("Album database entry missing.", 1)
|
||||
emby_albumId = item['AlbumId']
|
||||
album = emby.getItem(emby_albumId)
|
||||
self.add_updateAlbum(album)
|
||||
emby_dbalbum = emby_db.getItem_byId(emby_albumId)
|
||||
try:
|
||||
albumid = emby_dbalbum[0]
|
||||
self.logMsg("Found albumid: %s" % albumid, 1)
|
||||
log("Found albumid: %s" % albumid, 1)
|
||||
except TypeError:
|
||||
# No album found, create a single's album
|
||||
self.logMsg("Failed to add album. Creating singles.", 1)
|
||||
log("Failed to add album. Creating singles.", 1)
|
||||
kodicursor.execute("select coalesce(max(idAlbum),0) from album")
|
||||
albumid = kodicursor.fetchone()[0] + 1
|
||||
if self.kodiversion == 16:
|
||||
|
@ -2306,7 +2257,7 @@ class Music(Items):
|
|||
try:
|
||||
kodiid = emby_dbitem[0]
|
||||
mediatype = emby_dbitem[4]
|
||||
self.logMsg("Update playstate for %s: %s" % (mediatype, item['Name']), 1)
|
||||
log("Update playstate for %s: %s" % (mediatype, item['Name']), 1)
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
|
@ -2314,8 +2265,8 @@ class Music(Items):
|
|||
|
||||
#should we ignore this item ?
|
||||
#happens when userdata updated by ratings method
|
||||
if utils.window("ignore-update-%s" %itemid):
|
||||
utils.window("ignore-update-%s" %itemid,clear=True)
|
||||
if window("ignore-update-%s" %itemid):
|
||||
window("ignore-update-%s" %itemid,clear=True)
|
||||
return
|
||||
|
||||
# Process playstates
|
||||
|
@ -2345,7 +2296,7 @@ class Music(Items):
|
|||
try:
|
||||
kodiid = emby_dbitem[0]
|
||||
mediatype = emby_dbitem[4]
|
||||
self.logMsg("Removing %s kodiid: %s" % (mediatype, kodiid), 1)
|
||||
log("Removing %s kodiid: %s" % (mediatype, kodiid), 1)
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
|
@ -2413,21 +2364,21 @@ class Music(Items):
|
|||
# Remove artist
|
||||
self.removeArtist(kodiid)
|
||||
|
||||
self.logMsg("Deleted %s: %s from kodi database" % (mediatype, itemid), 1)
|
||||
log("Deleted %s: %s from kodi database" % (mediatype, itemid), 1)
|
||||
|
||||
def removeSong(self, kodiid):
|
||||
def removeSong(self, kodiId):
|
||||
|
||||
kodicursor = self.kodicursor
|
||||
|
||||
self.artwork.deleteArtwork(kodiid, "song", self.kodicursor)
|
||||
self.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):
|
||||
def removeAlbum(self, kodiId):
|
||||
|
||||
self.artwork.deleteArtwork(kodiid, "album", self.kodicursor)
|
||||
self.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):
|
||||
def removeArtist(self, kodiId):
|
||||
|
||||
self.artwork.deleteArtwork(kodiid, "artist", self.kodicursor)
|
||||
self.kodicursor.execute("DELETE FROM artist WHERE idArtist = ?", (kodiid,))
|
||||
self.artwork.deleteArtwork(kodiId, "artist", self.kodicursor)
|
||||
self.kodicursor.execute("DELETE FROM artist WHERE idArtist = ?", (kodiId,))
|
|
@ -7,7 +7,7 @@ import xbmc
|
|||
import api
|
||||
import artwork
|
||||
import clientinfo
|
||||
import utils
|
||||
from utils import Logging
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
@ -19,17 +19,15 @@ class Kodidb_Functions():
|
|||
|
||||
def __init__(self, cursor):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.cursor = cursor
|
||||
|
||||
self.clientInfo = clientinfo.ClientInfo()
|
||||
self.addonName = self.clientInfo.getAddonName()
|
||||
self.artwork = artwork.Artwork()
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
|
||||
def addPath(self, path):
|
||||
|
||||
|
@ -153,7 +151,7 @@ class Kodidb_Functions():
|
|||
|
||||
query = "INSERT INTO country(country_id, name) values(?, ?)"
|
||||
self.cursor.execute(query, (country_id, country))
|
||||
self.logMsg("Add country to media, processing: %s" % country, 2)
|
||||
log("Add country to media, processing: %s" % country, 2)
|
||||
|
||||
finally: # Assign country to content
|
||||
query = (
|
||||
|
@ -187,7 +185,7 @@ class Kodidb_Functions():
|
|||
|
||||
query = "INSERT INTO country(idCountry, strCountry) values(?, ?)"
|
||||
self.cursor.execute(query, (idCountry, country))
|
||||
self.logMsg("Add country to media, processing: %s" % country, 2)
|
||||
log("Add country to media, processing: %s" % country, 2)
|
||||
|
||||
finally:
|
||||
# Only movies have a country field
|
||||
|
@ -232,7 +230,7 @@ class Kodidb_Functions():
|
|||
|
||||
query = "INSERT INTO actor(actor_id, name) values(?, ?)"
|
||||
self.cursor.execute(query, (actorid, name))
|
||||
self.logMsg("Add people to media, processing: %s" % name, 2)
|
||||
log("Add people to media, processing: %s" % name, 2)
|
||||
|
||||
finally:
|
||||
# Link person to content
|
||||
|
@ -302,7 +300,7 @@ class Kodidb_Functions():
|
|||
|
||||
query = "INSERT INTO actors(idActor, strActor) values(?, ?)"
|
||||
self.cursor.execute(query, (actorid, name))
|
||||
self.logMsg("Add people to media, processing: %s" % name, 2)
|
||||
log("Add people to media, processing: %s" % name, 2)
|
||||
|
||||
finally:
|
||||
# Link person to content
|
||||
|
@ -462,7 +460,7 @@ class Kodidb_Functions():
|
|||
|
||||
query = "INSERT INTO genre(genre_id, name) values(?, ?)"
|
||||
self.cursor.execute(query, (genre_id, genre))
|
||||
self.logMsg("Add Genres to media, processing: %s" % genre, 2)
|
||||
log("Add Genres to media, processing: %s" % genre, 2)
|
||||
|
||||
finally:
|
||||
# Assign genre to item
|
||||
|
@ -507,7 +505,7 @@ class Kodidb_Functions():
|
|||
|
||||
query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)"
|
||||
self.cursor.execute(query, (idGenre, genre))
|
||||
self.logMsg("Add Genres to media, processing: %s" % genre, 2)
|
||||
log("Add Genres to media, processing: %s" % genre, 2)
|
||||
|
||||
finally:
|
||||
# Assign genre to item
|
||||
|
@ -566,7 +564,7 @@ class Kodidb_Functions():
|
|||
|
||||
query = "INSERT INTO studio(studio_id, name) values(?, ?)"
|
||||
self.cursor.execute(query, (studioid, studio))
|
||||
self.logMsg("Add Studios to media, processing: %s" % studio, 2)
|
||||
log("Add Studios to media, processing: %s" % studio, 2)
|
||||
|
||||
finally: # Assign studio to item
|
||||
query = (
|
||||
|
@ -597,7 +595,7 @@ class Kodidb_Functions():
|
|||
|
||||
query = "INSERT INTO studio(idstudio, strstudio) values(?, ?)"
|
||||
self.cursor.execute(query, (studioid, studio))
|
||||
self.logMsg("Add Studios to media, processing: %s" % studio, 2)
|
||||
log("Add Studios to media, processing: %s" % studio, 2)
|
||||
|
||||
finally: # Assign studio to item
|
||||
if "movie" in mediatype:
|
||||
|
@ -728,7 +726,7 @@ class Kodidb_Functions():
|
|||
self.cursor.execute(query, (kodiid, mediatype))
|
||||
|
||||
# Add tags
|
||||
self.logMsg("Adding Tags: %s" % tags, 2)
|
||||
log("Adding Tags: %s" % tags, 2)
|
||||
for tag in tags:
|
||||
self.addTag(kodiid, tag, mediatype)
|
||||
|
||||
|
@ -750,7 +748,7 @@ class Kodidb_Functions():
|
|||
except TypeError:
|
||||
# Create the tag, because it does not exist
|
||||
tag_id = self.createTag(tag)
|
||||
self.logMsg("Adding tag: %s" % tag, 2)
|
||||
log("Adding tag: %s" % tag, 2)
|
||||
|
||||
finally:
|
||||
# Assign tag to item
|
||||
|
@ -779,7 +777,7 @@ class Kodidb_Functions():
|
|||
except TypeError:
|
||||
# Create the tag
|
||||
tag_id = self.createTag(tag)
|
||||
self.logMsg("Adding tag: %s" % tag, 2)
|
||||
log("Adding tag: %s" % tag, 2)
|
||||
|
||||
finally:
|
||||
# Assign tag to item
|
||||
|
@ -815,7 +813,7 @@ class Kodidb_Functions():
|
|||
|
||||
query = "INSERT INTO tag(tag_id, name) values(?, ?)"
|
||||
self.cursor.execute(query, (tag_id, name))
|
||||
self.logMsg("Create tag_id: %s name: %s" % (tag_id, name), 2)
|
||||
log("Create tag_id: %s name: %s" % (tag_id, name), 2)
|
||||
else:
|
||||
# Kodi Helix
|
||||
query = ' '.join((
|
||||
|
@ -835,13 +833,13 @@ class Kodidb_Functions():
|
|||
|
||||
query = "INSERT INTO tag(idTag, strTag) values(?, ?)"
|
||||
self.cursor.execute(query, (tag_id, name))
|
||||
self.logMsg("Create idTag: %s name: %s" % (tag_id, name), 2)
|
||||
log("Create idTag: %s name: %s" % (tag_id, name), 2)
|
||||
|
||||
return tag_id
|
||||
|
||||
def updateTag(self, oldtag, newtag, kodiid, mediatype):
|
||||
|
||||
self.logMsg("Updating: %s with %s for %s: %s" % (oldtag, newtag, mediatype, kodiid), 2)
|
||||
log("Updating: %s with %s for %s: %s" % (oldtag, newtag, mediatype, kodiid), 2)
|
||||
|
||||
if self.kodiversion in (15, 16, 17):
|
||||
# Kodi Isengard, Jarvis, Krypton
|
||||
|
@ -858,7 +856,7 @@ class Kodidb_Functions():
|
|||
except Exception as e:
|
||||
# The new tag we are going to apply already exists for this item
|
||||
# delete current tag instead
|
||||
self.logMsg("Exception: %s" % e, 1)
|
||||
log("Exception: %s" % e, 1)
|
||||
query = ' '.join((
|
||||
|
||||
"DELETE FROM tag_link",
|
||||
|
@ -882,7 +880,7 @@ class Kodidb_Functions():
|
|||
except Exception as e:
|
||||
# The new tag we are going to apply already exists for this item
|
||||
# delete current tag instead
|
||||
self.logMsg("Exception: %s" % e, 1)
|
||||
log("Exception: %s" % e, 1)
|
||||
query = ' '.join((
|
||||
|
||||
"DELETE FROM taglinks",
|
||||
|
@ -943,7 +941,7 @@ class Kodidb_Functions():
|
|||
|
||||
def createBoxset(self, boxsetname):
|
||||
|
||||
self.logMsg("Adding boxset: %s" % boxsetname, 2)
|
||||
log("Adding boxset: %s" % boxsetname, 2)
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idSet",
|
||||
|
|
|
@ -11,7 +11,7 @@ import clientinfo
|
|||
import downloadutils
|
||||
import embydb_functions as embydb
|
||||
import playbackutils as pbutils
|
||||
import utils
|
||||
from utils import Logging, window, settings, kodiSQL
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -21,27 +21,25 @@ class KodiMonitor(xbmc.Monitor):
|
|||
|
||||
def __init__(self):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.clientInfo = clientinfo.ClientInfo()
|
||||
self.addonName = self.clientInfo.getAddonName()
|
||||
self.doUtils = downloadutils.DownloadUtils()
|
||||
|
||||
self.logMsg("Kodi monitor started.", 1)
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
self.className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
|
||||
log("Kodi monitor started.", 1)
|
||||
|
||||
|
||||
def onScanStarted(self, library):
|
||||
self.logMsg("Kodi library scan %s running." % library, 2)
|
||||
log("Kodi library scan %s running." % library, 2)
|
||||
if library == "video":
|
||||
utils.window('emby_kodiScan', value="true")
|
||||
window('emby_kodiScan', value="true")
|
||||
|
||||
def onScanFinished(self, library):
|
||||
self.logMsg("Kodi library scan %s finished." % library, 2)
|
||||
log("Kodi library scan %s finished." % library, 2)
|
||||
if library == "video":
|
||||
utils.window('emby_kodiScan', clear=True)
|
||||
window('emby_kodiScan', clear=True)
|
||||
|
||||
def onSettingsChanged(self):
|
||||
# Monitor emby settings
|
||||
|
@ -50,7 +48,7 @@ class KodiMonitor(xbmc.Monitor):
|
|||
'''currentPath = utils.settings('useDirectPaths')
|
||||
if utils.window('emby_pluginpath') != currentPath:
|
||||
# Plugin path value changed. Offer to reset
|
||||
self.logMsg("Changed to playback mode detected", 1)
|
||||
log("Changed to playback mode detected", 1)
|
||||
utils.window('emby_pluginpath', value=currentPath)
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
heading="Playback mode change detected",
|
||||
|
@ -61,17 +59,17 @@ class KodiMonitor(xbmc.Monitor):
|
|||
if resp:
|
||||
utils.reset()'''
|
||||
|
||||
currentLog = utils.settings('logLevel')
|
||||
if utils.window('emby_logLevel') != currentLog:
|
||||
currentLog = settings('logLevel')
|
||||
if window('emby_logLevel') != currentLog:
|
||||
# The log level changed, set new prop
|
||||
self.logMsg("New log level: %s" % currentLog, 1)
|
||||
utils.window('emby_logLevel', value=currentLog)
|
||||
log("New log level: %s" % currentLog, 1)
|
||||
window('emby_logLevel', value=currentLog)
|
||||
|
||||
def onNotification(self, sender, method, data):
|
||||
|
||||
doUtils = self.doUtils
|
||||
if method not in ("Playlist.OnAdd"):
|
||||
self.logMsg("Method: %s Data: %s" % (method, data), 1)
|
||||
log("Method: %s Data: %s" % (method, data), 1)
|
||||
|
||||
if data:
|
||||
data = json.loads(data,'utf-8')
|
||||
|
@ -84,23 +82,23 @@ class KodiMonitor(xbmc.Monitor):
|
|||
kodiid = item['id']
|
||||
item_type = item['type']
|
||||
except (KeyError, TypeError):
|
||||
self.logMsg("Item is invalid for playstate update.", 1)
|
||||
log("Item is invalid for playstate update.", 1)
|
||||
else:
|
||||
if ((utils.settings('useDirectPaths') == "1" and not item_type == "song") or
|
||||
(item_type == "song" and utils.settings('enableMusic') == "true")):
|
||||
if ((settings('useDirectPaths') == "1" and not item_type == "song") or
|
||||
(item_type == "song" and settings('enableMusic') == "true")):
|
||||
# Set up properties for player
|
||||
embyconn = utils.kodiSQL('emby')
|
||||
embyconn = kodiSQL('emby')
|
||||
embycursor = embyconn.cursor()
|
||||
emby_db = embydb.Embydb_Functions(embycursor)
|
||||
emby_dbitem = emby_db.getItem_byKodiId(kodiid, item_type)
|
||||
try:
|
||||
itemid = emby_dbitem[0]
|
||||
except TypeError:
|
||||
self.logMsg("No kodiid returned.", 1)
|
||||
log("No kodiId returned.", 1)
|
||||
else:
|
||||
url = "{server}/emby/Users/{UserId}/Items/%s?format=json" % itemid
|
||||
result = doUtils.downloadUrl(url)
|
||||
self.logMsg("Item: %s" % result, 2)
|
||||
log("Item: %s" % result, 2)
|
||||
|
||||
playurl = None
|
||||
count = 0
|
||||
|
@ -114,12 +112,10 @@ class KodiMonitor(xbmc.Monitor):
|
|||
listItem = xbmcgui.ListItem()
|
||||
playback = pbutils.PlaybackUtils(result)
|
||||
|
||||
if item_type == "song" and utils.settings('streamMusic') == "true":
|
||||
utils.window('emby_%s.playmethod' % playurl,
|
||||
value="DirectStream")
|
||||
if item_type == "song" and settings('streamMusic') == "true":
|
||||
window('emby_%s.playmethod' % playurl, value="DirectStream")
|
||||
else:
|
||||
utils.window('emby_%s.playmethod' % playurl,
|
||||
value="DirectPlay")
|
||||
window('emby_%s.playmethod' % playurl, value="DirectPlay")
|
||||
# Set properties for player.py
|
||||
playback.setProperties(playurl, listItem)
|
||||
finally:
|
||||
|
@ -134,31 +130,31 @@ class KodiMonitor(xbmc.Monitor):
|
|||
kodiid = item['id']
|
||||
item_type = item['type']
|
||||
except (KeyError, TypeError):
|
||||
self.logMsg("Item is invalid for playstate update.", 1)
|
||||
log("Item is invalid for playstate update.", 1)
|
||||
else:
|
||||
# Send notification to the server.
|
||||
embyconn = utils.kodiSQL('emby')
|
||||
embyconn = kodiSQL('emby')
|
||||
embycursor = embyconn.cursor()
|
||||
emby_db = embydb.Embydb_Functions(embycursor)
|
||||
emby_dbitem = emby_db.getItem_byKodiId(kodiid, item_type)
|
||||
try:
|
||||
itemid = emby_dbitem[0]
|
||||
except TypeError:
|
||||
self.logMsg("Could not find itemid in emby database.", 1)
|
||||
log("Could not find itemid in emby database.", 1)
|
||||
else:
|
||||
# Stop from manually marking as watched unwatched, with actual playback.
|
||||
if utils.window('emby_skipWatched%s' % itemid) == "true":
|
||||
if window('emby_skipWatched%s' % itemid) == "true":
|
||||
# property is set in player.py
|
||||
utils.window('emby_skipWatched%s' % itemid, clear=True)
|
||||
window('emby_skipWatched%s' % itemid, clear=True)
|
||||
else:
|
||||
# notify the server
|
||||
url = "{server}/emby/Users/{UserId}/PlayedItems/%s?format=json" % itemid
|
||||
if playcount != 0:
|
||||
doUtils.downloadUrl(url, action_type="POST")
|
||||
self.logMsg("Mark as watched for itemid: %s" % itemid, 1)
|
||||
log("Mark as watched for itemid: %s" % itemid, 1)
|
||||
else:
|
||||
doUtils.downloadUrl(url, action_type="DELETE")
|
||||
self.logMsg("Mark as unwatched for itemid: %s" % itemid, 1)
|
||||
log("Mark as unwatched for itemid: %s" % itemid, 1)
|
||||
finally:
|
||||
embycursor.close()
|
||||
|
||||
|
@ -172,7 +168,7 @@ class KodiMonitor(xbmc.Monitor):
|
|||
kodiid = data['id']
|
||||
type = data['type']
|
||||
except (KeyError, TypeError):
|
||||
self.logMsg("Item is invalid for emby deletion.", 1)
|
||||
log("Item is invalid for emby deletion.", 1)
|
||||
else:
|
||||
# Send the delete action to the server.
|
||||
embyconn = utils.kodiSQL('emby')
|
||||
|
@ -182,19 +178,19 @@ class KodiMonitor(xbmc.Monitor):
|
|||
try:
|
||||
itemid = emby_dbitem[0]
|
||||
except TypeError:
|
||||
self.logMsg("Could not find itemid in emby database.", 1)
|
||||
log("Could not find itemid in emby database.", 1)
|
||||
else:
|
||||
if utils.settings('skipContextMenu') != "true":
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
heading="Confirm delete",
|
||||
line1="Delete file on Emby Server?")
|
||||
if not resp:
|
||||
self.logMsg("User skipped deletion.", 1)
|
||||
log("User skipped deletion.", 1)
|
||||
embycursor.close()
|
||||
return
|
||||
|
||||
url = "{server}/emby/Items/%s?format=json" % itemid
|
||||
self.logMsg("Deleting request: %s" % itemid)
|
||||
log("Deleting request: %s" % itemid)
|
||||
doUtils.downloadUrl(url, action_type="DELETE")
|
||||
finally:
|
||||
embycursor.close()'''
|
||||
|
@ -203,13 +199,13 @@ class KodiMonitor(xbmc.Monitor):
|
|||
elif method == "System.OnWake":
|
||||
# Allow network to wake up
|
||||
xbmc.sleep(10000)
|
||||
utils.window('emby_onWake', value="true")
|
||||
window('emby_onWake', value="true")
|
||||
|
||||
|
||||
elif method == "GUI.OnScreensaverDeactivated":
|
||||
if utils.settings('dbSyncScreensaver') == "true":
|
||||
if settings('dbSyncScreensaver') == "true":
|
||||
xbmc.sleep(5000);
|
||||
utils.window('emby_onWake', value="true")
|
||||
window('emby_onWake', value="true")
|
||||
|
||||
|
||||
elif method == "Playlist.OnClear":
|
||||
|
|
|
@ -20,6 +20,7 @@ import kodidb_functions as kodidb
|
|||
import read_embyserver as embyserver
|
||||
import userclient
|
||||
import videonodes
|
||||
from utils import Logging, window, settings, language as lang
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
@ -42,6 +43,9 @@ class LibrarySync(threading.Thread):
|
|||
|
||||
def __init__(self):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.__dict__ = self._shared_state
|
||||
self.monitor = xbmc.Monitor()
|
||||
|
||||
|
@ -54,26 +58,20 @@ class LibrarySync(threading.Thread):
|
|||
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
|
||||
def progressDialog(self, title, forced=False):
|
||||
|
||||
dialog = None
|
||||
|
||||
if utils.settings('dbSyncIndicator') == "true" or forced:
|
||||
if settings('dbSyncIndicator') == "true" or forced:
|
||||
dialog = xbmcgui.DialogProgressBG()
|
||||
dialog.create("Emby for Kodi", title)
|
||||
self.logMsg("Show progress dialog: %s" % title, 2)
|
||||
log("Show progress dialog: %s" % title, 2)
|
||||
|
||||
return dialog
|
||||
|
||||
def startSync(self):
|
||||
|
||||
settings = utils.settings
|
||||
# Run at start up - optional to use the server plugin
|
||||
if settings('SyncInstallRunDone') == "true":
|
||||
|
||||
|
@ -88,7 +86,7 @@ class LibrarySync(threading.Thread):
|
|||
|
||||
for plugin in result:
|
||||
if plugin['Name'] == "Emby.Kodi Sync Queue":
|
||||
self.logMsg("Found server plugin.", 2)
|
||||
log("Found server plugin.", 2)
|
||||
completed = self.fastSync()
|
||||
break
|
||||
|
||||
|
@ -103,37 +101,31 @@ class LibrarySync(threading.Thread):
|
|||
|
||||
def fastSync(self):
|
||||
|
||||
lastSync = utils.settings('LastIncrementalSync')
|
||||
lastSync = settings('LastIncrementalSync')
|
||||
if not lastSync:
|
||||
lastSync = "2010-01-01T00:00:00Z"
|
||||
|
||||
lastSyncTime = utils.convertdate(lastSync)
|
||||
self.logMsg("Last sync run: %s" % lastSyncTime, 1)
|
||||
lastSyncTime = utils.convertDate(lastSync)
|
||||
log("Last sync run: %s" % lastSyncTime, 1)
|
||||
|
||||
# get server RetentionDateTime
|
||||
result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/GetServerDateTime?format=json")
|
||||
retention_time = "2010-01-01T00:00:00Z"
|
||||
if result and result.get('RetentionDateTime'):
|
||||
retention_time = result['RetentionDateTime']
|
||||
|
||||
#Try/except equivalent
|
||||
'''
|
||||
try:
|
||||
retention_time = result['RetentionDateTime']
|
||||
except (TypeError, KeyError):
|
||||
retention_time = "2010-01-01T00:00:00Z"
|
||||
'''
|
||||
|
||||
retention_time = utils.convertdate(retention_time)
|
||||
self.logMsg("RetentionDateTime: %s" % retention_time, 1)
|
||||
retention_time = utils.convertDate(retention_time)
|
||||
log("RetentionDateTime: %s" % retention_time, 1)
|
||||
|
||||
# if last sync before retention time do a full sync
|
||||
if retention_time > lastSyncTime:
|
||||
self.logMsg("Fast sync server retention insufficient, fall back to full sync", 1)
|
||||
log("Fast sync server retention insufficient, fall back to full sync", 1)
|
||||
return False
|
||||
|
||||
params = {'LastUpdateDT': lastSync}
|
||||
result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/{UserId}/GetItems?format=json", parameters=params)
|
||||
url = "{server}/emby/Emby.Kodi.SyncQueue/{UserId}/GetItems?format=json"
|
||||
result = self.doUtils(url, parameters=params)
|
||||
|
||||
try:
|
||||
processlist = {
|
||||
|
@ -145,11 +137,11 @@ class LibrarySync(threading.Thread):
|
|||
}
|
||||
|
||||
except (KeyError, TypeError):
|
||||
self.logMsg("Failed to retrieve latest updates using fast sync.", 1)
|
||||
log("Failed to retrieve latest updates using fast sync.", 1)
|
||||
return False
|
||||
|
||||
else:
|
||||
self.logMsg("Fast sync changes: %s" % result, 1)
|
||||
log("Fast sync changes: %s" % result, 1)
|
||||
for action in processlist:
|
||||
self.triage_items(action, processlist[action])
|
||||
|
||||
|
@ -163,60 +155,55 @@ class LibrarySync(threading.Thread):
|
|||
result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/GetServerDateTime?format=json")
|
||||
try: # datetime fails when used more than once, TypeError
|
||||
server_time = result['ServerDateTime']
|
||||
server_time = utils.convertdate(server_time)
|
||||
server_time = utils.convertDate(server_time)
|
||||
|
||||
except Exception as e:
|
||||
# If the server plugin is not installed or an error happened.
|
||||
self.logMsg("An exception occurred: %s" % e, 1)
|
||||
log("An exception occurred: %s" % e, 1)
|
||||
time_now = datetime.utcnow()-timedelta(minutes=overlap)
|
||||
lastSync = time_now.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
self.logMsg("New sync time: client time -%s min: %s" % (overlap, lastSync), 1)
|
||||
log("New sync time: client time -%s min: %s" % (overlap, lastSync), 1)
|
||||
|
||||
else:
|
||||
lastSync = (server_time - timedelta(minutes=overlap)).strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
self.logMsg("New sync time: server time -%s min: %s" % (overlap, lastSync), 1)
|
||||
log("New sync time: server time -%s min: %s" % (overlap, lastSync), 1)
|
||||
|
||||
finally:
|
||||
utils.settings('LastIncrementalSync', value=lastSync)
|
||||
settings('LastIncrementalSync', value=lastSync)
|
||||
|
||||
def shouldStop(self):
|
||||
# Checkpoint during the syncing process
|
||||
if self.monitor.abortRequested():
|
||||
return True
|
||||
elif utils.window('emby_shouldStop') == "true":
|
||||
elif window('emby_shouldStop') == "true":
|
||||
return True
|
||||
else: # Keep going
|
||||
return False
|
||||
|
||||
def dbCommit(self, connection):
|
||||
|
||||
window = utils.window
|
||||
# Central commit, verifies if Kodi database update is running
|
||||
kodidb_scan = window('emby_kodiScan') == "true"
|
||||
|
||||
while kodidb_scan:
|
||||
|
||||
self.logMsg("Kodi scan is running. Waiting...", 1)
|
||||
log("Kodi scan is running. Waiting...", 1)
|
||||
kodidb_scan = window('emby_kodiScan') == "true"
|
||||
|
||||
if self.shouldStop():
|
||||
self.logMsg("Commit unsuccessful. Sync terminated.", 1)
|
||||
log("Commit unsuccessful. Sync terminated.", 1)
|
||||
break
|
||||
|
||||
if self.monitor.waitForAbort(1):
|
||||
# Abort was requested while waiting. We should exit
|
||||
self.logMsg("Commit unsuccessful.", 1)
|
||||
log("Commit unsuccessful.", 1)
|
||||
break
|
||||
else:
|
||||
connection.commit()
|
||||
self.logMsg("Commit successful.", 1)
|
||||
log("Commit successful.", 1)
|
||||
|
||||
def fullSync(self, manualrun=False, repair=False, forceddialog=False):
|
||||
|
||||
window = utils.window
|
||||
settings = utils.settings
|
||||
# Only run once when first setting up. Can be run manually.
|
||||
music_enabled = utils.settings('enableMusic') == "true"
|
||||
music_enabled = settings('enableMusic') == "true"
|
||||
|
||||
xbmc.executebuiltin('InhibitIdleShutdown(true)')
|
||||
screensaver = utils.getScreensaver()
|
||||
|
@ -284,7 +271,7 @@ class LibrarySync(threading.Thread):
|
|||
self.dbCommit(kodiconn)
|
||||
embyconn.commit()
|
||||
elapsedTime = datetime.now() - startTime
|
||||
self.logMsg("SyncDatabase (finished %s in: %s)"
|
||||
log("SyncDatabase (finished %s in: %s)"
|
||||
% (itemtype, str(elapsedTime).split('.')[0]), 1)
|
||||
else:
|
||||
# Close the Kodi cursor
|
||||
|
@ -312,7 +299,7 @@ class LibrarySync(threading.Thread):
|
|||
musicconn.commit()
|
||||
embyconn.commit()
|
||||
elapsedTime = datetime.now() - startTime
|
||||
self.logMsg("SyncDatabase (finished music in: %s)"
|
||||
log("SyncDatabase (finished music in: %s)"
|
||||
% (str(elapsedTime).split('.')[0]), 1)
|
||||
musiccursor.close()
|
||||
|
||||
|
@ -333,9 +320,9 @@ class LibrarySync(threading.Thread):
|
|||
window('emby_initialScan', clear=True)
|
||||
if forceddialog:
|
||||
xbmcgui.Dialog().notification(
|
||||
heading="Emby for Kodi",
|
||||
heading=lang(29999),
|
||||
message="%s %s %s" %
|
||||
(message, utils.language(33025), str(elapsedtotal).split('.')[0]),
|
||||
(message, lang(33025), str(elapsedtotal).split('.')[0]),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
sound=False)
|
||||
return True
|
||||
|
@ -378,7 +365,7 @@ class LibrarySync(threading.Thread):
|
|||
if view['type'] == "mixed":
|
||||
sorted_views.append(view['name'])
|
||||
sorted_views.append(view['name'])
|
||||
self.logMsg("Sorted views: %s" % sorted_views, 1)
|
||||
log("Sorted views: %s" % sorted_views, 1)
|
||||
|
||||
# total nodes for window properties
|
||||
self.vnodes.clearProperties()
|
||||
|
@ -415,7 +402,8 @@ class LibrarySync(threading.Thread):
|
|||
'Limit': 1,
|
||||
'IncludeItemTypes': emby_mediatypes[mediatype]
|
||||
} # Get one item from server using the folderid
|
||||
result = self.doUtils("{server}/emby/Users/{UserId}/Items?format=json", parameters=params)
|
||||
url = "{server}/emby/Users/{UserId}/Items?format=json"
|
||||
result = self.doUtils(url, parameters=params)
|
||||
try:
|
||||
verifyitem = result['Items'][0]['Id']
|
||||
except (TypeError, IndexError):
|
||||
|
@ -430,14 +418,14 @@ class LibrarySync(threading.Thread):
|
|||
# Take the userview, and validate the item belong to the view
|
||||
if self.emby.verifyView(grouped_view['Id'], verifyitem):
|
||||
# Take the name of the userview
|
||||
self.logMsg("Found corresponding view: %s %s"
|
||||
log("Found corresponding view: %s %s"
|
||||
% (grouped_view['Name'], grouped_view['Id']), 1)
|
||||
foldername = grouped_view['Name']
|
||||
break
|
||||
else:
|
||||
# Unable to find a match, add the name to our sorted_view list
|
||||
sorted_views.append(foldername)
|
||||
self.logMsg("Couldn't find corresponding grouped view: %s" % sorted_views, 1)
|
||||
log("Couldn't find corresponding grouped view: %s" % sorted_views, 1)
|
||||
|
||||
# Failsafe
|
||||
try:
|
||||
|
@ -453,7 +441,7 @@ class LibrarySync(threading.Thread):
|
|||
current_tagid = view[2]
|
||||
|
||||
except TypeError:
|
||||
self.logMsg("Creating viewid: %s in Emby database." % folderid, 1)
|
||||
log("Creating viewid: %s in Emby database." % folderid, 1)
|
||||
tagid = kodi_db.createTag(foldername)
|
||||
# Create playlist for the video library
|
||||
if (foldername not in playlists and
|
||||
|
@ -472,7 +460,7 @@ class LibrarySync(threading.Thread):
|
|||
emby_db.addView(folderid, foldername, viewtype, tagid)
|
||||
|
||||
else:
|
||||
self.logMsg(' '.join((
|
||||
log(' '.join((
|
||||
|
||||
"Found viewid: %s" % folderid,
|
||||
"viewname: %s" % current_viewname,
|
||||
|
@ -488,7 +476,7 @@ class LibrarySync(threading.Thread):
|
|||
|
||||
# View was modified, update with latest info
|
||||
if current_viewname != foldername:
|
||||
self.logMsg("viewid: %s new viewname: %s" % (folderid, foldername), 1)
|
||||
log("viewid: %s new viewname: %s" % (folderid, foldername), 1)
|
||||
tagid = kodi_db.createTag(foldername)
|
||||
|
||||
# Update view with new info
|
||||
|
@ -553,23 +541,22 @@ class LibrarySync(threading.Thread):
|
|||
self.vnodes.singleNode(totalnodes, "channels", "movies", "channels")
|
||||
totalnodes += 1
|
||||
# Save total
|
||||
utils.window('Emby.nodes.total', str(totalnodes))
|
||||
window('Emby.nodes.total', str(totalnodes))
|
||||
|
||||
# Remove any old referenced views
|
||||
self.logMsg("Removing views: %s" % current_views, 1)
|
||||
log("Removing views: %s" % current_views, 1)
|
||||
for view in current_views:
|
||||
emby_db.removeView(view)
|
||||
|
||||
def movies(self, embycursor, kodicursor, pdialog):
|
||||
|
||||
lang = utils.language
|
||||
# Get movies from emby
|
||||
emby_db = embydb.Embydb_Functions(embycursor)
|
||||
movies = itemtypes.Movies(embycursor, kodicursor)
|
||||
|
||||
views = emby_db.getView_byType('movies')
|
||||
views += emby_db.getView_byType('mixed')
|
||||
self.logMsg("Media folders: %s" % views, 1)
|
||||
log("Media folders: %s" % views, 1)
|
||||
|
||||
##### PROCESS MOVIES #####
|
||||
for view in views:
|
||||
|
@ -580,7 +567,7 @@ class LibrarySync(threading.Thread):
|
|||
# Get items per view
|
||||
if pdialog:
|
||||
pdialog.update(
|
||||
heading="Emby for Kodi",
|
||||
heading=lang(29999),
|
||||
message="%s %s..." % (lang(33017), view['name']))
|
||||
|
||||
# Initial or repair sync
|
||||
|
@ -604,12 +591,12 @@ class LibrarySync(threading.Thread):
|
|||
count += 1
|
||||
movies.add_update(embymovie, view['name'], view['id'])
|
||||
else:
|
||||
self.logMsg("Movies finished.", 2)
|
||||
log("Movies finished.", 2)
|
||||
|
||||
|
||||
##### PROCESS BOXSETS #####
|
||||
if pdialog:
|
||||
pdialog.update(heading="Emby for Kodi", message=lang(33018))
|
||||
pdialog.update(heading=lang(29999), message=lang(33018))
|
||||
|
||||
boxsets = self.emby.getBoxset(dialog=pdialog)
|
||||
total = boxsets['TotalRecordCount']
|
||||
|
@ -631,7 +618,7 @@ class LibrarySync(threading.Thread):
|
|||
count += 1
|
||||
movies.add_updateBoxset(boxset)
|
||||
else:
|
||||
self.logMsg("Boxsets finished.", 2)
|
||||
log("Boxsets finished.", 2)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -642,7 +629,7 @@ class LibrarySync(threading.Thread):
|
|||
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
|
||||
|
||||
views = emby_db.getView_byType('musicvideos')
|
||||
self.logMsg("Media folders: %s" % views, 1)
|
||||
log("Media folders: %s" % views, 1)
|
||||
|
||||
for view in views:
|
||||
|
||||
|
@ -655,8 +642,8 @@ class LibrarySync(threading.Thread):
|
|||
|
||||
if pdialog:
|
||||
pdialog.update(
|
||||
heading="Emby for Kodi",
|
||||
message="%s %s..." % (utils.language(33019), viewName))
|
||||
heading=lang(29999),
|
||||
message="%s %s..." % (lang(33019), viewName))
|
||||
|
||||
# Initial or repair sync
|
||||
all_embymvideos = self.emby.getMusicVideos(viewId, dialog=pdialog)
|
||||
|
@ -679,7 +666,7 @@ class LibrarySync(threading.Thread):
|
|||
count += 1
|
||||
mvideos.add_update(embymvideo, viewName, viewId)
|
||||
else:
|
||||
self.logMsg("MusicVideos finished.", 2)
|
||||
log("MusicVideos finished.", 2)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -691,7 +678,7 @@ class LibrarySync(threading.Thread):
|
|||
|
||||
views = emby_db.getView_byType('tvshows')
|
||||
views += emby_db.getView_byType('mixed')
|
||||
self.logMsg("Media folders: %s" % views, 1)
|
||||
log("Media folders: %s" % views, 1)
|
||||
|
||||
for view in views:
|
||||
|
||||
|
@ -701,8 +688,8 @@ class LibrarySync(threading.Thread):
|
|||
# Get items per view
|
||||
if pdialog:
|
||||
pdialog.update(
|
||||
heading="Emby for Kodi",
|
||||
message="%s %s..." % (utils.language(33020), view['name']))
|
||||
heading=lang(29999),
|
||||
message="%s %s..." % (lang(33020), view['name']))
|
||||
|
||||
all_embytvshows = self.emby.getShows(view['id'], dialog=pdialog)
|
||||
total = all_embytvshows['TotalRecordCount']
|
||||
|
@ -737,7 +724,7 @@ class LibrarySync(threading.Thread):
|
|||
pdialog.update(percentage, message="%s - %s" % (title, episodetitle))
|
||||
tvshows.add_updateEpisode(episode)
|
||||
else:
|
||||
self.logMsg("TVShows finished.", 2)
|
||||
log("TVShows finished.", 2)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -756,8 +743,8 @@ class LibrarySync(threading.Thread):
|
|||
|
||||
if pdialog:
|
||||
pdialog.update(
|
||||
heading="Emby for Kodi",
|
||||
message="%s %s..." % (utils.language(33021), itemtype))
|
||||
heading=lang(29999),
|
||||
message="%s %s..." % (lang(33021), itemtype))
|
||||
|
||||
all_embyitems = process[itemtype][0](dialog=pdialog)
|
||||
total = all_embyitems['TotalRecordCount']
|
||||
|
@ -778,7 +765,7 @@ class LibrarySync(threading.Thread):
|
|||
|
||||
process[itemtype][1](embyitem)
|
||||
else:
|
||||
self.logMsg("%s finished." % itemtype, 2)
|
||||
log("%s finished." % itemtype, 2)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -799,7 +786,7 @@ class LibrarySync(threading.Thread):
|
|||
itemids.append(item['ItemId'])
|
||||
items = itemids
|
||||
|
||||
self.logMsg("Queue %s: %s" % (process, items), 1)
|
||||
log("Queue %s: %s" % (process, items), 1)
|
||||
processlist[process].extend(items)
|
||||
|
||||
def incrementalSync(self):
|
||||
|
@ -833,7 +820,7 @@ class LibrarySync(threading.Thread):
|
|||
}
|
||||
for process_type in ['added', 'update', 'userdata', 'remove']:
|
||||
|
||||
if process[process_type] and utils.window('emby_kodiScan') != "true":
|
||||
if process[process_type] and window('emby_kodiScan') != "true":
|
||||
|
||||
listItems = list(process[process_type])
|
||||
del process[process_type][:] # Reset class list
|
||||
|
@ -871,7 +858,7 @@ class LibrarySync(threading.Thread):
|
|||
|
||||
if update_embydb:
|
||||
update_embydb = False
|
||||
self.logMsg("Updating emby database.", 1)
|
||||
log("Updating emby database.", 1)
|
||||
embyconn.commit()
|
||||
self.saveLastSync()
|
||||
|
||||
|
@ -880,8 +867,8 @@ class LibrarySync(threading.Thread):
|
|||
self.forceLibraryUpdate = False
|
||||
self.dbCommit(kodiconn)
|
||||
|
||||
self.logMsg("Updating video library.", 1)
|
||||
utils.window('emby_kodiScan', value="true")
|
||||
log("Updating video library.", 1)
|
||||
window('emby_kodiScan', value="true")
|
||||
xbmc.executebuiltin('UpdateLibrary(video)')
|
||||
|
||||
if pDialog:
|
||||
|
@ -893,7 +880,7 @@ class LibrarySync(threading.Thread):
|
|||
|
||||
def compareDBVersion(self, current, minimum):
|
||||
# It returns True is database is up to date. False otherwise.
|
||||
self.logMsg("current: %s minimum: %s" % (current, minimum), 1)
|
||||
log("current: %s minimum: %s" % (current, minimum), 1)
|
||||
currMajor, currMinor, currPatch = current.split(".")
|
||||
minMajor, minMinor, minPatch = minimum.split(".")
|
||||
|
||||
|
@ -911,25 +898,22 @@ class LibrarySync(threading.Thread):
|
|||
try:
|
||||
self.run_internal()
|
||||
except Exception as e:
|
||||
utils.window('emby_dbScan', clear=True)
|
||||
window('emby_dbScan', clear=True)
|
||||
xbmcgui.Dialog().ok(
|
||||
heading="Emby for Kodi",
|
||||
heading=lang(29999),
|
||||
line1=(
|
||||
"Library sync thread has exited! "
|
||||
"You should restart Kodi now. "
|
||||
"Please report this on the forum."))
|
||||
raise
|
||||
|
||||
@utils.profiling()
|
||||
def run_internal(self):
|
||||
|
||||
lang = utils.language
|
||||
window = utils.window
|
||||
settings = utils.settings
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
startupComplete = False
|
||||
|
||||
self.logMsg("---===### Starting LibrarySync ###===---", 0)
|
||||
log("---===### Starting LibrarySync ###===---", 0)
|
||||
|
||||
while not self.monitor.abortRequested():
|
||||
|
||||
|
@ -947,13 +931,13 @@ class LibrarySync(threading.Thread):
|
|||
uptoDate = self.compareDBVersion(currentVersion, minVersion)
|
||||
|
||||
if not uptoDate:
|
||||
self.logMsg("Database version out of date: %s minimum version required: %s"
|
||||
log("Database version out of date: %s minimum version required: %s"
|
||||
% (currentVersion, minVersion), 0)
|
||||
|
||||
resp = dialog.yesno("Emby for Kodi", lang(33022))
|
||||
resp = dialog.yesno(lang(29999), lang(33022))
|
||||
if not resp:
|
||||
self.logMsg("Database version is out of date! USER IGNORED!", 0)
|
||||
dialog.ok("Emby for Kodi", lang(33023))
|
||||
log("Database version is out of date! USER IGNORED!", 0)
|
||||
dialog.ok(lang(29999), lang(33023))
|
||||
else:
|
||||
utils.reset()
|
||||
|
||||
|
@ -967,24 +951,24 @@ class LibrarySync(threading.Thread):
|
|||
videoDb = utils.getKodiVideoDBPath()
|
||||
if not xbmcvfs.exists(videoDb):
|
||||
# Database does not exists
|
||||
self.logMsg(
|
||||
log(
|
||||
"The current Kodi version is incompatible "
|
||||
"with the Emby for Kodi add-on. Please visit "
|
||||
"https://github.com/MediaBrowser/Emby.Kodi/wiki "
|
||||
"to know which Kodi versions are supported.", 0)
|
||||
|
||||
dialog.ok(
|
||||
heading="Emby for Kodi",
|
||||
heading=lang(29999),
|
||||
line1=lang(33024))
|
||||
break
|
||||
|
||||
# Run start up sync
|
||||
self.logMsg("Database version: %s" % settings('dbCreatedWithVersion'), 0)
|
||||
self.logMsg("SyncDatabase (started)", 1)
|
||||
log("Database version: %s" % settings('dbCreatedWithVersion'), 0)
|
||||
log("SyncDatabase (started)", 1)
|
||||
startTime = datetime.now()
|
||||
librarySync = self.startSync()
|
||||
elapsedTime = datetime.now() - startTime
|
||||
self.logMsg("SyncDatabase (finished in: %s) %s"
|
||||
log("SyncDatabase (finished in: %s) %s"
|
||||
% (str(elapsedTime).split('.')[0], librarySync), 1)
|
||||
# Only try the initial sync once per kodi session regardless
|
||||
# This will prevent an infinite loop in case something goes wrong.
|
||||
|
@ -999,32 +983,32 @@ class LibrarySync(threading.Thread):
|
|||
# Set in kodimonitor.py
|
||||
window('emby_onWake', clear=True)
|
||||
if window('emby_syncRunning') != "true":
|
||||
self.logMsg("SyncDatabase onWake (started)", 0)
|
||||
log("SyncDatabase onWake (started)", 0)
|
||||
librarySync = self.startSync()
|
||||
self.logMsg("SyncDatabase onWake (finished) %s" % librarySync, 0)
|
||||
log("SyncDatabase onWake (finished) %s" % librarySync, 0)
|
||||
|
||||
if self.stop_thread:
|
||||
# Set in service.py
|
||||
self.logMsg("Service terminated thread.", 2)
|
||||
log("Service terminated thread.", 2)
|
||||
break
|
||||
|
||||
if self.monitor.waitForAbort(1):
|
||||
# Abort was requested while waiting. We should exit
|
||||
break
|
||||
|
||||
self.logMsg("###===--- LibrarySync Stopped ---===###", 0)
|
||||
log("###===--- LibrarySync Stopped ---===###", 0)
|
||||
|
||||
def stopThread(self):
|
||||
self.stop_thread = True
|
||||
self.logMsg("Ending thread...", 2)
|
||||
log("Ending thread...", 2)
|
||||
|
||||
def suspendThread(self):
|
||||
self.suspend_thread = True
|
||||
self.logMsg("Pausing thread...", 0)
|
||||
log("Pausing thread...", 0)
|
||||
|
||||
def resumeThread(self):
|
||||
self.suspend_thread = False
|
||||
self.logMsg("Resuming thread...", 0)
|
||||
log("Resuming thread...", 0)
|
||||
|
||||
|
||||
class ManualSync(LibrarySync):
|
||||
|
@ -1041,14 +1025,13 @@ class ManualSync(LibrarySync):
|
|||
|
||||
def movies(self, embycursor, kodicursor, pdialog):
|
||||
|
||||
lang = utils.language
|
||||
# Get movies from emby
|
||||
emby_db = embydb.Embydb_Functions(embycursor)
|
||||
movies = itemtypes.Movies(embycursor, kodicursor)
|
||||
|
||||
views = emby_db.getView_byType('movies')
|
||||
views += emby_db.getView_byType('mixed')
|
||||
self.logMsg("Media folders: %s" % views, 1)
|
||||
log("Media folders: %s" % views, 1)
|
||||
|
||||
# Pull the list of movies and boxsets in Kodi
|
||||
try:
|
||||
|
@ -1077,7 +1060,7 @@ class ManualSync(LibrarySync):
|
|||
|
||||
if pdialog:
|
||||
pdialog.update(
|
||||
heading="Emby for Kodi",
|
||||
heading=lang(29999),
|
||||
message="%s %s..." % (lang(33026), viewName))
|
||||
|
||||
all_embymovies = self.emby.getMovies(viewId, basic=True, dialog=pdialog)
|
||||
|
@ -1095,7 +1078,7 @@ class ManualSync(LibrarySync):
|
|||
# Only update if movie is not in Kodi or checksum is different
|
||||
updatelist.append(itemid)
|
||||
|
||||
self.logMsg("Movies to update for %s: %s" % (viewName, updatelist), 1)
|
||||
log("Movies to update for %s: %s" % (viewName, updatelist), 1)
|
||||
embymovies = self.emby.getFullItems(updatelist)
|
||||
total = len(updatelist)
|
||||
del updatelist[:]
|
||||
|
@ -1121,7 +1104,7 @@ class ManualSync(LibrarySync):
|
|||
embyboxsets = []
|
||||
|
||||
if pdialog:
|
||||
pdialog.update(heading="Emby for Kodi", message=lang(33027))
|
||||
pdialog.update(heading=lang(29999), message=lang(33027))
|
||||
|
||||
for boxset in boxsets['Items']:
|
||||
|
||||
|
@ -1137,7 +1120,7 @@ class ManualSync(LibrarySync):
|
|||
updatelist.append(itemid)
|
||||
embyboxsets.append(boxset)
|
||||
|
||||
self.logMsg("Boxsets to update: %s" % updatelist, 1)
|
||||
log("Boxsets to update: %s" % updatelist, 1)
|
||||
total = len(updatelist)
|
||||
|
||||
if pdialog:
|
||||
|
@ -1161,13 +1144,13 @@ class ManualSync(LibrarySync):
|
|||
if kodimovie not in all_embymoviesIds:
|
||||
movies.remove(kodimovie)
|
||||
else:
|
||||
self.logMsg("Movies compare finished.", 1)
|
||||
log("Movies compare finished.", 1)
|
||||
|
||||
for boxset in all_kodisets:
|
||||
if boxset not in all_embyboxsetsIds:
|
||||
movies.remove(boxset)
|
||||
else:
|
||||
self.logMsg("Boxsets compare finished.", 1)
|
||||
log("Boxsets compare finished.", 1)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -1178,7 +1161,7 @@ class ManualSync(LibrarySync):
|
|||
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
|
||||
|
||||
views = emby_db.getView_byType('musicvideos')
|
||||
self.logMsg("Media folders: %s" % views, 1)
|
||||
log("Media folders: %s" % views, 1)
|
||||
|
||||
# Pull the list of musicvideos in Kodi
|
||||
try:
|
||||
|
@ -1200,8 +1183,8 @@ class ManualSync(LibrarySync):
|
|||
|
||||
if pdialog:
|
||||
pdialog.update(
|
||||
heading="Emby for Kodi",
|
||||
message="%s %s..." % (utils.language(33028), viewName))
|
||||
heading=lang(29999),
|
||||
message="%s %s..." % (lang(33028), viewName))
|
||||
|
||||
all_embymvideos = self.emby.getMusicVideos(viewId, basic=True, dialog=pdialog)
|
||||
for embymvideo in all_embymvideos['Items']:
|
||||
|
@ -1218,7 +1201,7 @@ class ManualSync(LibrarySync):
|
|||
# Only update if musicvideo is not in Kodi or checksum is different
|
||||
updatelist.append(itemid)
|
||||
|
||||
self.logMsg("MusicVideos to update for %s: %s" % (viewName, updatelist), 1)
|
||||
log("MusicVideos to update for %s: %s" % (viewName, updatelist), 1)
|
||||
embymvideos = self.emby.getFullItems(updatelist)
|
||||
total = len(updatelist)
|
||||
del updatelist[:]
|
||||
|
@ -1245,20 +1228,19 @@ class ManualSync(LibrarySync):
|
|||
if kodimvideo not in all_embymvideosIds:
|
||||
mvideos.remove(kodimvideo)
|
||||
else:
|
||||
self.logMsg("MusicVideos compare finished.", 1)
|
||||
log("MusicVideos compare finished.", 1)
|
||||
|
||||
return True
|
||||
|
||||
def tvshows(self, embycursor, kodicursor, pdialog):
|
||||
|
||||
lang = utils.language
|
||||
# Get shows from emby
|
||||
emby_db = embydb.Embydb_Functions(embycursor)
|
||||
tvshows = itemtypes.TVShows(embycursor, kodicursor)
|
||||
|
||||
views = emby_db.getView_byType('tvshows')
|
||||
views += emby_db.getView_byType('mixed')
|
||||
self.logMsg("Media folders: %s" % views, 1)
|
||||
log("Media folders: %s" % views, 1)
|
||||
|
||||
# Pull the list of tvshows and episodes in Kodi
|
||||
try:
|
||||
|
@ -1287,7 +1269,7 @@ class ManualSync(LibrarySync):
|
|||
|
||||
if pdialog:
|
||||
pdialog.update(
|
||||
heading="Emby for Kodi",
|
||||
heading=lang(29999),
|
||||
message="%s %s..." % (lang(33029), viewName))
|
||||
|
||||
all_embytvshows = self.emby.getShows(viewId, basic=True, dialog=pdialog)
|
||||
|
@ -1305,7 +1287,7 @@ class ManualSync(LibrarySync):
|
|||
# Only update if movie is not in Kodi or checksum is different
|
||||
updatelist.append(itemid)
|
||||
|
||||
self.logMsg("TVShows to update for %s: %s" % (viewName, updatelist), 1)
|
||||
log("TVShows to update for %s: %s" % (viewName, updatelist), 1)
|
||||
embytvshows = self.emby.getFullItems(updatelist)
|
||||
total = len(updatelist)
|
||||
del updatelist[:]
|
||||
|
@ -1332,7 +1314,7 @@ class ManualSync(LibrarySync):
|
|||
# Get all episodes in view
|
||||
if pdialog:
|
||||
pdialog.update(
|
||||
heading="Emby for Kodi",
|
||||
heading=lang(29999),
|
||||
message="%s %s..." % (lang(33030), viewName))
|
||||
|
||||
all_embyepisodes = self.emby.getEpisodes(viewId, basic=True, dialog=pdialog)
|
||||
|
@ -1349,7 +1331,7 @@ class ManualSync(LibrarySync):
|
|||
# Only update if movie is not in Kodi or checksum is different
|
||||
updatelist.append(itemid)
|
||||
|
||||
self.logMsg("Episodes to update for %s: %s" % (viewName, updatelist), 1)
|
||||
log("Episodes to update for %s: %s" % (viewName, updatelist), 1)
|
||||
embyepisodes = self.emby.getFullItems(updatelist)
|
||||
total = len(updatelist)
|
||||
del updatelist[:]
|
||||
|
@ -1363,7 +1345,8 @@ class ManualSync(LibrarySync):
|
|||
|
||||
if pdialog:
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
pdialog.update(percentage, message="%s - %s" % (episode['SeriesName'], episode['Name']))
|
||||
title = "%s - %s" % (episode.get('SeriesName', "Unknown"), episode['Name'])
|
||||
pdialog.update(percentage, message=title)
|
||||
count += 1
|
||||
tvshows.add_updateEpisode(episode)
|
||||
|
||||
|
@ -1373,13 +1356,13 @@ class ManualSync(LibrarySync):
|
|||
if koditvshow not in all_embytvshowsIds:
|
||||
tvshows.remove(koditvshow)
|
||||
else:
|
||||
self.logMsg("TVShows compare finished.", 1)
|
||||
log("TVShows compare finished.", 1)
|
||||
|
||||
for kodiepisode in all_kodiepisodes:
|
||||
if kodiepisode not in all_embyepisodesIds:
|
||||
tvshows.remove(kodiepisode)
|
||||
else:
|
||||
self.logMsg("Episodes compare finished.", 1)
|
||||
log("Episodes compare finished.", 1)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -1419,8 +1402,8 @@ class ManualSync(LibrarySync):
|
|||
for data_type in ['artists', 'albums', 'songs']:
|
||||
if pdialog:
|
||||
pdialog.update(
|
||||
heading="Emby for Kodi",
|
||||
message="%s %s..." % (utils.language(33031), data_type))
|
||||
heading=lang(29999),
|
||||
message="%s %s..." % (lang(33031), data_type))
|
||||
if data_type != "artists":
|
||||
all_embyitems = process[data_type][0](basic=True, dialog=pdialog)
|
||||
else:
|
||||
|
@ -1445,7 +1428,7 @@ class ManualSync(LibrarySync):
|
|||
if all_kodisongs.get(itemid) != API.getChecksum():
|
||||
# Only update if songs is not in Kodi or checksum is different
|
||||
updatelist.append(itemid)
|
||||
self.logMsg("%s to update: %s" % (data_type, updatelist), 1)
|
||||
log("%s to update: %s" % (data_type, updatelist), 1)
|
||||
embyitems = self.emby.getFullItems(updatelist)
|
||||
total = len(updatelist)
|
||||
del updatelist[:]
|
||||
|
@ -1466,15 +1449,15 @@ class ManualSync(LibrarySync):
|
|||
if kodiartist not in all_embyartistsIds and all_kodiartists[kodiartist] is not None:
|
||||
music.remove(kodiartist)
|
||||
else:
|
||||
self.logMsg("Artist compare finished.", 1)
|
||||
log("Artist compare finished.", 1)
|
||||
for kodialbum in all_kodialbums:
|
||||
if kodialbum not in all_embyalbumsIds:
|
||||
music.remove(kodialbum)
|
||||
else:
|
||||
self.logMsg("Albums compare finished.", 1)
|
||||
log("Albums compare finished.", 1)
|
||||
for kodisong in all_kodisongs:
|
||||
if kodisong not in all_embysongsIds:
|
||||
music.remove(kodisong)
|
||||
else:
|
||||
self.logMsg("Songs compare finished.", 1)
|
||||
log("Songs compare finished.", 1)
|
||||
return True
|
|
@ -14,20 +14,18 @@ from mutagen import id3
|
|||
import base64
|
||||
|
||||
import read_embyserver as embyserver
|
||||
import utils
|
||||
from utils import Logging, window
|
||||
log = Logging('MusicTools').log
|
||||
|
||||
#################################################################################################
|
||||
|
||||
# Helper for the music library, intended to fix missing song ID3 tags on Emby
|
||||
|
||||
def logMsg(msg, lvl=1):
|
||||
utils.logMsg("%s %s" % ("Emby", "musictools"), msg, lvl)
|
||||
|
||||
def getRealFileName(filename, isTemp=False):
|
||||
#get the filename path accessible by python if possible...
|
||||
|
||||
if not xbmcvfs.exists(filename):
|
||||
logMsg( "File does not exist! %s" %(filename), 0)
|
||||
log("File does not exist! %s" % filename, 0)
|
||||
return (False, "")
|
||||
|
||||
#if we use os.path method on older python versions (sunch as some android builds), we need to pass arguments as string
|
||||
|
@ -104,7 +102,7 @@ def getAdditionalSongTags(embyid, emby_rating, API, kodicursor, emby_db, enablei
|
|||
elif file_rating is None and not currentvalue:
|
||||
return (emby_rating, comment, False)
|
||||
|
||||
logMsg("getAdditionalSongTags --> embyid: %s - emby_rating: %s - file_rating: %s - current rating in kodidb: %s" %(embyid, emby_rating, file_rating, currentvalue))
|
||||
log("getAdditionalSongTags --> embyid: %s - emby_rating: %s - file_rating: %s - current rating in kodidb: %s" %(embyid, emby_rating, file_rating, currentvalue))
|
||||
|
||||
updateFileRating = False
|
||||
updateEmbyRating = False
|
||||
|
@ -171,7 +169,7 @@ def getAdditionalSongTags(embyid, emby_rating, API, kodicursor, emby_db, enablei
|
|||
if updateEmbyRating and enableexportsongrating:
|
||||
# sync details to emby server. Translation needed between ID3 rating and emby likes/favourites:
|
||||
like, favourite, deletelike = getEmbyRatingFromKodiRating(rating)
|
||||
utils.window("ignore-update-%s" %embyid, "true") #set temp windows prop to ignore the update from webclient update
|
||||
window("ignore-update-%s" %embyid, "true") #set temp windows prop to ignore the update from webclient update
|
||||
emby.updateUserRating(embyid, like, favourite, deletelike)
|
||||
|
||||
return (rating, comment, hasEmbeddedCover)
|
||||
|
@ -183,7 +181,7 @@ def getSongTags(file):
|
|||
hasEmbeddedCover = False
|
||||
|
||||
isTemp,filename = getRealFileName(file)
|
||||
logMsg( "getting song ID3 tags for " + filename)
|
||||
log( "getting song ID3 tags for " + filename)
|
||||
|
||||
try:
|
||||
###### FLAC FILES #############
|
||||
|
@ -217,14 +215,14 @@ def getSongTags(file):
|
|||
#POPM rating is 0-255 and needs to be converted to 0-5 range
|
||||
if rating > 5: rating = (rating / 255) * 5
|
||||
else:
|
||||
logMsg( "Not supported fileformat or unable to access file: %s" %(filename))
|
||||
log( "Not supported fileformat or unable to access file: %s" %(filename))
|
||||
|
||||
#the rating must be a round value
|
||||
rating = int(round(rating,0))
|
||||
|
||||
except Exception as e:
|
||||
#file in use ?
|
||||
utils.logMsg("Exception in getSongTags", str(e),0)
|
||||
log("Exception in getSongTags", str(e),0)
|
||||
rating = None
|
||||
|
||||
#remove tempfile if needed....
|
||||
|
@ -246,7 +244,7 @@ def updateRatingToFile(rating, file):
|
|||
xbmcvfs.copy(file, tempfile)
|
||||
tempfile = xbmc.translatePath(tempfile).decode("utf-8")
|
||||
|
||||
logMsg( "setting song rating: %s for filename: %s - using tempfile: %s" %(rating,file,tempfile))
|
||||
log( "setting song rating: %s for filename: %s - using tempfile: %s" %(rating,file,tempfile))
|
||||
|
||||
if not tempfile:
|
||||
return
|
||||
|
@ -263,7 +261,7 @@ def updateRatingToFile(rating, file):
|
|||
audio.add(id3.POPM(email="Windows Media Player 9 Series", rating=calcrating, count=1))
|
||||
audio.save()
|
||||
else:
|
||||
logMsg( "Not supported fileformat: %s" %(tempfile))
|
||||
log( "Not supported fileformat: %s" %(tempfile))
|
||||
|
||||
#once we have succesfully written the flags we move the temp file to destination, otherwise not proceeding and just delete the temp
|
||||
#safety check: we check the file size of the temp file before proceeding with overwite of original file
|
||||
|
@ -274,14 +272,14 @@ def updateRatingToFile(rating, file):
|
|||
xbmcvfs.delete(file)
|
||||
xbmcvfs.copy(tempfile,file)
|
||||
else:
|
||||
logMsg( "Checksum mismatch for filename: %s - using tempfile: %s - not proceeding with file overwite!" %(rating,file,tempfile))
|
||||
log( "Checksum mismatch for filename: %s - using tempfile: %s - not proceeding with file overwite!" %(rating,file,tempfile))
|
||||
|
||||
#always delete the tempfile
|
||||
xbmcvfs.delete(tempfile)
|
||||
|
||||
except Exception as e:
|
||||
#file in use ?
|
||||
logMsg("Exception in updateRatingToFile %s" %e,0)
|
||||
log("Exception in updateRatingToFile %s" %e,0)
|
||||
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ import downloadutils
|
|||
import playutils as putils
|
||||
import playlist
|
||||
import read_embyserver as embyserver
|
||||
import utils
|
||||
from utils import Logging, window, settings, language as lang
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -26,6 +26,9 @@ class PlaybackUtils():
|
|||
|
||||
def __init__(self, item):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.item = item
|
||||
self.API = api.API(self.item)
|
||||
|
||||
|
@ -33,28 +36,20 @@ class PlaybackUtils():
|
|||
self.addonName = self.clientInfo.getAddonName()
|
||||
self.doUtils = downloadutils.DownloadUtils().downloadUrl
|
||||
|
||||
self.userid = utils.window('emby_currUser')
|
||||
self.server = utils.window('emby_server%s' % self.userid)
|
||||
self.userid = window('emby_currUser')
|
||||
self.server = window('emby_server%s' % self.userid)
|
||||
|
||||
self.artwork = artwork.Artwork()
|
||||
self.emby = embyserver.Read_EmbyServer()
|
||||
self.pl = playlist.Playlist()
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
self.className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
|
||||
|
||||
|
||||
def play(self, itemid, dbid=None):
|
||||
|
||||
window = utils.window
|
||||
settings = utils.settings
|
||||
|
||||
listitem = xbmcgui.ListItem()
|
||||
playutils = putils.PlayUtils(self.item)
|
||||
|
||||
self.logMsg("Play called.", 1)
|
||||
log("Play called.", 1)
|
||||
playurl = playutils.getPlayUrl()
|
||||
if not playurl:
|
||||
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
|
||||
|
@ -77,9 +72,9 @@ class PlaybackUtils():
|
|||
introsPlaylist = False
|
||||
dummyPlaylist = False
|
||||
|
||||
self.logMsg("Playlist start position: %s" % startPos, 2)
|
||||
self.logMsg("Playlist plugin position: %s" % currentPosition, 2)
|
||||
self.logMsg("Playlist size: %s" % sizePlaylist, 2)
|
||||
log("Playlist start position: %s" % startPos, 2)
|
||||
log("Playlist plugin position: %s" % currentPosition, 2)
|
||||
log("Playlist size: %s" % sizePlaylist, 2)
|
||||
|
||||
############### RESUME POINT ################
|
||||
|
||||
|
@ -91,12 +86,11 @@ class PlaybackUtils():
|
|||
if not propertiesPlayback:
|
||||
|
||||
window('emby_playbackProps', value="true")
|
||||
self.logMsg("Setting up properties in playlist.", 1)
|
||||
log("Setting up properties in playlist.", 1)
|
||||
|
||||
if (not homeScreen and not seektime and
|
||||
window('emby_customPlaylist') != "true"):
|
||||
if not homeScreen and not seektime and window('emby_customPlaylist') != "true":
|
||||
|
||||
self.logMsg("Adding dummy file to playlist.", 2)
|
||||
log("Adding dummy file to playlist.", 2)
|
||||
dummyPlaylist = True
|
||||
playlist.add(playurl, listitem, index=startPos)
|
||||
# Remove the original item from playlist
|
||||
|
@ -116,18 +110,18 @@ class PlaybackUtils():
|
|||
getTrailers = True
|
||||
|
||||
if settings('askCinema') == "true":
|
||||
resp = xbmcgui.Dialog().yesno("Emby for Kodi", utils.language(33016))
|
||||
resp = xbmcgui.Dialog().yesno("Emby for Kodi", lang(33016))
|
||||
if not resp:
|
||||
# User selected to not play trailers
|
||||
getTrailers = False
|
||||
self.logMsg("Skip trailers.", 1)
|
||||
log("Skip trailers.", 1)
|
||||
|
||||
if getTrailers:
|
||||
for intro in intros['Items']:
|
||||
# The server randomly returns intros, process them.
|
||||
introListItem = xbmcgui.ListItem()
|
||||
introPlayurl = putils.PlayUtils(intro).getPlayUrl()
|
||||
self.logMsg("Adding Intro: %s" % introPlayurl, 1)
|
||||
log("Adding Intro: %s" % introPlayurl, 1)
|
||||
|
||||
# Set listitem and properties for intros
|
||||
pbutils = PlaybackUtils(intro)
|
||||
|
@ -143,7 +137,7 @@ class PlaybackUtils():
|
|||
if homeScreen and not seektime and not sizePlaylist:
|
||||
# Extend our current playlist with the actual item to play
|
||||
# only if there's no playlist first
|
||||
self.logMsg("Adding main item to playlist.", 1)
|
||||
log("Adding main item to playlist.", 1)
|
||||
self.pl.addtoPlaylist(dbid, self.item['Type'].lower())
|
||||
|
||||
# Ensure that additional parts are played after the main item
|
||||
|
@ -160,7 +154,7 @@ class PlaybackUtils():
|
|||
|
||||
additionalListItem = xbmcgui.ListItem()
|
||||
additionalPlayurl = putils.PlayUtils(part).getPlayUrl()
|
||||
self.logMsg("Adding additional part: %s" % partcount, 1)
|
||||
log("Adding additional part: %s" % partcount, 1)
|
||||
|
||||
# Set listitem and properties for each additional parts
|
||||
pbutils = PlaybackUtils(part)
|
||||
|
@ -174,13 +168,13 @@ class PlaybackUtils():
|
|||
if dummyPlaylist:
|
||||
# Added a dummy file to the playlist,
|
||||
# because the first item is going to fail automatically.
|
||||
self.logMsg("Processed as a playlist. First item is skipped.", 1)
|
||||
log("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:
|
||||
self.logMsg("Resetting properties playback flag.", 2)
|
||||
log("Resetting properties playback flag.", 2)
|
||||
window('emby_playbackProps', clear=True)
|
||||
|
||||
#self.pl.verifyPlaylist()
|
||||
|
@ -190,7 +184,7 @@ class PlaybackUtils():
|
|||
if window('emby_%s.playmethod' % playurl) == "Transcode":
|
||||
# Filter ISO since Emby does not probe anymore
|
||||
if self.item.get('VideoType') == "Iso":
|
||||
self.logMsg("Skipping audio/subs prompt, ISO detected.", 1)
|
||||
log("Skipping audio/subs prompt, ISO detected.", 1)
|
||||
else:
|
||||
playurl = playutils.audioSubsPref(playurl, listitem)
|
||||
window('emby_%s.playmethod' % playurl, value="Transcode")
|
||||
|
@ -201,23 +195,22 @@ class PlaybackUtils():
|
|||
############### PLAYBACK ################
|
||||
|
||||
if homeScreen and seektime and window('emby_customPlaylist') != "true":
|
||||
self.logMsg("Play as a widget item.", 1)
|
||||
log("Play as a widget item.", 1)
|
||||
self.setListItem(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.
|
||||
self.logMsg("Play playlist.", 1)
|
||||
log("Play playlist.", 1)
|
||||
xbmc.Player().play(playlist, startpos=startPos)
|
||||
|
||||
else:
|
||||
self.logMsg("Play as a regular item.", 1)
|
||||
log("Play as a regular item.", 1)
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
|
||||
|
||||
def setProperties(self, playurl, listitem):
|
||||
|
||||
window = utils.window
|
||||
# Set all properties necessary for plugin path playback
|
||||
itemid = self.item['Id']
|
||||
itemtype = self.item['Type']
|
||||
|
@ -233,7 +226,7 @@ class PlaybackUtils():
|
|||
window('%s.refreshid' % embyitem, value=itemid)
|
||||
|
||||
# Append external subtitles to stream
|
||||
playmethod = utils.window('%s.playmethod' % embyitem)
|
||||
playmethod = window('%s.playmethod' % embyitem)
|
||||
# Only for direct stream
|
||||
if playmethod in ("DirectStream"):
|
||||
# Direct play automatically appends external
|
||||
|
@ -272,7 +265,7 @@ class PlaybackUtils():
|
|||
kodiindex += 1
|
||||
|
||||
mapping = json.dumps(mapping)
|
||||
utils.window('emby_%s.indexMapping' % playurl, value=mapping)
|
||||
window('emby_%s.indexMapping' % playurl, value=mapping)
|
||||
|
||||
return externalsubs
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@ import json
|
|||
import xbmc
|
||||
import xbmcgui
|
||||
|
||||
import utils
|
||||
import clientinfo
|
||||
import downloadutils
|
||||
import kodidb_functions as kodidb
|
||||
import websocket_client as wsc
|
||||
from utils import Logging, window, settings, language as lang
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -28,6 +28,9 @@ class Player(xbmc.Player):
|
|||
|
||||
def __init__(self):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.__dict__ = self._shared_state
|
||||
|
||||
self.clientInfo = clientinfo.ClientInfo()
|
||||
|
@ -36,20 +39,13 @@ class Player(xbmc.Player):
|
|||
self.ws = wsc.WebSocket_Client()
|
||||
self.xbmcplayer = xbmc.Player()
|
||||
|
||||
self.logMsg("Starting playback monitor.", 2)
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
self.className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
|
||||
log("Starting playback monitor.", 2)
|
||||
|
||||
|
||||
def GetPlayStats(self):
|
||||
return self.playStats
|
||||
|
||||
def onPlayBackStarted(self):
|
||||
|
||||
window = utils.window
|
||||
# Will be called when xbmc starts playing a file
|
||||
self.stopAll()
|
||||
|
||||
|
@ -67,7 +63,7 @@ class Player(xbmc.Player):
|
|||
except: pass
|
||||
|
||||
if count == 5: # try 5 times
|
||||
self.logMsg("Cancelling playback report...", 1)
|
||||
log("Cancelling playback report...", 1)
|
||||
break
|
||||
else: count += 1
|
||||
|
||||
|
@ -84,12 +80,12 @@ class Player(xbmc.Player):
|
|||
xbmc.sleep(200)
|
||||
itemId = window("emby_%s.itemid" % currentFile)
|
||||
if tryCount == 20: # try 20 times or about 10 seconds
|
||||
self.logMsg("Could not find itemId, cancelling playback report...", 1)
|
||||
log("Could not find itemId, cancelling playback report...", 1)
|
||||
break
|
||||
else: tryCount += 1
|
||||
|
||||
else:
|
||||
self.logMsg("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId), 0)
|
||||
log("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId), 0)
|
||||
|
||||
# Only proceed if an itemId was found.
|
||||
embyitem = "emby_%s" % currentFile
|
||||
|
@ -102,7 +98,7 @@ class Player(xbmc.Player):
|
|||
customseek = window('emby_customPlaylist.seektime')
|
||||
if window('emby_customPlaylist') == "true" and customseek:
|
||||
# Start at, when using custom playlist (play to Kodi from webclient)
|
||||
self.logMsg("Seeking to: %s" % customseek, 1)
|
||||
log("Seeking to: %s" % customseek, 1)
|
||||
self.xbmcplayer.seekTime(int(customseek)/10000000.0)
|
||||
window('emby_customPlaylist.seektime', clear=True)
|
||||
|
||||
|
@ -189,7 +185,7 @@ class Player(xbmc.Player):
|
|||
|
||||
if mapping: # Set in playbackutils.py
|
||||
|
||||
self.logMsg("Mapping for external subtitles index: %s" % mapping, 2)
|
||||
log("Mapping for external subtitles index: %s" % mapping, 2)
|
||||
externalIndex = json.loads(mapping)
|
||||
|
||||
if externalIndex.get(str(indexSubs)):
|
||||
|
@ -207,7 +203,7 @@ class Player(xbmc.Player):
|
|||
|
||||
|
||||
# Post playback to server
|
||||
self.logMsg("Sending POST play started: %s." % postdata, 2)
|
||||
log("Sending POST play started: %s." % postdata, 2)
|
||||
self.doUtils(url, postBody=postdata, action_type="POST")
|
||||
|
||||
# Ensure we do have a runtime
|
||||
|
@ -215,7 +211,7 @@ class Player(xbmc.Player):
|
|||
runtime = int(runtime)
|
||||
except ValueError:
|
||||
runtime = self.xbmcplayer.getTotalTime()
|
||||
self.logMsg("Runtime is missing, Kodi runtime: %s" % runtime, 1)
|
||||
log("Runtime is missing, Kodi runtime: %s" % runtime, 1)
|
||||
|
||||
# Save data map for updates and position calls
|
||||
data = {
|
||||
|
@ -232,7 +228,7 @@ class Player(xbmc.Player):
|
|||
}
|
||||
|
||||
self.played_info[currentFile] = data
|
||||
self.logMsg("ADDING_FILE: %s" % self.played_info, 1)
|
||||
log("ADDING_FILE: %s" % self.played_info, 1)
|
||||
|
||||
# log some playback stats
|
||||
'''if(itemType != None):
|
||||
|
@ -251,7 +247,7 @@ class Player(xbmc.Player):
|
|||
|
||||
def reportPlayback(self):
|
||||
|
||||
self.logMsg("reportPlayback Called", 2)
|
||||
log("reportPlayback Called", 2)
|
||||
|
||||
# Get current file
|
||||
currentFile = self.currentFile
|
||||
|
@ -345,11 +341,11 @@ class Player(xbmc.Player):
|
|||
|
||||
# Number of audiotracks to help get Emby Index
|
||||
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
||||
mapping = utils.window("emby_%s.indexMapping" % currentFile)
|
||||
mapping = window("emby_%s.indexMapping" % currentFile)
|
||||
|
||||
if mapping: # Set in PlaybackUtils.py
|
||||
|
||||
self.logMsg("Mapping for external subtitles index: %s" % mapping, 2)
|
||||
log("Mapping for external subtitles index: %s" % mapping, 2)
|
||||
externalIndex = json.loads(mapping)
|
||||
|
||||
if externalIndex.get(str(indexSubs)):
|
||||
|
@ -369,13 +365,13 @@ class Player(xbmc.Player):
|
|||
|
||||
# Report progress via websocketclient
|
||||
postdata = json.dumps(postdata)
|
||||
self.logMsg("Report: %s" % postdata, 2)
|
||||
log("Report: %s" % postdata, 2)
|
||||
self.ws.sendProgressUpdate(postdata)
|
||||
|
||||
def onPlayBackPaused(self):
|
||||
|
||||
currentFile = self.currentFile
|
||||
self.logMsg("PLAYBACK_PAUSED: %s" % currentFile, 2)
|
||||
log("PLAYBACK_PAUSED: %s" % currentFile, 2)
|
||||
|
||||
if self.played_info.get(currentFile):
|
||||
self.played_info[currentFile]['paused'] = True
|
||||
|
@ -385,7 +381,7 @@ class Player(xbmc.Player):
|
|||
def onPlayBackResumed(self):
|
||||
|
||||
currentFile = self.currentFile
|
||||
self.logMsg("PLAYBACK_RESUMED: %s" % currentFile, 2)
|
||||
log("PLAYBACK_RESUMED: %s" % currentFile, 2)
|
||||
|
||||
if self.played_info.get(currentFile):
|
||||
self.played_info[currentFile]['paused'] = False
|
||||
|
@ -395,7 +391,7 @@ class Player(xbmc.Player):
|
|||
def onPlayBackSeek(self, time, seekOffset):
|
||||
# Make position when seeking a bit more accurate
|
||||
currentFile = self.currentFile
|
||||
self.logMsg("PLAYBACK_SEEK: %s" % currentFile, 2)
|
||||
log("PLAYBACK_SEEK: %s" % currentFile, 2)
|
||||
|
||||
if self.played_info.get(currentFile):
|
||||
position = self.xbmcplayer.getTime()
|
||||
|
@ -404,39 +400,34 @@ class Player(xbmc.Player):
|
|||
self.reportPlayback()
|
||||
|
||||
def onPlayBackStopped(self):
|
||||
|
||||
window = utils.window
|
||||
# Will be called when user stops xbmc playing a file
|
||||
self.logMsg("ONPLAYBACK_STOPPED", 2)
|
||||
log("ONPLAYBACK_STOPPED", 2)
|
||||
window('emby_customPlaylist', clear=True)
|
||||
window('emby_customPlaylist.seektime', clear=True)
|
||||
window('emby_playbackProps', clear=True)
|
||||
self.logMsg("Clear playlist properties.", 1)
|
||||
log("Clear playlist properties.", 1)
|
||||
self.stopAll()
|
||||
|
||||
def onPlayBackEnded(self):
|
||||
# Will be called when xbmc stops playing a file
|
||||
self.logMsg("ONPLAYBACK_ENDED", 2)
|
||||
utils.window('emby_customPlaylist.seektime', clear=True)
|
||||
log("ONPLAYBACK_ENDED", 2)
|
||||
window('emby_customPlaylist.seektime', clear=True)
|
||||
self.stopAll()
|
||||
|
||||
def stopAll(self):
|
||||
|
||||
lang = utils.language
|
||||
settings = utils.settings
|
||||
|
||||
if not self.played_info:
|
||||
return
|
||||
|
||||
self.logMsg("Played_information: %s" % self.played_info, 1)
|
||||
log("Played_information: %s" % self.played_info, 1)
|
||||
# Process each items
|
||||
for item in self.played_info:
|
||||
|
||||
data = self.played_info.get(item)
|
||||
if data:
|
||||
|
||||
self.logMsg("Item path: %s" % item, 2)
|
||||
self.logMsg("Item data: %s" % data, 2)
|
||||
log("Item path: %s" % item, 2)
|
||||
log("Item data: %s" % data, 2)
|
||||
|
||||
runtime = data['runtime']
|
||||
currentPosition = data['currentPosition']
|
||||
|
@ -447,7 +438,7 @@ class Player(xbmc.Player):
|
|||
playMethod = data['playmethod']
|
||||
|
||||
# Prevent manually mark as watched in Kodi monitor
|
||||
utils.window('emby_skipWatched%s' % itemid, value="true")
|
||||
window('emby_skipWatched%s' % itemid, value="true")
|
||||
|
||||
if currentPosition and runtime:
|
||||
try:
|
||||
|
@ -457,7 +448,7 @@ class Player(xbmc.Player):
|
|||
percentComplete = 0
|
||||
|
||||
markPlayedAt = float(settings('markPlayed')) / 100
|
||||
self.logMsg("Percent complete: %s Mark played at: %s"
|
||||
log("Percent complete: %s Mark played at: %s"
|
||||
% (percentComplete, markPlayedAt), 1)
|
||||
|
||||
# Send the delete action to the server.
|
||||
|
@ -475,18 +466,18 @@ class Player(xbmc.Player):
|
|||
if percentComplete >= markPlayedAt and offerDelete:
|
||||
resp = xbmcgui.Dialog().yesno(lang(30091), lang(33015), autoclose=120000)
|
||||
if not resp:
|
||||
self.logMsg("User skipped deletion.", 1)
|
||||
log("User skipped deletion.", 1)
|
||||
continue
|
||||
|
||||
url = "{server}/emby/Items/%s?format=json" % itemid
|
||||
self.logMsg("Deleting request: %s" % itemid, 1)
|
||||
log("Deleting request: %s" % itemid, 1)
|
||||
self.doUtils(url, action_type="DELETE")
|
||||
|
||||
self.stopPlayback(data)
|
||||
|
||||
# Stop transcoding
|
||||
if playMethod == "Transcode":
|
||||
self.logMsg("Transcoding for %s terminated." % itemid, 1)
|
||||
log("Transcoding for %s terminated." % itemid, 1)
|
||||
deviceId = self.clientInfo.getDeviceId()
|
||||
url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId
|
||||
self.doUtils(url, action_type="DELETE")
|
||||
|
@ -495,7 +486,7 @@ class Player(xbmc.Player):
|
|||
|
||||
def stopPlayback(self, data):
|
||||
|
||||
self.logMsg("stopPlayback called", 2)
|
||||
log("stopPlayback called", 2)
|
||||
|
||||
itemId = data['item_id']
|
||||
currentPosition = data['currentPosition']
|
||||
|
|
|
@ -13,7 +13,7 @@ import playutils
|
|||
import playbackutils
|
||||
import embydb_functions as embydb
|
||||
import read_embyserver as embyserver
|
||||
import utils
|
||||
from utils import Logging, window, settings, language as lang, kodiSQL
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -23,25 +23,21 @@ class Playlist():
|
|||
|
||||
def __init__(self):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.clientInfo = clientinfo.ClientInfo()
|
||||
self.addonName = self.clientInfo.getAddonName()
|
||||
|
||||
self.userid = utils.window('emby_currUser')
|
||||
self.server = utils.window('emby_server%s' % self.userid)
|
||||
self.userid = window('emby_currUser')
|
||||
self.server = window('emby_server%s' % self.userid)
|
||||
|
||||
self.emby = embyserver.Read_EmbyServer()
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
self.className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
|
||||
|
||||
|
||||
def playAll(self, itemids, startat):
|
||||
|
||||
window = utils.window
|
||||
|
||||
embyconn = utils.kodiSQL('emby')
|
||||
embyconn = kodiSQL('emby')
|
||||
embycursor = embyconn.cursor()
|
||||
emby_db = embydb.Embydb_Functions(embycursor)
|
||||
|
||||
|
@ -49,8 +45,8 @@ class Playlist():
|
|||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
playlist.clear()
|
||||
|
||||
self.logMsg("---*** PLAY ALL ***---", 1)
|
||||
self.logMsg("Items: %s and start at: %s" % (itemids, startat), 1)
|
||||
log("---*** PLAY ALL ***---", 1)
|
||||
log("Items: %s and start at: %s" % (itemids, startat), 1)
|
||||
|
||||
started = False
|
||||
window('emby_customplaylist', value="true")
|
||||
|
@ -66,14 +62,14 @@ class Playlist():
|
|||
mediatype = embydb_item[4]
|
||||
except TypeError:
|
||||
# Item is not found in our database, add item manually
|
||||
self.logMsg("Item was not found in the database, manually adding item.", 1)
|
||||
log("Item was not found in the database, manually adding item.", 1)
|
||||
item = self.emby.getItem(itemid)
|
||||
self.addtoPlaylist_xbmc(playlist, item)
|
||||
else:
|
||||
# Add to playlist
|
||||
self.addtoPlaylist(dbid, mediatype)
|
||||
|
||||
self.logMsg("Adding %s to playlist." % itemid, 1)
|
||||
log("Adding %s to playlist." % itemid, 1)
|
||||
|
||||
if not started:
|
||||
started = True
|
||||
|
@ -84,12 +80,12 @@ class Playlist():
|
|||
|
||||
def modifyPlaylist(self, itemids):
|
||||
|
||||
embyconn = utils.kodiSQL('emby')
|
||||
embyconn = kodiSQL('emby')
|
||||
embycursor = embyconn.cursor()
|
||||
emby_db = embydb.Embydb_Functions(embycursor)
|
||||
|
||||
self.logMsg("---*** ADD TO PLAYLIST ***---", 1)
|
||||
self.logMsg("Items: %s" % itemids, 1)
|
||||
log("---*** ADD TO PLAYLIST ***---", 1)
|
||||
log("Items: %s" % itemids, 1)
|
||||
|
||||
player = xbmc.Player()
|
||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
|
@ -107,7 +103,7 @@ class Playlist():
|
|||
# Add to playlist
|
||||
self.addtoPlaylist(dbid, mediatype)
|
||||
|
||||
self.logMsg("Adding %s to playlist." % itemid, 1)
|
||||
log("Adding %s to playlist." % itemid, 1)
|
||||
|
||||
self.verifyPlaylist()
|
||||
embycursor.close()
|
||||
|
@ -130,17 +126,17 @@ class Playlist():
|
|||
else:
|
||||
pl['params']['item'] = {'file': url}
|
||||
|
||||
self.logMsg(xbmc.executeJSONRPC(json.dumps(pl)), 2)
|
||||
log(xbmc.executeJSONRPC(json.dumps(pl)), 2)
|
||||
|
||||
def addtoPlaylist_xbmc(self, playlist, item):
|
||||
|
||||
playurl = playutils.PlayUtils(item).getPlayUrl()
|
||||
if not playurl:
|
||||
# Playurl failed
|
||||
self.logMsg("Failed to retrieve playurl.", 1)
|
||||
log("Failed to retrieve playurl.", 1)
|
||||
return
|
||||
|
||||
self.logMsg("Playurl: %s" % playurl)
|
||||
log("Playurl: %s" % playurl)
|
||||
listitem = xbmcgui.ListItem()
|
||||
playbackutils.PlaybackUtils(item).setProperties(playurl, listitem)
|
||||
|
||||
|
@ -164,7 +160,7 @@ class Playlist():
|
|||
else:
|
||||
pl['params']['item'] = {'file': url}
|
||||
|
||||
self.logMsg(xbmc.executeJSONRPC(json.dumps(pl)), 2)
|
||||
log(xbmc.executeJSONRPC(json.dumps(pl)), 2)
|
||||
|
||||
def verifyPlaylist(self):
|
||||
|
||||
|
@ -178,7 +174,7 @@ class Playlist():
|
|||
'playlistid': 1
|
||||
}
|
||||
}
|
||||
self.logMsg(xbmc.executeJSONRPC(json.dumps(pl)), 2)
|
||||
log(xbmc.executeJSONRPC(json.dumps(pl)), 2)
|
||||
|
||||
def removefromPlaylist(self, position):
|
||||
|
||||
|
@ -193,4 +189,4 @@ class Playlist():
|
|||
'position': position
|
||||
}
|
||||
}
|
||||
self.logMsg(xbmc.executeJSONRPC(json.dumps(pl)), 2)
|
||||
log(xbmc.executeJSONRPC(json.dumps(pl)), 2)
|
|
@ -7,7 +7,7 @@ import xbmcgui
|
|||
import xbmcvfs
|
||||
|
||||
import clientinfo
|
||||
import utils
|
||||
from utils import Logging, window, settings, language as lang
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -17,41 +17,37 @@ class PlayUtils():
|
|||
|
||||
def __init__(self, item):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.item = item
|
||||
self.clientInfo = clientinfo.ClientInfo()
|
||||
self.addonName = self.clientInfo.getAddonName()
|
||||
|
||||
self.userid = utils.window('emby_currUser')
|
||||
self.server = utils.window('emby_server%s' % self.userid)
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
self.className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
|
||||
self.userid = window('emby_currUser')
|
||||
self.server = window('emby_server%s' % self.userid)
|
||||
|
||||
|
||||
def getPlayUrl(self):
|
||||
|
||||
window = utils.window
|
||||
|
||||
playurl = None
|
||||
|
||||
if (self.item.get('Type') in ("Recording", "TvChannel") and
|
||||
self.item.get('MediaSources') and self.item['MediaSources'][0]['Protocol'] == "Http"):
|
||||
if (self.item.get('Type') in ("Recording", "TvChannel") and self.item.get('MediaSources')
|
||||
and self.item['MediaSources'][0]['Protocol'] == "Http"):
|
||||
# Play LiveTV or recordings
|
||||
self.logMsg("File protocol is http (livetv).", 1)
|
||||
log("File protocol is http (livetv).", 1)
|
||||
playurl = "%s/emby/Videos/%s/live.m3u8?static=true" % (self.server, self.item['Id'])
|
||||
window('emby_%s.playmethod' % playurl, value="Transcode")
|
||||
|
||||
elif self.item.get('MediaSources') and self.item['MediaSources'][0]['Protocol'] == "Http":
|
||||
# Only play as http, used for channels, or online hosting of content
|
||||
self.logMsg("File protocol is http.", 1)
|
||||
log("File protocol is http.", 1)
|
||||
playurl = self.httpPlay()
|
||||
window('emby_%s.playmethod' % playurl, value="DirectStream")
|
||||
|
||||
elif self.isDirectPlay():
|
||||
|
||||
self.logMsg("File is direct playing.", 1)
|
||||
log("File is direct playing.", 1)
|
||||
playurl = self.directPlay()
|
||||
playurl = playurl.encode('utf-8')
|
||||
# Set playmethod property
|
||||
|
@ -59,14 +55,14 @@ class PlayUtils():
|
|||
|
||||
elif self.isDirectStream():
|
||||
|
||||
self.logMsg("File is direct streaming.", 1)
|
||||
log("File is direct streaming.", 1)
|
||||
playurl = self.directStream()
|
||||
# Set playmethod property
|
||||
window('emby_%s.playmethod' % playurl, value="DirectStream")
|
||||
|
||||
elif self.isTranscoding():
|
||||
|
||||
self.logMsg("File is transcoding.", 1)
|
||||
log("File is transcoding.", 1)
|
||||
playurl = self.transcoding()
|
||||
# Set playmethod property
|
||||
window('emby_%s.playmethod' % playurl, value="Transcode")
|
||||
|
@ -88,21 +84,18 @@ class PlayUtils():
|
|||
|
||||
def isDirectPlay(self):
|
||||
|
||||
lang = utils.language
|
||||
settings = utils.settings
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
|
||||
# Requirement: Filesystem, Accessible path
|
||||
if settings('playFromStream') == "true":
|
||||
# User forcing to play via HTTP
|
||||
self.logMsg("Can't direct play, play from HTTP enabled.", 1)
|
||||
log("Can't direct play, play from HTTP enabled.", 1)
|
||||
return False
|
||||
|
||||
videotrack = self.item['MediaSources'][0]['Name']
|
||||
transcodeH265 = settings('transcodeH265')
|
||||
videoprofiles = [x['Profile'] for x in self.item['MediaSources'][0]['MediaStreams'] if 'Profile' in x]
|
||||
transcodeHi10P = utils.settings('transcodeHi10P')
|
||||
transcodeHi10P = settings('transcodeHi10P')
|
||||
|
||||
if transcodeHi10P == "true" and "H264" in videotrack and "High 10" in videoprofiles:
|
||||
return False
|
||||
|
@ -116,7 +109,7 @@ class PlayUtils():
|
|||
'2': 720,
|
||||
'3': 1080
|
||||
}
|
||||
self.logMsg("Resolution is: %sP, transcode for resolution: %sP+"
|
||||
log("Resolution is: %sP, transcode for resolution: %sP+"
|
||||
% (resolution, res[transcodeH265]), 1)
|
||||
if res[transcodeH265] <= resolution:
|
||||
return False
|
||||
|
@ -124,25 +117,25 @@ class PlayUtils():
|
|||
canDirectPlay = self.item['MediaSources'][0]['SupportsDirectPlay']
|
||||
# Make sure direct play is supported by the server
|
||||
if not canDirectPlay:
|
||||
self.logMsg("Can't direct play, server doesn't allow/support it.", 1)
|
||||
log("Can't direct play, server doesn't allow/support it.", 1)
|
||||
return False
|
||||
|
||||
location = self.item['LocationType']
|
||||
if location == "FileSystem":
|
||||
# Verify the path
|
||||
if not self.fileExists():
|
||||
self.logMsg("Unable to direct play.")
|
||||
log("Unable to direct play.", 1)
|
||||
try:
|
||||
count = int(settings('failCount'))
|
||||
except ValueError:
|
||||
count = 0
|
||||
self.logMsg("Direct play failed: %s times." % count, 1)
|
||||
log("Direct play failed: %s times." % count, 1)
|
||||
|
||||
if count < 2:
|
||||
# Let the user know that direct play failed
|
||||
settings('failCount', value=str(count+1))
|
||||
dialog.notification(
|
||||
heading="Emby for Kodi",
|
||||
heading=lang(29999),
|
||||
message=lang(33011),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
sound=False)
|
||||
|
@ -151,7 +144,7 @@ class PlayUtils():
|
|||
settings('playFromStream', value="true")
|
||||
settings('failCount', value="0")
|
||||
dialog.notification(
|
||||
heading="Emby for Kodi",
|
||||
heading=lang(29999),
|
||||
message=lang(33012),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
sound=False)
|
||||
|
@ -192,27 +185,26 @@ class PlayUtils():
|
|||
|
||||
# Convert path to direct play
|
||||
path = self.directPlay()
|
||||
self.logMsg("Verifying path: %s" % path, 1)
|
||||
log("Verifying path: %s" % path, 1)
|
||||
|
||||
if xbmcvfs.exists(path):
|
||||
self.logMsg("Path exists.", 1)
|
||||
log("Path exists.", 1)
|
||||
return True
|
||||
|
||||
elif ":" not in path:
|
||||
self.logMsg("Can't verify path, assumed linux. Still try to direct play.", 1)
|
||||
log("Can't verify path, assumed linux. Still try to direct play.", 1)
|
||||
return True
|
||||
|
||||
else:
|
||||
self.logMsg("Failed to find file.", 1)
|
||||
log("Failed to find file.", 1)
|
||||
return False
|
||||
|
||||
def isDirectStream(self):
|
||||
|
||||
|
||||
videotrack = self.item['MediaSources'][0]['Name']
|
||||
transcodeH265 = utils.settings('transcodeH265')
|
||||
transcodeH265 = settings('transcodeH265')
|
||||
videoprofiles = [x['Profile'] for x in self.item['MediaSources'][0]['MediaStreams'] if 'Profile' in x]
|
||||
transcodeHi10P = utils.settings('transcodeHi10P')
|
||||
transcodeHi10P = settings('transcodeHi10P')
|
||||
|
||||
if transcodeHi10P == "true" and "H264" in videotrack and "High 10" in videoprofiles:
|
||||
return False
|
||||
|
@ -226,7 +218,7 @@ class PlayUtils():
|
|||
'2': 720,
|
||||
'3': 1080
|
||||
}
|
||||
self.logMsg("Resolution is: %sP, transcode for resolution: %sP+"
|
||||
log("Resolution is: %sP, transcode for resolution: %sP+"
|
||||
% (resolution, res[transcodeH265]), 1)
|
||||
if res[transcodeH265] <= resolution:
|
||||
return False
|
||||
|
@ -239,7 +231,7 @@ class PlayUtils():
|
|||
|
||||
# Verify the bitrate
|
||||
if not self.isNetworkSufficient():
|
||||
self.logMsg("The network speed is insufficient to direct stream file.", 1)
|
||||
log("The network speed is insufficient to direct stream file.", 1)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -258,15 +250,14 @@ class PlayUtils():
|
|||
|
||||
def isNetworkSufficient(self):
|
||||
|
||||
|
||||
settings = self.getBitrate()*1000
|
||||
|
||||
try:
|
||||
sourceBitrate = int(self.item['MediaSources'][0]['Bitrate'])
|
||||
except (KeyError, TypeError):
|
||||
self.logMsg("Bitrate value is missing.", 1)
|
||||
log("Bitrate value is missing.", 1)
|
||||
else:
|
||||
self.logMsg("The add-on settings bitrate is: %s, the video bitrate required is: %s"
|
||||
log("The add-on settings bitrate is: %s, the video bitrate required is: %s"
|
||||
% (settings, sourceBitrate), 1)
|
||||
if settings < sourceBitrate:
|
||||
return False
|
||||
|
@ -325,11 +316,10 @@ class PlayUtils():
|
|||
}
|
||||
|
||||
# max bit rate supported by server (max signed 32bit integer)
|
||||
return bitrate.get(utils.settings('videoBitrate'), 2147483)
|
||||
return bitrate.get(settings('videoBitrate'), 2147483)
|
||||
|
||||
def audioSubsPref(self, url, listitem):
|
||||
|
||||
lang = utils.language
|
||||
dialog = xbmcgui.Dialog()
|
||||
# For transcoding only
|
||||
# Present the list of audio to select from
|
||||
|
|
|
@ -4,21 +4,22 @@
|
|||
|
||||
import xbmc
|
||||
|
||||
import utils
|
||||
import clientinfo
|
||||
import downloadutils
|
||||
from utils import Logging, window, settings, kodiSQL
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
||||
class Read_EmbyServer():
|
||||
|
||||
limitIndex = int(utils.settings('limitindex'))
|
||||
limitIndex = int(settings('limitindex'))
|
||||
|
||||
|
||||
def __init__(self):
|
||||
|
||||
window = utils.window
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.clientInfo = clientinfo.ClientInfo()
|
||||
self.addonName = self.clientInfo.getAddonName()
|
||||
|
@ -27,17 +28,11 @@ class Read_EmbyServer():
|
|||
self.userId = window('emby_currUser')
|
||||
self.server = window('emby_server%s' % self.userId)
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
|
||||
def split_list(self, itemlist, size):
|
||||
# Split up list in pieces of size. Will generate a list of lists
|
||||
return [itemlist[i:i+size] for i in range(0, len(itemlist), size)]
|
||||
|
||||
|
||||
def getItem(self, itemid):
|
||||
# This will return the full item
|
||||
item = {}
|
||||
|
@ -60,7 +55,8 @@ class Read_EmbyServer():
|
|||
'Ids': ",".join(itemlist),
|
||||
'Fields': "Etag"
|
||||
}
|
||||
result = self.doUtils("{server}/emby/Users/{UserId}/Items?&format=json", parameters=params)
|
||||
url = "{server}/emby/Users/{UserId}/Items?&format=json"
|
||||
result = self.doUtils(url, parameters=params)
|
||||
if result:
|
||||
items.extend(result['Items'])
|
||||
|
||||
|
@ -86,7 +82,8 @@ class Read_EmbyServer():
|
|||
"MediaSources,VoteCount"
|
||||
)
|
||||
}
|
||||
result = self.doUtils("{server}/emby/Users/{UserId}/Items?format=json", parameters=params)
|
||||
url = "{server}/emby/Users/{UserId}/Items?format=json"
|
||||
result = self.doUtils(url, parameters=params)
|
||||
if result:
|
||||
items.extend(result['Items'])
|
||||
|
||||
|
@ -96,14 +93,15 @@ class Read_EmbyServer():
|
|||
# Returns ancestors using embyId
|
||||
viewId = None
|
||||
|
||||
for view in self.doUtils("{server}/emby/Items/%s/Ancestors?UserId={UserId}&format=json" % itemid):
|
||||
url = "{server}/emby/Items/%s/Ancestors?UserId={UserId}&format=json" % itemid
|
||||
for view in self.doUtils(url):
|
||||
|
||||
if view['Type'] == "CollectionFolder":
|
||||
# Found view
|
||||
viewId = view['Id']
|
||||
|
||||
# Compare to view table in emby database
|
||||
emby = utils.kodiSQL('emby')
|
||||
emby = kodiSQL('emby')
|
||||
cursor_emby = emby.cursor()
|
||||
query = ' '.join((
|
||||
|
||||
|
@ -124,7 +122,8 @@ class Read_EmbyServer():
|
|||
|
||||
return [viewName, viewId, mediatype]
|
||||
|
||||
def getFilteredSection(self, parentid, itemtype=None, sortby="SortName", recursive=True, limit=None, sortorder="Ascending", filter=""):
|
||||
def getFilteredSection(self, parentid, itemtype=None, sortby="SortName", recursive=True,
|
||||
limit=None, sortorder="Ascending", filter=""):
|
||||
params = {
|
||||
|
||||
'ParentId': parentid,
|
||||
|
@ -137,39 +136,54 @@ class Read_EmbyServer():
|
|||
'SortBy': sortby,
|
||||
'SortOrder': sortorder,
|
||||
'Filters': filter,
|
||||
'Fields': ( "Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
|
||||
'Fields': (
|
||||
|
||||
"Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
|
||||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers"
|
||||
)
|
||||
}
|
||||
return self.doUtils("{server}/emby/Users/{UserId}/Items?format=json", parameters=params)
|
||||
|
||||
def getTvChannels(self):
|
||||
|
||||
params = {
|
||||
|
||||
'EnableImages': True,
|
||||
'Fields': ( "Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
|
||||
'Fields': (
|
||||
|
||||
"Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
|
||||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers"
|
||||
)
|
||||
}
|
||||
return self.doUtils("{server}/emby/LiveTv/Channels/?userid={UserId}&format=json", parameters=params)
|
||||
url = "{server}/emby/LiveTv/Channels/?userid={UserId}&format=json"
|
||||
return self.doUtils(url, parameters=params)
|
||||
|
||||
def getTvRecordings(self, groupid):
|
||||
if groupid == "root": groupid = ""
|
||||
|
||||
if groupid == "root":
|
||||
groupid = ""
|
||||
|
||||
params = {
|
||||
|
||||
'GroupId': groupid,
|
||||
'EnableImages': True,
|
||||
'Fields': ( "Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
|
||||
'Fields': (
|
||||
|
||||
"Path,Genres,SortName,Studios,Writer,ProductionYear,Taglines,"
|
||||
"CommunityRating,OfficialRating,CumulativeRunTimeTicks,"
|
||||
"Metascore,AirTime,DateCreated,MediaStreams,People,Overview,"
|
||||
"CriticRating,CriticRatingSummary,Etag,ShortOverview,ProductionLocations,"
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers")
|
||||
"Tags,ProviderIds,ParentId,RemoteTrailers,SpecialEpisodeNumbers"
|
||||
)
|
||||
}
|
||||
return self.doUtils("{server}/emby/LiveTv/Recordings/?userid={UserId}&format=json", parameters=params)
|
||||
url = "{server}/emby/LiveTv/Recordings/?userid={UserId}&format=json"
|
||||
return self.doUtils(url, parameters=params)
|
||||
|
||||
def getSection(self, parentid, itemtype=None, sortby="SortName", basic=False, dialog=None):
|
||||
|
||||
|
@ -197,7 +211,7 @@ class Read_EmbyServer():
|
|||
items['TotalRecordCount'] = total
|
||||
|
||||
except TypeError: # Failed to retrieve
|
||||
self.logMsg("%s:%s Failed to retrieve the server response." % (url, params), 2)
|
||||
log("%s:%s Failed to retrieve the server response." % (url, params), 2)
|
||||
|
||||
else:
|
||||
index = 0
|
||||
|
@ -239,27 +253,27 @@ class Read_EmbyServer():
|
|||
# Something happened to the connection
|
||||
if not throttled:
|
||||
throttled = True
|
||||
self.logMsg("Throttle activated.", 1)
|
||||
log("Throttle activated.", 1)
|
||||
|
||||
if jump == highestjump:
|
||||
# We already tried with the highestjump, but it failed. Reset value.
|
||||
self.logMsg("Reset highest value.", 1)
|
||||
log("Reset highest value.", 1)
|
||||
highestjump = 0
|
||||
|
||||
# Lower the number by half
|
||||
if highestjump:
|
||||
throttled = False
|
||||
jump = highestjump
|
||||
self.logMsg("Throttle deactivated.", 1)
|
||||
log("Throttle deactivated.", 1)
|
||||
else:
|
||||
jump = int(jump/4)
|
||||
self.logMsg("Set jump limit to recover: %s" % jump, 2)
|
||||
log("Set jump limit to recover: %s" % jump, 2)
|
||||
|
||||
retry = 0
|
||||
while utils.window('emby_online') != "true":
|
||||
while window('emby_online') != "true":
|
||||
# Wait server to come back online
|
||||
if retry == 5:
|
||||
self.logMsg("Unable to reconnect to server. Abort process.", 1)
|
||||
log("Unable to reconnect to server. Abort process.", 1)
|
||||
return items
|
||||
|
||||
retry += 1
|
||||
|
@ -287,7 +301,7 @@ class Read_EmbyServer():
|
|||
increment = 10
|
||||
|
||||
jump += increment
|
||||
self.logMsg("Increase jump limit to: %s" % jump, 1)
|
||||
log("Increase jump limit to: %s" % jump, 1)
|
||||
return items
|
||||
|
||||
def getViews(self, mediatype="", root=False, sortedlist=False):
|
||||
|
@ -304,7 +318,7 @@ class Read_EmbyServer():
|
|||
try:
|
||||
items = result['Items']
|
||||
except TypeError:
|
||||
self.logMsg("Error retrieving views for type: %s" % mediatype, 2)
|
||||
log("Error retrieving views for type: %s" % mediatype, 2)
|
||||
else:
|
||||
for item in items:
|
||||
|
||||
|
@ -373,15 +387,18 @@ class Read_EmbyServer():
|
|||
return belongs
|
||||
|
||||
def getMovies(self, parentId, basic=False, dialog=None):
|
||||
|
||||
return self.getSection(parentId, "Movie", basic=basic, dialog=dialog)
|
||||
|
||||
def getBoxset(self, dialog=None):
|
||||
|
||||
return self.getSection(None, "BoxSet", dialog=dialog)
|
||||
|
||||
def getMovies_byBoxset(self, boxsetid):
|
||||
return self.getSection(boxsetid, "Movie")
|
||||
|
||||
def getMusicVideos(self, parentId, basic=False, dialog=None):
|
||||
|
||||
return self.getSection(parentId, "MusicVideo", basic=basic, dialog=dialog)
|
||||
|
||||
def getHomeVideos(self, parentId):
|
||||
|
@ -389,6 +406,7 @@ class Read_EmbyServer():
|
|||
return self.getSection(parentId, "Video")
|
||||
|
||||
def getShows(self, parentId, basic=False, dialog=None):
|
||||
|
||||
return self.getSection(parentId, "Series", basic=basic, dialog=dialog)
|
||||
|
||||
def getSeasons(self, showId):
|
||||
|
@ -404,7 +422,8 @@ class Read_EmbyServer():
|
|||
'IsVirtualUnaired': False,
|
||||
'Fields': "Etag"
|
||||
}
|
||||
result = self.doUtils("{server}/emby/Shows/%s/Seasons?UserId={UserId}&format=json" % showId, parameters=params)
|
||||
url = "{server}/emby/Shows/%s/Seasons?UserId={UserId}&format=json" % showId
|
||||
result = self.doUtils(url, parameters=params)
|
||||
if result:
|
||||
items = result
|
||||
|
||||
|
@ -422,7 +441,6 @@ class Read_EmbyServer():
|
|||
|
||||
return self.getSection(seasonId, "Episode")
|
||||
|
||||
|
||||
def getArtists(self, dialog=None):
|
||||
|
||||
items = {
|
||||
|
@ -444,7 +462,7 @@ class Read_EmbyServer():
|
|||
items['TotalRecordCount'] = total
|
||||
|
||||
except TypeError: # Failed to retrieve
|
||||
self.logMsg("%s:%s Failed to retrieve the server response." % (url, params), 2)
|
||||
log("%s:%s Failed to retrieve the server response." % (url, params), 2)
|
||||
|
||||
else:
|
||||
index = 1
|
||||
|
@ -478,17 +496,20 @@ class Read_EmbyServer():
|
|||
return items
|
||||
|
||||
def getAlbums(self, basic=False, dialog=None):
|
||||
|
||||
return self.getSection(None, "MusicAlbum", sortby="DateCreated", basic=basic, dialog=dialog)
|
||||
|
||||
def getAlbumsbyArtist(self, artistId):
|
||||
|
||||
return self.getSection(artistId, "MusicAlbum", sortby="DateCreated")
|
||||
|
||||
def getSongs(self, basic=False, dialog=None):
|
||||
|
||||
return self.getSection(None, "Audio", basic=basic, dialog=dialog)
|
||||
|
||||
def getSongsbyAlbum(self, albumId):
|
||||
return self.getSection(albumId, "Audio")
|
||||
|
||||
return self.getSection(albumId, "Audio")
|
||||
|
||||
def getAdditionalParts(self, itemId):
|
||||
|
||||
|
@ -497,8 +518,8 @@ class Read_EmbyServer():
|
|||
'Items': [],
|
||||
'TotalRecordCount': 0
|
||||
}
|
||||
|
||||
result = self.doUtils("{server}/emby/Videos/%s/AdditionalParts?UserId={UserId}&format=json" % itemId)
|
||||
url = "{server}/emby/Videos/%s/AdditionalParts?UserId={UserId}&format=json" % itemId
|
||||
result = self.doUtils(url)
|
||||
if result:
|
||||
items = result
|
||||
|
||||
|
@ -518,23 +539,36 @@ class Read_EmbyServer():
|
|||
|
||||
return sorted_items
|
||||
|
||||
def updateUserRating(self, itemid, like=None, favourite=None, deletelike=False):
|
||||
def updateUserRating(self, itemid, favourite=None):
|
||||
# Updates the user rating to Emby
|
||||
doUtils = self.doUtils
|
||||
|
||||
if favourite:
|
||||
self.doUtils("{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid, action_type="POST")
|
||||
elif favourite == False:
|
||||
self.doUtils("{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid, action_type="DELETE")
|
||||
|
||||
if not deletelike and like:
|
||||
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:
|
||||
self.doUtils("{server}/emby/Users/{UserId}/Items/%s/Rating?format=json" % itemid, action_type="DELETE")
|
||||
url = "{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid
|
||||
doUtils(url, action_type="POST")
|
||||
elif not favourite:
|
||||
url = "{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid
|
||||
doUtils(url, action_type="DELETE")
|
||||
else:
|
||||
self.logMsg("Error processing user rating.", 1)
|
||||
log("Error processing user rating.", 1)
|
||||
|
||||
self.logMsg("Update user rating to emby for itemid: %s "
|
||||
"| like: %s | favourite: %s | deletelike: %s"
|
||||
% (itemid, like, favourite, deletelike), 1)
|
||||
log("Update user rating to emby for itemid: %s | favourite: %s" % (itemid, favourite), 1)
|
||||
|
||||
def refreshItem(self, itemid):
|
||||
|
||||
url = "{server}/emby/Items/%s/Refresh?format=json" % itemid
|
||||
params = {
|
||||
|
||||
'Recursive': True,
|
||||
'ImageRefreshMode': "FullRefresh",
|
||||
'MetadataRefreshMode': "FullRefresh",
|
||||
'ReplaceAllImages': False,
|
||||
'ReplaceAllMetadata': True
|
||||
|
||||
}
|
||||
self.doUtils(url, postBody=params, action_type="POST")
|
||||
|
||||
def deleteItem(self, itemid):
|
||||
|
||||
url = "{server}/emby/Items/%s?format=json" % itemId
|
||||
self.doUtils(url, action_type="DELETE")
|
|
@ -11,9 +11,9 @@ import xbmcaddon
|
|||
import xbmcvfs
|
||||
|
||||
import artwork
|
||||
import utils
|
||||
import clientinfo
|
||||
import downloadutils
|
||||
from utils import Logging, window, settings, language as lang
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
@ -39,6 +39,9 @@ class UserClient(threading.Thread):
|
|||
|
||||
def __init__(self):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.__dict__ = self._shared_state
|
||||
self.addon = xbmcaddon.Addon()
|
||||
|
||||
|
@ -47,25 +50,20 @@ class UserClient(threading.Thread):
|
|||
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
|
||||
def getAdditionalUsers(self):
|
||||
|
||||
additionalUsers = utils.settings('additionalUsers')
|
||||
additionalUsers = settings('additionalUsers')
|
||||
|
||||
if additionalUsers:
|
||||
self.AdditionalUser = additionalUsers.split(',')
|
||||
|
||||
def getUsername(self):
|
||||
|
||||
username = utils.settings('username')
|
||||
username = settings('username')
|
||||
|
||||
if not username:
|
||||
self.logMsg("No username saved.", 2)
|
||||
log("No username saved.", 2)
|
||||
return ""
|
||||
|
||||
return username
|
||||
|
@ -73,7 +71,7 @@ class UserClient(threading.Thread):
|
|||
def getLogLevel(self):
|
||||
|
||||
try:
|
||||
logLevel = int(utils.settings('logLevel'))
|
||||
logLevel = int(settings('logLevel'))
|
||||
except ValueError:
|
||||
logLevel = 0
|
||||
|
||||
|
@ -81,9 +79,6 @@ class UserClient(threading.Thread):
|
|||
|
||||
def getUserId(self):
|
||||
|
||||
window = utils.window
|
||||
settings = utils.settings
|
||||
|
||||
username = self.getUsername()
|
||||
w_userId = window('emby_currUser')
|
||||
s_userId = settings('userId%s' % username)
|
||||
|
@ -93,22 +88,20 @@ class UserClient(threading.Thread):
|
|||
if not s_userId:
|
||||
# Save access token if it's missing from settings
|
||||
settings('userId%s' % username, value=w_userId)
|
||||
self.logMsg("Returning userId from WINDOW for username: %s UserId: %s"
|
||||
log("Returning userId from WINDOW for username: %s UserId: %s"
|
||||
% (username, w_userId), 2)
|
||||
return w_userId
|
||||
# Verify the settings
|
||||
elif s_userId:
|
||||
self.logMsg("Returning userId from SETTINGS for username: %s userId: %s"
|
||||
log("Returning userId from SETTINGS for username: %s userId: %s"
|
||||
% (username, s_userId), 2)
|
||||
return s_userId
|
||||
# No userId found
|
||||
else:
|
||||
self.logMsg("No userId saved for username: %s." % username, 1)
|
||||
log("No userId saved for username: %s." % username, 1)
|
||||
|
||||
def getServer(self, prefix=True):
|
||||
|
||||
settings = utils.settings
|
||||
|
||||
alternate = settings('altip') == "true"
|
||||
if alternate:
|
||||
# Alternate host
|
||||
|
@ -124,7 +117,7 @@ class UserClient(threading.Thread):
|
|||
server = host + ":" + port
|
||||
|
||||
if not host:
|
||||
self.logMsg("No server information saved.", 2)
|
||||
log("No server information saved.", 2)
|
||||
return False
|
||||
|
||||
# If https is true
|
||||
|
@ -141,9 +134,6 @@ class UserClient(threading.Thread):
|
|||
|
||||
def getToken(self):
|
||||
|
||||
window = utils.window
|
||||
settings = utils.settings
|
||||
|
||||
username = self.getUsername()
|
||||
userId = self.getUserId()
|
||||
w_token = window('emby_accessToken%s' % userId)
|
||||
|
@ -154,23 +144,21 @@ class UserClient(threading.Thread):
|
|||
if not s_token:
|
||||
# Save access token if it's missing from settings
|
||||
settings('accessToken', value=w_token)
|
||||
self.logMsg("Returning accessToken from WINDOW for username: %s accessToken: %s"
|
||||
log("Returning accessToken from WINDOW for username: %s accessToken: %s"
|
||||
% (username, w_token), 2)
|
||||
return w_token
|
||||
# Verify the settings
|
||||
elif s_token:
|
||||
self.logMsg("Returning accessToken from SETTINGS for username: %s accessToken: %s"
|
||||
log("Returning accessToken from SETTINGS for username: %s accessToken: %s"
|
||||
% (username, s_token), 2)
|
||||
window('emby_accessToken%s' % username, value=s_token)
|
||||
return s_token
|
||||
else:
|
||||
self.logMsg("No token found.", 1)
|
||||
log("No token found.", 1)
|
||||
return ""
|
||||
|
||||
def getSSLverify(self):
|
||||
# Verify host certificate
|
||||
settings = utils.settings
|
||||
|
||||
s_sslverify = settings('sslverify')
|
||||
if settings('altip') == "true":
|
||||
s_sslverify = settings('secondsslverify')
|
||||
|
@ -182,8 +170,6 @@ class UserClient(threading.Thread):
|
|||
|
||||
def getSSL(self):
|
||||
# Client side certificate
|
||||
settings = utils.settings
|
||||
|
||||
s_cert = settings('sslcert')
|
||||
if settings('altip') == "true":
|
||||
s_cert = settings('secondsslcert')
|
||||
|
@ -201,16 +187,16 @@ class UserClient(threading.Thread):
|
|||
self.userSettings = result
|
||||
# Set user image for skin display
|
||||
if result.get('PrimaryImageTag'):
|
||||
utils.window('EmbyUserImage', value=artwork.Artwork().getUserArtwork(result['Id'], 'Primary'))
|
||||
window('EmbyUserImage', value=artwork.Artwork().getUserArtwork(result['Id'], 'Primary'))
|
||||
|
||||
# Set resume point max
|
||||
result = doUtils("{server}/emby/System/Configuration?format=json")
|
||||
|
||||
utils.settings('markPlayed', value=str(result['MaxResumePct']))
|
||||
settings('markPlayed', value=str(result['MaxResumePct']))
|
||||
|
||||
def getPublicUsers(self):
|
||||
# Get public Users
|
||||
result = self.doUtils.downloadUrl("%s/emby/Users/Public?format=json" % self.getServer(), authenticate=False)
|
||||
url = "%s/emby/Users/Public?format=json" % self.getServer()
|
||||
result = self.doUtils.downloadUrl(url, authenticate=False)
|
||||
if result != "":
|
||||
return result
|
||||
else:
|
||||
|
@ -220,13 +206,11 @@ class UserClient(threading.Thread):
|
|||
|
||||
def hasAccess(self):
|
||||
# hasAccess is verified in service.py
|
||||
window = utils.window
|
||||
|
||||
result = self.doUtils.downloadUrl("{server}/emby/Users?format=json")
|
||||
|
||||
if result == False:
|
||||
# Access is restricted, set in downloadutils.py via exception
|
||||
self.logMsg("Access is restricted.", 1)
|
||||
log("Access is restricted.", 1)
|
||||
self.HasAccess = False
|
||||
|
||||
elif window('emby_online') != "true":
|
||||
|
@ -234,15 +218,13 @@ class UserClient(threading.Thread):
|
|||
pass
|
||||
|
||||
elif window('emby_serverStatus') == "restricted":
|
||||
self.logMsg("Access is granted.", 1)
|
||||
log("Access is granted.", 1)
|
||||
self.HasAccess = True
|
||||
window('emby_serverStatus', clear=True)
|
||||
xbmcgui.Dialog().notification("Emby for Kodi", utils.language(33007))
|
||||
xbmcgui.Dialog().notification(lang(29999), lang(33007))
|
||||
|
||||
def loadCurrUser(self, authenticated=False):
|
||||
|
||||
window = utils.window
|
||||
|
||||
doUtils = self.doUtils
|
||||
username = self.getUsername()
|
||||
userId = self.getUserId()
|
||||
|
@ -290,9 +272,6 @@ class UserClient(threading.Thread):
|
|||
|
||||
def authenticate(self):
|
||||
|
||||
lang = utils.language
|
||||
window = utils.window
|
||||
settings = utils.settings
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
# Get /profile/addon_data
|
||||
|
@ -304,12 +283,12 @@ class UserClient(threading.Thread):
|
|||
|
||||
# If there's no settings.xml
|
||||
if not hasSettings:
|
||||
self.logMsg("No settings.xml found.", 1)
|
||||
log("No settings.xml found.", 1)
|
||||
self.auth = False
|
||||
return
|
||||
# If no user information
|
||||
elif not server or not username:
|
||||
self.logMsg("Missing server information.", 1)
|
||||
log("Missing server information.", 1)
|
||||
self.auth = False
|
||||
return
|
||||
# If there's a token, load the user
|
||||
|
@ -319,9 +298,9 @@ class UserClient(threading.Thread):
|
|||
if result is False:
|
||||
pass
|
||||
else:
|
||||
self.logMsg("Current user: %s" % self.currUser, 1)
|
||||
self.logMsg("Current userId: %s" % self.currUserId, 1)
|
||||
self.logMsg("Current accessToken: %s" % self.currToken, 2)
|
||||
log("Current user: %s" % self.currUser, 1)
|
||||
log("Current userId: %s" % self.currUserId, 1)
|
||||
log("Current accessToken: %s" % self.currToken, 2)
|
||||
return
|
||||
|
||||
##### AUTHENTICATE USER #####
|
||||
|
@ -341,7 +320,7 @@ class UserClient(threading.Thread):
|
|||
option=xbmcgui.ALPHANUM_HIDE_INPUT)
|
||||
# If password dialog is cancelled
|
||||
if not password:
|
||||
self.logMsg("No password entered.", 0)
|
||||
log("No password entered.", 0)
|
||||
window('emby_serverStatus', value="Stop")
|
||||
self.auth = False
|
||||
return
|
||||
|
@ -356,37 +335,38 @@ class UserClient(threading.Thread):
|
|||
|
||||
# Authenticate username and password
|
||||
data = {'username': username, 'password': sha1}
|
||||
self.logMsg(data, 2)
|
||||
log(data, 2)
|
||||
|
||||
result = self.doUtils.downloadUrl("%s/emby/Users/AuthenticateByName?format=json" % server, postBody=data, action_type="POST", authenticate=False)
|
||||
url = "%s/emby/Users/AuthenticateByName?format=json" % server
|
||||
result = self.doUtils.downloadUrl(url, postBody=data, action_type="POST", authenticate=False)
|
||||
|
||||
try:
|
||||
self.logMsg("Auth response: %s" % result, 1)
|
||||
log("Auth response: %s" % result, 1)
|
||||
accessToken = result['AccessToken']
|
||||
|
||||
except (KeyError, TypeError):
|
||||
self.logMsg("Failed to retrieve the api key.", 1)
|
||||
log("Failed to retrieve the api key.", 1)
|
||||
accessToken = None
|
||||
|
||||
if accessToken is not None:
|
||||
self.currUser = username
|
||||
dialog.notification("Emby for Kodi",
|
||||
dialog.notification(lang(29999),
|
||||
"%s %s!" % (lang(33000), self.currUser.decode('utf-8')))
|
||||
settings('accessToken', value=accessToken)
|
||||
settings('userId%s' % username, value=result['User']['Id'])
|
||||
self.logMsg("User Authenticated: %s" % accessToken, 1)
|
||||
log("User Authenticated: %s" % accessToken, 1)
|
||||
self.loadCurrUser(authenticated=True)
|
||||
window('emby_serverStatus', clear=True)
|
||||
self.retry = 0
|
||||
else:
|
||||
self.logMsg("User authentication failed.", 1)
|
||||
log("User authentication failed.", 1)
|
||||
settings('accessToken', value="")
|
||||
settings('userId%s' % username, value="")
|
||||
dialog.ok(lang(33001), lang(33009))
|
||||
|
||||
# Give two attempts at entering password
|
||||
if self.retry == 2:
|
||||
self.logMsg("Too many retries. "
|
||||
log("Too many retries. "
|
||||
"You can retry by resetting attempts in the addon settings.", 1)
|
||||
window('emby_serverStatus', value="Stop")
|
||||
dialog.ok(lang(33001), lang(33010))
|
||||
|
@ -396,23 +376,21 @@ class UserClient(threading.Thread):
|
|||
|
||||
def resetClient(self):
|
||||
|
||||
self.logMsg("Reset UserClient authentication.", 1)
|
||||
log("Reset UserClient authentication.", 1)
|
||||
if self.currToken is not None:
|
||||
# In case of 401, removed saved token
|
||||
utils.settings('accessToken', value="")
|
||||
utils.window('emby_accessToken%s' % self.getUserId(), clear=True)
|
||||
settings('accessToken', value="")
|
||||
window('emby_accessToken%s' % self.getUserId(), clear=True)
|
||||
self.currToken = None
|
||||
self.logMsg("User token has been removed.", 1)
|
||||
log("User token has been removed.", 1)
|
||||
|
||||
self.auth = True
|
||||
self.currUser = None
|
||||
|
||||
def run(self):
|
||||
|
||||
window = utils.window
|
||||
|
||||
monitor = xbmc.Monitor()
|
||||
self.logMsg("----===## Starting UserClient ##===----", 0)
|
||||
log("----===## Starting UserClient ##===----", 0)
|
||||
|
||||
while not monitor.abortRequested():
|
||||
|
||||
|
@ -447,8 +425,8 @@ class UserClient(threading.Thread):
|
|||
# The status Stop is for when user cancelled password dialog.
|
||||
if server and username and status != "Stop":
|
||||
# Only if there's information found to login
|
||||
self.logMsg("Server found: %s" % server, 2)
|
||||
self.logMsg("Username found: %s" % username, 2)
|
||||
log("Server found: %s" % server, 2)
|
||||
log("Username found: %s" % username, 2)
|
||||
self.auth = True
|
||||
|
||||
|
||||
|
@ -461,7 +439,7 @@ class UserClient(threading.Thread):
|
|||
break
|
||||
|
||||
self.doUtils.stopSession()
|
||||
self.logMsg("##===---- UserClient Stopped ----===##", 0)
|
||||
log("##===---- UserClient Stopped ----===##", 0)
|
||||
|
||||
def stopClient(self):
|
||||
# When emby for kodi terminates
|
||||
|
|
|
@ -9,10 +9,10 @@ import pstats
|
|||
import sqlite3
|
||||
import StringIO
|
||||
import os
|
||||
from datetime import datetime, time
|
||||
import time
|
||||
import unicodedata
|
||||
import xml.etree.ElementTree as etree
|
||||
from datetime import datetime, time
|
||||
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
|
@ -20,9 +20,22 @@ import xbmcgui
|
|||
import xbmcvfs
|
||||
|
||||
#################################################################################################
|
||||
# Main methods
|
||||
|
||||
class Logging():
|
||||
|
||||
LOGGINGCLASS = None
|
||||
|
||||
|
||||
def logMsg(title, msg, level=1):
|
||||
def __init__(self, classname=""):
|
||||
|
||||
self.LOGGINGCLASS = classname
|
||||
|
||||
def log(self, msg, level=1):
|
||||
|
||||
self.logMsg("EMBY %s" % self.LOGGINGCLASS, msg, level)
|
||||
|
||||
def logMsg(self, title, msg, level=1):
|
||||
|
||||
# Get the logLevel set in UserClient
|
||||
try:
|
||||
|
@ -43,43 +56,46 @@ def logMsg(title, msg, level=1):
|
|||
except UnicodeEncodeError:
|
||||
xbmc.log("%s -> %s" % (title, msg.encode('utf-8')))
|
||||
|
||||
def window(property, value=None, clear=False, windowid=10000):
|
||||
# Get or set window property
|
||||
WINDOW = xbmcgui.Window(windowid)
|
||||
# Initiate class for utils.py document logging
|
||||
log = Logging('Utils').log
|
||||
|
||||
#setproperty accepts both string and unicode but utf-8 strings are adviced by kodi devs because some unicode can give issues
|
||||
'''if isinstance(property, unicode):
|
||||
property = property.encode("utf-8")
|
||||
if isinstance(value, unicode):
|
||||
value = value.encode("utf-8")'''
|
||||
|
||||
def window(property, value=None, clear=False, window_id=10000):
|
||||
# Get or set window property
|
||||
WINDOW = xbmcgui.Window(window_id)
|
||||
|
||||
if clear:
|
||||
WINDOW.clearProperty(property)
|
||||
elif value is not None:
|
||||
WINDOW.setProperty(property, value)
|
||||
else: #getproperty returns string so convert to unicode
|
||||
return WINDOW.getProperty(property)#.decode("utf-8")
|
||||
else:
|
||||
return WINDOW.getProperty(property)
|
||||
|
||||
def settings(setting, value=None):
|
||||
# Get or add addon setting
|
||||
if value is not None:
|
||||
xbmcaddon.Addon(id='plugin.video.emby').setSetting(setting, value)
|
||||
else:
|
||||
return xbmcaddon.Addon(id='plugin.video.emby').getSetting(setting) #returns unicode object
|
||||
addon = xbmcaddon.Addon(id='plugin.video.emby')
|
||||
|
||||
def language(stringid):
|
||||
# Central string retrieval
|
||||
string = xbmcaddon.Addon(id='plugin.video.emby').getLocalizedString(stringid) #returns unicode object
|
||||
if value is not None:
|
||||
addon.setSetting(setting, value)
|
||||
else: # returns unicode object
|
||||
return addon.getSetting(setting)
|
||||
|
||||
def language(string_id):
|
||||
# Central string retrieval - unicode
|
||||
string = xbmcaddon.Addon(id='plugin.video.emby').getLocalizedString(string_id)
|
||||
return string
|
||||
|
||||
#################################################################################################
|
||||
# Database related methods
|
||||
|
||||
def kodiSQL(media_type="video"):
|
||||
|
||||
if media_type == "emby":
|
||||
dbPath = xbmc.translatePath("special://database/emby.db").decode('utf-8')
|
||||
elif media_type == "music":
|
||||
dbPath = getKodiMusicDBPath()
|
||||
elif media_type == "texture":
|
||||
dbPath = xbmc.translatePath("special://database/Textures13.db").decode('utf-8')
|
||||
elif media_type == "music":
|
||||
dbPath = getKodiMusicDBPath()
|
||||
else:
|
||||
dbPath = getKodiVideoDBPath()
|
||||
|
||||
|
@ -118,6 +134,9 @@ def getKodiMusicDBPath():
|
|||
% dbVersion.get(xbmc.getInfoLabel('System.BuildVersion')[:2], "")).decode('utf-8')
|
||||
return dbPath
|
||||
|
||||
#################################################################################################
|
||||
# Utility methods
|
||||
|
||||
def getScreensaver():
|
||||
# Get the current screensaver value
|
||||
query = {
|
||||
|
@ -145,141 +164,10 @@ def setScreensaver(value):
|
|||
'value': value
|
||||
}
|
||||
}
|
||||
logMsg("EMBY", "Toggling screensaver: %s %s" % (value, xbmc.executeJSONRPC(json.dumps(query))), 1)
|
||||
result = xbmc.executeJSONRPC(json.dumps(query))
|
||||
log("Toggling screensaver: %s %s" % (value, result), 1)
|
||||
|
||||
def reset():
|
||||
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
if dialog.yesno("Warning", "Are you sure you want to reset your local Kodi database?") == 0:
|
||||
return
|
||||
|
||||
# first stop any db sync
|
||||
window('emby_shouldStop', value="true")
|
||||
count = 10
|
||||
while window('emby_dbScan') == "true":
|
||||
logMsg("EMBY", "Sync is running, will retry: %s..." % count)
|
||||
count -= 1
|
||||
if count == 0:
|
||||
dialog.ok("Warning", "Could not stop the database from running. Try again.")
|
||||
return
|
||||
xbmc.sleep(1000)
|
||||
|
||||
# Clean up the playlists
|
||||
deletePlaylists()
|
||||
|
||||
# Clean up the video nodes
|
||||
deleteNodes()
|
||||
|
||||
# Wipe the kodi databases
|
||||
logMsg("EMBY", "Resetting the Kodi video database.", 0)
|
||||
connection = kodiSQL('video')
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
tablename = row[0]
|
||||
if tablename != "version":
|
||||
cursor.execute("DELETE FROM " + tablename)
|
||||
connection.commit()
|
||||
cursor.close()
|
||||
|
||||
if settings('enableMusic') == "true":
|
||||
logMsg("EMBY", "Resetting the Kodi music database.")
|
||||
connection = kodiSQL('music')
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
tablename = row[0]
|
||||
if tablename != "version":
|
||||
cursor.execute("DELETE FROM " + tablename)
|
||||
connection.commit()
|
||||
cursor.close()
|
||||
|
||||
# Wipe the emby database
|
||||
logMsg("EMBY", "Resetting the Emby database.", 0)
|
||||
connection = kodiSQL('emby')
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
tablename = row[0]
|
||||
if tablename != "version":
|
||||
cursor.execute("DELETE FROM " + tablename)
|
||||
cursor.execute('DROP table IF EXISTS emby')
|
||||
cursor.execute('DROP table IF EXISTS view')
|
||||
connection.commit()
|
||||
cursor.close()
|
||||
|
||||
# Offer to wipe cached thumbnails
|
||||
resp = dialog.yesno("Warning", "Remove all cached artwork?")
|
||||
if resp:
|
||||
logMsg("EMBY", "Resetting all cached artwork.", 0)
|
||||
# Remove all existing textures first
|
||||
path = xbmc.translatePath("special://thumbnails/").decode('utf-8')
|
||||
if xbmcvfs.exists(path):
|
||||
allDirs, allFiles = xbmcvfs.listdir(path)
|
||||
for dir in allDirs:
|
||||
allDirs, allFiles = xbmcvfs.listdir(path+dir)
|
||||
for file in allFiles:
|
||||
if os.path.supports_unicode_filenames:
|
||||
xbmcvfs.delete(os.path.join(path+dir.decode('utf-8'),file.decode('utf-8')))
|
||||
else:
|
||||
xbmcvfs.delete(os.path.join(path.encode('utf-8')+dir,file))
|
||||
|
||||
# remove all existing data from texture DB
|
||||
connection = kodiSQL('texture')
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
tableName = row[0]
|
||||
if(tableName != "version"):
|
||||
cursor.execute("DELETE FROM " + tableName)
|
||||
connection.commit()
|
||||
cursor.close()
|
||||
|
||||
# reset the install run flag
|
||||
settings('SyncInstallRunDone', value="false")
|
||||
|
||||
# Remove emby info
|
||||
resp = dialog.yesno("Warning", "Reset all Emby Addon settings?")
|
||||
if resp:
|
||||
# Delete the settings
|
||||
addon = xbmcaddon.Addon()
|
||||
addondir = xbmc.translatePath(addon.getAddonInfo('profile')).decode('utf-8')
|
||||
dataPath = "%ssettings.xml" % addondir
|
||||
xbmcvfs.delete(dataPath)
|
||||
logMsg("EMBY", "Deleting: settings.xml", 1)
|
||||
|
||||
dialog.ok(
|
||||
heading="Emby for Kodi",
|
||||
line1="Database reset has completed, Kodi will now restart to apply the changes.")
|
||||
xbmc.executebuiltin('RestartApp')
|
||||
|
||||
def profiling(sortby="cumulative"):
|
||||
# Will print results to Kodi log
|
||||
def decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
|
||||
pr = cProfile.Profile()
|
||||
|
||||
pr.enable()
|
||||
result = func(*args, **kwargs)
|
||||
pr.disable()
|
||||
|
||||
s = StringIO.StringIO()
|
||||
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
|
||||
ps.print_stats()
|
||||
logMsg("EMBY Profiling", s.getvalue(), 1)
|
||||
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
def convertdate(date):
|
||||
def convertDate(date):
|
||||
try:
|
||||
date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%SZ")
|
||||
except TypeError:
|
||||
|
@ -344,6 +232,139 @@ def indent(elem, level=0):
|
|||
if level and (not elem.tail or not elem.tail.strip()):
|
||||
elem.tail = i
|
||||
|
||||
def profiling(sortby="cumulative"):
|
||||
# Will print results to Kodi log
|
||||
def decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
|
||||
pr = cProfile.Profile()
|
||||
|
||||
pr.enable()
|
||||
result = func(*args, **kwargs)
|
||||
pr.disable()
|
||||
|
||||
s = StringIO.StringIO()
|
||||
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
|
||||
ps.print_stats()
|
||||
log(s.getvalue(), 1)
|
||||
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
#################################################################################################
|
||||
# Addon utilities
|
||||
|
||||
def reset():
|
||||
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
if not dialog.yesno(language(29999), language(33074)):
|
||||
return
|
||||
|
||||
# first stop any db sync
|
||||
window('emby_shouldStop', value="true")
|
||||
count = 10
|
||||
while window('emby_dbScan') == "true":
|
||||
log("Sync is running, will retry: %s..." % count)
|
||||
count -= 1
|
||||
if count == 0:
|
||||
dialog.ok(language(29999), language(33085))
|
||||
return
|
||||
xbmc.sleep(1000)
|
||||
|
||||
# Clean up the playlists
|
||||
deletePlaylists()
|
||||
|
||||
# Clean up the video nodes
|
||||
deleteNodes()
|
||||
|
||||
# Wipe the kodi databases
|
||||
log("Resetting the Kodi video database.", 0)
|
||||
connection = kodiSQL('video')
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
tablename = row[0]
|
||||
if tablename != "version":
|
||||
cursor.execute("DELETE FROM " + tablename)
|
||||
connection.commit()
|
||||
cursor.close()
|
||||
|
||||
if settings('enableMusic') == "true":
|
||||
log("Resetting the Kodi music database.", 0)
|
||||
connection = kodiSQL('music')
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
tablename = row[0]
|
||||
if tablename != "version":
|
||||
cursor.execute("DELETE FROM " + tablename)
|
||||
connection.commit()
|
||||
cursor.close()
|
||||
|
||||
# Wipe the emby database
|
||||
log("Resetting the Emby database.", 0)
|
||||
connection = kodiSQL('emby')
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
tablename = row[0]
|
||||
if tablename != "version":
|
||||
cursor.execute("DELETE FROM " + tablename)
|
||||
cursor.execute('DROP table IF EXISTS emby')
|
||||
cursor.execute('DROP table IF EXISTS view')
|
||||
connection.commit()
|
||||
cursor.close()
|
||||
|
||||
# Offer to wipe cached thumbnails
|
||||
resp = dialog.yesno(language(29999), language(33086))
|
||||
if resp:
|
||||
log("Resetting all cached artwork.", 0)
|
||||
# Remove all existing textures first
|
||||
path = xbmc.translatePath("special://thumbnails/").decode('utf-8')
|
||||
if xbmcvfs.exists(path):
|
||||
allDirs, allFiles = xbmcvfs.listdir(path)
|
||||
for dir in allDirs:
|
||||
allDirs, allFiles = xbmcvfs.listdir(path+dir)
|
||||
for file in allFiles:
|
||||
if os.path.supports_unicode_filenames:
|
||||
xbmcvfs.delete(os.path.join(path+dir.decode('utf-8'),file.decode('utf-8')))
|
||||
else:
|
||||
xbmcvfs.delete(os.path.join(path.encode('utf-8')+dir,file))
|
||||
|
||||
# remove all existing data from texture DB
|
||||
connection = kodiSQL('texture')
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
tableName = row[0]
|
||||
if(tableName != "version"):
|
||||
cursor.execute("DELETE FROM " + tableName)
|
||||
connection.commit()
|
||||
cursor.close()
|
||||
|
||||
# reset the install run flag
|
||||
settings('SyncInstallRunDone', value="false")
|
||||
|
||||
# Remove emby info
|
||||
resp = dialog.yesno(language(29999), language(33087))
|
||||
if resp:
|
||||
# Delete the settings
|
||||
addon = xbmcaddon.Addon()
|
||||
addondir = xbmc.translatePath(addon.getAddonInfo('profile')).decode('utf-8')
|
||||
dataPath = "%ssettings.xml" % addondir
|
||||
xbmcvfs.delete(dataPath)
|
||||
log("Deleting: settings.xml", 1)
|
||||
|
||||
dialog.ok(heading=language(29999), line1=language(33088))
|
||||
xbmc.executebuiltin('RestartApp')
|
||||
|
||||
def sourcesXML():
|
||||
# To make Master lock compatible
|
||||
path = xbmc.translatePath("special://profile/").decode('utf-8')
|
||||
|
@ -401,7 +422,7 @@ def passwordsXML():
|
|||
credentials = settings('networkCreds')
|
||||
if credentials:
|
||||
# Present user with options
|
||||
option = dialog.select("Modify/Remove network credentials", ["Modify", "Remove"])
|
||||
option = dialog.select(language(33075), [language(33076), language(33077)])
|
||||
|
||||
if option < 0:
|
||||
# User cancelled dialog
|
||||
|
@ -413,17 +434,16 @@ def passwordsXML():
|
|||
for path in paths:
|
||||
if path.find('.//from').text == "smb://%s/" % credentials:
|
||||
paths.remove(path)
|
||||
logMsg("EMBY", "Successfully removed credentials for: %s"
|
||||
% credentials, 1)
|
||||
log("Successfully removed credentials for: %s" % credentials, 1)
|
||||
etree.ElementTree(root).write(xmlpath)
|
||||
break
|
||||
else:
|
||||
logMsg("EMBY", "Failed to find saved server: %s in passwords.xml" % credentials, 1)
|
||||
log("Failed to find saved server: %s in passwords.xml" % credentials, 1)
|
||||
|
||||
settings('networkCreds', value="")
|
||||
xbmcgui.Dialog().notification(
|
||||
heading="Emby for Kodi",
|
||||
message="%s removed from passwords.xml" % credentials,
|
||||
heading=language(29999),
|
||||
message="%s %s" % (language(33078), credentials),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
time=1000,
|
||||
sound=False)
|
||||
|
@ -431,28 +451,22 @@ def passwordsXML():
|
|||
|
||||
elif option == 0:
|
||||
# User selected to modify
|
||||
server = dialog.input("Modify the computer name or ip address", credentials)
|
||||
server = dialog.input(language(33083), credentials)
|
||||
if not server:
|
||||
return
|
||||
else:
|
||||
# No credentials added
|
||||
dialog.ok(
|
||||
heading="Network credentials",
|
||||
line1= (
|
||||
"Input the server name or IP address as indicated in your emby library paths. "
|
||||
'For example, the server name: \\\\SERVER-PC\\path\\ is "SERVER-PC".'))
|
||||
server = dialog.input("Enter the server name or IP address")
|
||||
dialog.ok(heading=language(29999), line1=language(33082))
|
||||
server = dialog.input(language(33084))
|
||||
if not server:
|
||||
return
|
||||
|
||||
# Network username
|
||||
user = dialog.input("Enter the network username")
|
||||
user = dialog.input(language(33079))
|
||||
if not user:
|
||||
return
|
||||
# Network password
|
||||
password = dialog.input(
|
||||
heading="Enter the network password",
|
||||
option=xbmcgui.ALPHANUM_HIDE_INPUT)
|
||||
password = dialog.input(heading=language(33080), option=xbmcgui.ALPHANUM_HIDE_INPUT)
|
||||
if not password:
|
||||
return
|
||||
|
||||
|
@ -473,7 +487,7 @@ def passwordsXML():
|
|||
|
||||
# Add credentials
|
||||
settings('networkCreds', value="%s" % server)
|
||||
logMsg("EMBY", "Added server: %s to passwords.xml" % server, 1)
|
||||
log("Added server: %s to passwords.xml" % server, 1)
|
||||
# Prettify and write to file
|
||||
try:
|
||||
indent(root)
|
||||
|
@ -481,8 +495,8 @@ def passwordsXML():
|
|||
etree.ElementTree(root).write(xmlpath)
|
||||
|
||||
dialog.notification(
|
||||
heading="Emby for Kodi",
|
||||
message="%s added to passwords.xml" % server,
|
||||
heading=language(29999),
|
||||
message="%s %s" % (language(33081), server),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
time=1000,
|
||||
sound=False)
|
||||
|
@ -501,7 +515,7 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
|
|||
|
||||
# Create the playlist directory
|
||||
if not xbmcvfs.exists(path):
|
||||
logMsg("EMBY", "Creating directory: %s" % path, 1)
|
||||
log("Creating directory: %s" % path, 1)
|
||||
xbmcvfs.mkdirs(path)
|
||||
|
||||
# Only add the playlist if it doesn't already exists
|
||||
|
@ -509,7 +523,7 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
|
|||
|
||||
if delete:
|
||||
xbmcvfs.delete(xsppath)
|
||||
logMsg("EMBY", "Successfully removed playlist: %s." % tagname, 1)
|
||||
log("Successfully removed playlist: %s." % tagname, 1)
|
||||
|
||||
return
|
||||
|
||||
|
@ -517,11 +531,11 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
|
|||
itemtypes = {
|
||||
'homevideos': "movies"
|
||||
}
|
||||
logMsg("EMBY", "Writing playlist file to: %s" % xsppath, 1)
|
||||
log("Writing playlist file to: %s" % xsppath, 1)
|
||||
try:
|
||||
f = xbmcvfs.File(xsppath, 'w')
|
||||
except:
|
||||
logMsg("EMBY", "Failed to create playlist: %s" % xsppath, 1)
|
||||
log("Failed to create playlist: %s" % xsppath, 1)
|
||||
return
|
||||
else:
|
||||
f.write(
|
||||
|
@ -535,7 +549,7 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False):
|
|||
'</smartplaylist>'
|
||||
% (itemtypes.get(mediatype, mediatype), plname, tagname))
|
||||
f.close()
|
||||
logMsg("EMBY", "Successfully added playlist: %s" % tagname)
|
||||
log("Successfully added playlist: %s" % tagname, 1)
|
||||
|
||||
def deletePlaylists():
|
||||
|
||||
|
@ -557,10 +571,10 @@ def deleteNodes():
|
|||
try:
|
||||
shutil.rmtree("%s%s" % (path, dir.decode('utf-8')))
|
||||
except:
|
||||
logMsg("EMBY", "Failed to delete directory: %s" % dir.decode('utf-8'))
|
||||
log("Failed to delete directory: %s" % dir.decode('utf-8'), 0)
|
||||
for file in files:
|
||||
if file.decode('utf-8').startswith('emby'):
|
||||
try:
|
||||
xbmcvfs.delete("%s%s" % (path, file.decode('utf-8')))
|
||||
except:
|
||||
logMsg("EMBY", "Failed to file: %s" % file.decode('utf-8'))
|
||||
log("Failed to file: %s" % file.decode('utf-8'), 0)
|
|
@ -11,6 +11,7 @@ import xbmcvfs
|
|||
|
||||
import clientinfo
|
||||
import utils
|
||||
from utils import Logging, window, language as lang
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -20,16 +21,14 @@ class VideoNodes(object):
|
|||
|
||||
def __init__(self):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
clientInfo = clientinfo.ClientInfo()
|
||||
self.addonName = clientInfo.getAddonName()
|
||||
|
||||
self.kodiversion = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
|
||||
def commonRoot(self, order, label, tagname, roottype=1):
|
||||
|
||||
|
@ -54,8 +53,6 @@ class VideoNodes(object):
|
|||
|
||||
def viewNode(self, indexnumber, tagname, mediatype, viewtype, viewid, delete=False):
|
||||
|
||||
window = utils.window
|
||||
|
||||
if viewtype == "mixed":
|
||||
dirname = "%s - %s" % (viewid, mediatype)
|
||||
else:
|
||||
|
@ -82,7 +79,7 @@ class VideoNodes(object):
|
|||
for file in files:
|
||||
xbmcvfs.delete(nodepath + file)
|
||||
|
||||
self.logMsg("Sucessfully removed videonode: %s." % tagname, 1)
|
||||
log("Sucessfully removed videonode: %s." % tagname, 1)
|
||||
return
|
||||
|
||||
# Create index entry
|
||||
|
@ -184,7 +181,7 @@ class VideoNodes(object):
|
|||
# Get label
|
||||
stringid = nodes[node]
|
||||
if node != "1":
|
||||
label = utils.language(stringid)
|
||||
label = lang(stringid)
|
||||
if not label:
|
||||
label = xbmc.getLocalizedString(stringid)
|
||||
else:
|
||||
|
@ -319,8 +316,6 @@ class VideoNodes(object):
|
|||
|
||||
def singleNode(self, indexnumber, tagname, mediatype, itemtype):
|
||||
|
||||
window = utils.window
|
||||
|
||||
tagname = tagname.encode('utf-8')
|
||||
cleantagname = utils.normalize_nodes(tagname)
|
||||
nodepath = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
|
||||
|
@ -342,7 +337,7 @@ class VideoNodes(object):
|
|||
'Favorite tvshows': 30181,
|
||||
'channels': 30173
|
||||
}
|
||||
label = utils.language(labels[tagname])
|
||||
label = lang(labels[tagname])
|
||||
embynode = "Emby.nodes.%s" % indexnumber
|
||||
window('%s.title' % embynode, value=label)
|
||||
window('%s.path' % embynode, value=windowpath)
|
||||
|
@ -369,9 +364,7 @@ class VideoNodes(object):
|
|||
|
||||
def clearProperties(self):
|
||||
|
||||
window = utils.window
|
||||
|
||||
self.logMsg("Clearing nodes properties.", 1)
|
||||
log("Clearing nodes properties.", 1)
|
||||
embyprops = window('Emby.nodes.total')
|
||||
propnames = [
|
||||
|
||||
|
|
|
@ -14,10 +14,7 @@ import downloadutils
|
|||
import librarysync
|
||||
import playlist
|
||||
import userclient
|
||||
import utils
|
||||
|
||||
import logging
|
||||
logging.basicConfig()
|
||||
from utils import Logging, window, settings, language as lang
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -32,6 +29,9 @@ class WebSocket_Client(threading.Thread):
|
|||
|
||||
def __init__(self):
|
||||
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.__dict__ = self._shared_state
|
||||
self.monitor = xbmc.Monitor()
|
||||
|
||||
|
@ -43,15 +43,10 @@ class WebSocket_Client(threading.Thread):
|
|||
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
self.className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
|
||||
|
||||
|
||||
def sendProgressUpdate(self, data):
|
||||
|
||||
self.logMsg("sendProgressUpdate", 2)
|
||||
log("sendProgressUpdate", 2)
|
||||
try:
|
||||
messageData = {
|
||||
|
||||
|
@ -60,23 +55,21 @@ class WebSocket_Client(threading.Thread):
|
|||
}
|
||||
messageString = json.dumps(messageData)
|
||||
self.client.send(messageString)
|
||||
self.logMsg("Message data: %s" % messageString, 2)
|
||||
log("Message data: %s" % messageString, 2)
|
||||
|
||||
except Exception as e:
|
||||
self.logMsg("Exception: %s" % e, 1)
|
||||
log("Exception: %s" % e, 1)
|
||||
|
||||
def on_message(self, ws, message):
|
||||
|
||||
window = utils.window
|
||||
lang = utils.language
|
||||
|
||||
result = json.loads(message)
|
||||
messageType = result['MessageType']
|
||||
data = result['Data']
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
if messageType not in ('SessionEnded'):
|
||||
# Mute certain events
|
||||
self.logMsg("Message: %s" % message, 1)
|
||||
log("Message: %s" % message, 1)
|
||||
|
||||
if messageType == "Play":
|
||||
# A remote control play command has been sent from the server.
|
||||
|
@ -84,11 +77,10 @@ class WebSocket_Client(threading.Thread):
|
|||
command = data['PlayCommand']
|
||||
|
||||
pl = playlist.Playlist()
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
if command == "PlayNow":
|
||||
dialog.notification(
|
||||
heading="Emby for Kodi",
|
||||
heading=lang(29999),
|
||||
message="%s %s" % (len(itemIds), lang(33004)),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
sound=False)
|
||||
|
@ -97,7 +89,7 @@ class WebSocket_Client(threading.Thread):
|
|||
|
||||
elif command == "PlayNext":
|
||||
dialog.notification(
|
||||
heading="Emby for Kodi",
|
||||
heading=lang(29999),
|
||||
message="%s %s" % (len(itemIds), lang(33005)),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
sound=False)
|
||||
|
@ -126,10 +118,10 @@ class WebSocket_Client(threading.Thread):
|
|||
seekto = data['SeekPositionTicks']
|
||||
seektime = seekto / 10000000.0
|
||||
action(seektime)
|
||||
self.logMsg("Seek to %s." % seektime, 1)
|
||||
log("Seek to %s." % seektime, 1)
|
||||
else:
|
||||
action()
|
||||
self.logMsg("Command: %s completed." % command, 1)
|
||||
log("Command: %s completed." % command, 1)
|
||||
|
||||
window('emby_command', value="true")
|
||||
|
||||
|
@ -199,7 +191,7 @@ class WebSocket_Client(threading.Thread):
|
|||
|
||||
header = arguments['Header']
|
||||
text = arguments['Text']
|
||||
xbmcgui.Dialog().notification(
|
||||
dialog.notification(
|
||||
heading=header,
|
||||
message=text,
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
|
@ -250,9 +242,9 @@ class WebSocket_Client(threading.Thread):
|
|||
xbmc.executebuiltin(action)
|
||||
|
||||
elif messageType == "ServerRestarting":
|
||||
if utils.settings('supressRestartMsg') == "true":
|
||||
xbmcgui.Dialog().notification(
|
||||
heading="Emby for Kodi",
|
||||
if settings('supressRestartMsg') == "true":
|
||||
dialog.notification(
|
||||
heading=lang(29999),
|
||||
message=lang(33006),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png")
|
||||
|
||||
|
@ -262,7 +254,7 @@ class WebSocket_Client(threading.Thread):
|
|||
self.librarySync.refresh_views = True
|
||||
|
||||
def on_close(self, ws):
|
||||
self.logMsg("Closed.", 2)
|
||||
log("Closed.", 2)
|
||||
|
||||
def on_open(self, ws):
|
||||
self.doUtils.postCapabilities(self.deviceId)
|
||||
|
@ -272,11 +264,10 @@ class WebSocket_Client(threading.Thread):
|
|||
# Server is offline
|
||||
pass
|
||||
else:
|
||||
self.logMsg("Error: %s" % error, 2)
|
||||
log("Error: %s" % error, 2)
|
||||
|
||||
def run(self):
|
||||
|
||||
window = utils.window
|
||||
loglevel = int(window('emby_logLevel'))
|
||||
# websocket.enableTrace(True)
|
||||
|
||||
|
@ -290,7 +281,7 @@ class WebSocket_Client(threading.Thread):
|
|||
server = server.replace('http', "ws")
|
||||
|
||||
websocket_url = "%s?api_key=%s&deviceId=%s" % (server, token, self.deviceId)
|
||||
self.logMsg("websocket url: %s" % websocket_url, 1)
|
||||
log("websocket url: %s" % websocket_url, 1)
|
||||
|
||||
self.client = websocket.WebSocketApp(websocket_url,
|
||||
on_message=self.on_message,
|
||||
|
@ -298,7 +289,7 @@ class WebSocket_Client(threading.Thread):
|
|||
on_close=self.on_close)
|
||||
|
||||
self.client.on_open = self.on_open
|
||||
self.logMsg("----===## Starting WebSocketClient ##===----", 0)
|
||||
log("----===## Starting WebSocketClient ##===----", 0)
|
||||
|
||||
while not self.monitor.abortRequested():
|
||||
|
||||
|
@ -310,10 +301,10 @@ class WebSocket_Client(threading.Thread):
|
|||
# Abort was requested, exit
|
||||
break
|
||||
|
||||
self.logMsg("##===---- WebSocketClient Stopped ----===##", 0)
|
||||
log("##===---- WebSocketClient Stopped ----===##", 0)
|
||||
|
||||
def stopClient(self):
|
||||
|
||||
self.stopWebsocket = True
|
||||
self.client.close()
|
||||
self.logMsg("Stopping thread.", 1)
|
||||
log("Stopping thread.", 1)
|
|
@ -33,7 +33,7 @@
|
|||
<setting id="imageCacheLimit" type="enum" label="30513" values="Disabled|5|10|15|20|25" default="0" visible="eq(-1,true)" subsetting="true" />
|
||||
<setting id="syncEmptyShows" type="bool" label="30508" default="false" />
|
||||
<setting id="dbSyncScreensaver" label="30536" type="bool" default="false" />
|
||||
<setting id="useDirectPaths" type="enum" label="30511" values="Addon(Default)|Native(Direct paths)" default="0" />
|
||||
<setting id="useDirectPaths" type="enum" label="30511" lvalues="33036|33037" default="0" />
|
||||
<setting id="enableMusic" type="bool" label="30509" default="true" />
|
||||
<setting id="streamMusic" type="bool" label="30510" default="false" visible="eq(-1,true)" subsetting="true" />
|
||||
<setting type="lsep" label="30523" />
|
||||
|
|
38
service.py
38
service.py
|
@ -16,9 +16,9 @@ import xbmcvfs
|
|||
#################################################################################################
|
||||
|
||||
_addon = xbmcaddon.Addon(id='plugin.video.emby')
|
||||
addon_path = _addon.getAddonInfo('path').decode('utf-8')
|
||||
base_resource = xbmc.translatePath(os.path.join(addon_path, 'resources', 'lib')).decode('utf-8')
|
||||
sys.path.append(base_resource)
|
||||
_addon_path = _addon.getAddonInfo('path').decode('utf-8')
|
||||
_base_resource = xbmc.translatePath(os.path.join(_addon_path, 'resources', 'lib')).decode('utf-8')
|
||||
sys.path.append(_base_resource)
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -28,9 +28,9 @@ import initialsetup
|
|||
import kodimonitor
|
||||
import librarysync
|
||||
import player
|
||||
import utils
|
||||
import videonodes
|
||||
import websocket_client as wsc
|
||||
from utils import Logging, window, settings, language as lang
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
@ -49,8 +49,8 @@ class Service():
|
|||
|
||||
def __init__(self):
|
||||
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
global log
|
||||
log = Logging(self.__class__.__name__).log
|
||||
|
||||
self.clientInfo = clientinfo.ClientInfo()
|
||||
self.addonName = self.clientInfo.getAddonName()
|
||||
|
@ -58,15 +58,14 @@ class Service():
|
|||
self.monitor = xbmc.Monitor()
|
||||
|
||||
window('emby_logLevel', value=str(logLevel))
|
||||
window('emby_kodiProfile', value=xbmc.translatePath("special://profile"))
|
||||
window('emby_pluginpath', value=utils.settings('useDirectPaths'))
|
||||
window('emby_kodiProfile', value=xbmc.translatePath('special://profile'))
|
||||
|
||||
# Initial logging
|
||||
log("======== START %s ========" % self.addonName, 0)
|
||||
log("Platform: %s" % (self.clientInfo.getPlatform()), 0)
|
||||
log("KODI Version: %s" % xbmc.getInfoLabel('System.BuildVersion'), 0)
|
||||
log("%s Version: %s" % (self.addonName, self.clientInfo.getVersion()), 0)
|
||||
log("Using plugin paths: %s" % (utils.settings('useDirectPaths') != "true"), 0)
|
||||
log("Using plugin paths: %s" % (settings('useDirectPaths') == "0"), 0)
|
||||
log("Log Level: %s" % logLevel, 0)
|
||||
|
||||
# Reset window props for profile switch
|
||||
|
@ -86,22 +85,13 @@ class Service():
|
|||
# Set the minimum database version
|
||||
window('emby_minDBVersion', value="1.1.63")
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
|
||||
def ServiceEntryPoint(self):
|
||||
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
lang = utils.language
|
||||
|
||||
# Important: Threads depending on abortRequest will not trigger
|
||||
# if profile switch happens more than once.
|
||||
monitor = self.monitor
|
||||
kodiProfile = xbmc.translatePath("special://profile")
|
||||
kodiProfile = xbmc.translatePath('special://profile')
|
||||
|
||||
# Server auto-detect
|
||||
initialsetup.InitialSetup().setup()
|
||||
|
@ -119,7 +109,7 @@ class Service():
|
|||
if window('emby_kodiProfile') != kodiProfile:
|
||||
# Profile change happened, terminate this thread and others
|
||||
log("Kodi profile was: %s and changed to: %s. Terminating old Emby thread."
|
||||
% (kodiProfile, utils.window('emby_kodiProfile')), 1)
|
||||
% (kodiProfile, window('emby_kodiProfile')), 1)
|
||||
|
||||
break
|
||||
|
||||
|
@ -167,7 +157,7 @@ class Service():
|
|||
else:
|
||||
# Start up events
|
||||
self.warn_auth = True
|
||||
if utils.settings('connectMsg') == "true" and self.welcome_msg:
|
||||
if settings('connectMsg') == "true" and self.welcome_msg:
|
||||
# Reset authentication warnings
|
||||
self.welcome_msg = False
|
||||
# Get additional users
|
||||
|
@ -177,7 +167,7 @@ class Service():
|
|||
else:
|
||||
add = ""
|
||||
xbmcgui.Dialog().notification(
|
||||
heading="Emby for Kodi",
|
||||
heading=lang(29999),
|
||||
message=("%s %s%s!"
|
||||
% (lang(33000), user.currUser.decode('utf-8'),
|
||||
add.decode('utf-8'))),
|
||||
|
@ -252,7 +242,7 @@ class Service():
|
|||
break
|
||||
# Alert the user that server is online.
|
||||
xbmcgui.Dialog().notification(
|
||||
heading="Emby for Kodi",
|
||||
heading=lang(29999),
|
||||
message=lang(33003),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
time=2000,
|
||||
|
@ -291,7 +281,7 @@ class Service():
|
|||
log("======== STOP %s ========" % self.addonName, 0)
|
||||
|
||||
# Delay option
|
||||
delay = int(utils.settings('startupDelay'))
|
||||
delay = int(settings('startupDelay'))
|
||||
|
||||
xbmc.log("Delaying emby startup by: %s sec..." % delay)
|
||||
if delay and xbmc.Monitor().waitForAbort(delay):
|
||||
|
|
Loading…
Reference in a new issue