Merge remote-tracking branch 'MediaBrowser/master' into develop
This commit is contained in:
commit
50d142ad0b
18 changed files with 414 additions and 227 deletions
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<addon id="plugin.video.plexkodiconnect"
|
||||
name="PlexKodiConnect"
|
||||
version="2.1.0"
|
||||
version="2.2.1"
|
||||
provider-name="croneter">
|
||||
<requires>
|
||||
<import addon="xbmc.python" version="2.1.0"/>
|
||||
|
|
|
@ -1,3 +1,23 @@
|
|||
version 2.2.1
|
||||
- Fix artist/album link for music videos
|
||||
- Fix progress dialog when the manual sync runs at start up
|
||||
- Fix encoding error for special characters in emby username
|
||||
- Offer delete dialog after playback now times out after 2 mins
|
||||
|
||||
version 2.1.4
|
||||
- Removed Emby delete via the Kodi context menu. It is exclusively offered via the extended emby context menu which is available for Isengard or higher. This change was necessary, because there was a risk of wiping the entire library if Kodi decides to run a clean database task and paths were set as plugin paths.
|
||||
|
||||
version 2.1.3
|
||||
- Fix Live TV to terminate ffmpeg processes.
|
||||
|
||||
version 2.1.2
|
||||
- Fix to repair entries if they are deleted by Kodi, but still exists in the Emby database.
|
||||
|
||||
version 2.1.1
|
||||
- Update setting - skip emby delete confirmation, it is now under the extras tab.
|
||||
- Update setting - new content notification, it's now disables the notification if the time is set to 0.
|
||||
- Prevent manual sync from running if the add-on is not yet connected to the emby server.
|
||||
|
||||
version 2.1.0
|
||||
- Add a throttle (automatically adjust the number of items requested at once) to prevent crashing during the initial sync
|
||||
- Do not update the video library when there's a music-only update
|
||||
|
|
|
@ -27,7 +27,7 @@ import musicutils as musicutils
|
|||
import api
|
||||
|
||||
def logMsg(msg, lvl=1):
|
||||
utils.logMsg("%s %s" % ("Emby", "Contextmenu"), msg, lvl)
|
||||
utils.logMsg("%s %s" % ("EMBY", "Contextmenu"), msg, lvl)
|
||||
|
||||
|
||||
#Kodi contextmenu item to configure the emby settings
|
||||
|
@ -127,11 +127,32 @@ if __name__ == '__main__':
|
|||
|
||||
if options[ret] == utils.language(30409):
|
||||
#delete item from the server
|
||||
if xbmcgui.Dialog().yesno("Do you really want to delete this item ?", "This will delete the item from the server and the file(s) from disk!"):
|
||||
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 not resp:
|
||||
logMsg("User skipped deletion for: %s." % embyid, 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, 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()
|
||||
url = "{server}/emby/Items/%s?format=json" % embyid
|
||||
doUtils.downloadUrl(url, type="DELETE")'''
|
||||
|
||||
xbmc.sleep(500)
|
||||
xbmc.executebuiltin("Container.Update")
|
|
@ -112,7 +112,7 @@ class Main:
|
|||
import librarysync
|
||||
lib = librarysync.LibrarySync()
|
||||
if mode == "manualsync":
|
||||
librarysync.ManualSync()
|
||||
librarysync.ManualSync(dialog=True)
|
||||
else:
|
||||
lib.fullSync(repair=True)
|
||||
else:
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
<string id="30022">Advanced</string>
|
||||
<string id="30024">Username</string><!-- Verified -->
|
||||
<string id="30026">Use SIMPLEJSON instead of JSON</string>
|
||||
|
||||
<string id="30030">Port Number</string><!-- Verified -->
|
||||
<string id="30036">Number of recent Movies to show:</string>
|
||||
|
@ -75,7 +74,7 @@
|
|||
<string id="30089">Western</string>
|
||||
|
||||
<string id="30090">Genre Filter</string>
|
||||
<string id="30091">Confirm file delete?</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>
|
||||
|
@ -105,11 +104,8 @@
|
|||
<string id="30120">Show Load Progress</string>
|
||||
<string id="30121">Loading Content</string>
|
||||
<string id="30122">Retrieving Data</string>
|
||||
<string id="30123">Parsing Jason Data</string>
|
||||
<string id="30124">Downloading Jason Data</string>
|
||||
<string id="30125">Done</string>
|
||||
<string id="30126">Processing Item : </string>
|
||||
<string id="30127">YOUCANUSETHIS</string>
|
||||
<string id="30128">Play Error</string>
|
||||
<string id="30129">This item is not playable</string>
|
||||
<string id="30130">Local path detected</string>
|
||||
|
@ -279,14 +275,14 @@
|
|||
<string id="30510">Direct stream music library</string>
|
||||
<string id="30511">Playback Mode</string>
|
||||
<string id="30512">Force artwork caching</string>
|
||||
<string id="30513">Limit artwork cache threads</string>
|
||||
<string id="30513">Limit artwork cache threads (recommended for rpi)</string>
|
||||
<string id="30514">Enable fast startup (requires server plugin)</string>
|
||||
<string id="30515">Maximum items to request from the server at once</string>
|
||||
<string id="30516">Playback</string>
|
||||
<string id="30517">Network credentials</string>
|
||||
<string id="30518">Enable Emby cinema mode</string>
|
||||
<string id="30519">Ask to play trailers</string>
|
||||
<string id="30520">Skip delete confirmation</string>
|
||||
<string id="30520">Skip Emby delete confirmation for the context menu (use at your own risk)</string>
|
||||
<string id="30521">Jump back on resume (in seconds)</string>
|
||||
<string id="30522">Force transcode H265</string>
|
||||
<string id="30523">Music metadata options (not compatible with direct stream)</string>
|
||||
|
@ -300,6 +296,7 @@
|
|||
<string id="30531">Enable new content notification</string>
|
||||
<string id="30532">Duration of the video library pop up (in seconds)</string>
|
||||
<string id="30533">Duration of the music library pop up (in seconds)</string>
|
||||
<string id="30534">Server messages</string>
|
||||
|
||||
<!-- service add-on -->
|
||||
<string id="33000">Welcome</string>
|
||||
|
@ -309,5 +306,30 @@
|
|||
<string id="33004">items added to playlist</string>
|
||||
<string id="33005">items queued to playlist</string>
|
||||
<string id="33006">Server is restarting</string>
|
||||
<string id="33007">Access is enabled</string>
|
||||
<string id="33008">Enter password for user:</string>
|
||||
<string id="33009">Invalid username or password</string>
|
||||
<string id="33010">Failed to authenticate too many times</string>
|
||||
<string id="33011">Unable to direct play</string>
|
||||
<string id="33012">Direct play failed 3 times. Enabled play from HTTP.</string>
|
||||
<string id="33013">Choose the audio stream</string>
|
||||
<string id="33014">Choose the subtitles stream</string>
|
||||
<string id="33015">Delete file from your Emby server?</string>
|
||||
<string id="33016">Play trailers?</string>
|
||||
<string id="33017">Gathering movies from:</string>
|
||||
<string id="33018">Gathering boxsets</string>
|
||||
<string id="33019">Gathering music videos from:</string>
|
||||
<string id="33020">Gathering tv shows from:</string>
|
||||
<string id="33021">Gathering:</string>
|
||||
<string id="33022">Detected the database needs to be recreated for this version of Emby for Kodi. Proceed?</string>
|
||||
<string id="33023">Emby for Kod may not work correctly until the database is reset.</string>
|
||||
<string id="33024">Cancelling the database syncing process. The current Kodi version is unsupported.</string>
|
||||
<string id="33025">completed in:</string>
|
||||
<string id="33026">Comparing movies from:</string>
|
||||
<string id="33027">Comparing boxsets</string>
|
||||
<string id="33028">Comparing music videos from:</string>
|
||||
<string id="33029">Comparing tv shows from:</string>
|
||||
<string id="33030">Comparing episodes from:</string>
|
||||
<string id="33031">Comparing:</string>
|
||||
|
||||
</strings>
|
||||
|
|
|
@ -314,6 +314,17 @@ class Movies(Items):
|
|||
kodicursor.execute("select coalesce(max(idMovie),0) from movie")
|
||||
movieid = kodicursor.fetchone()[0] + 1
|
||||
|
||||
else:
|
||||
# Verification the item is still in Kodi
|
||||
query = "SELECT * FROM movie WHERE idMovie = ?"
|
||||
kodicursor.execute(query, (movieid,))
|
||||
try:
|
||||
kodicursor.fetchone()[0]
|
||||
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)
|
||||
|
||||
if not viewtag or not viewid:
|
||||
# Get view tag from emby
|
||||
viewtag, viewid, mediatype = self.emby.getView_embyId(itemid)
|
||||
|
@ -577,6 +588,17 @@ class MusicVideos(Items):
|
|||
kodicursor.execute("select coalesce(max(idMVideo),0) from musicvideo")
|
||||
mvideoid = kodicursor.fetchone()[0] + 1
|
||||
|
||||
else:
|
||||
# Verification the item is still in Kodi
|
||||
query = "SELECT * FROM musicvideo WHERE idMVideo = ?"
|
||||
kodicursor.execute(query, (mvideoid,))
|
||||
try:
|
||||
kodicursor.fetchone()[0]
|
||||
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)
|
||||
|
||||
if not viewtag or not viewid:
|
||||
# Get view tag from emby
|
||||
viewtag, viewid, mediatype = self.emby.getView_embyId(itemid)
|
||||
|
@ -896,12 +918,38 @@ class TVShows(Items):
|
|||
if not itemid:
|
||||
self.logMsg("Cannot parse XML data for TV show", -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
|
||||
update_item = True
|
||||
force_episodes = False
|
||||
itemid = item['Id']
|
||||
emby_dbitem = emby_db.getItem_byId(itemid)
|
||||
try:
|
||||
showid = emby_dbitem[0]
|
||||
pathid = emby_dbitem[2]
|
||||
except TypeError:
|
||||
update_item = False
|
||||
kodicursor.execute("select coalesce(max(idShow),0) from tvshow")
|
||||
showid = kodicursor.fetchone()[0] + 1
|
||||
|
||||
else:
|
||||
# Verification the item is still in Kodi
|
||||
query = "SELECT * FROM tvshow WHERE idShow = ?"
|
||||
kodicursor.execute(query, (showid,))
|
||||
try:
|
||||
kodicursor.fetchone()[0]
|
||||
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)
|
||||
# Force re-add episodes after the show is re-created.
|
||||
force_episodes = True
|
||||
|
||||
|
||||
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)
|
||||
|
||||
# fileId information
|
||||
checksum = API.getChecksum()
|
||||
|
@ -991,8 +1039,6 @@ class TVShows(Items):
|
|||
pathid = kodi_db.addPath(path)
|
||||
|
||||
# Create the tvshow entry
|
||||
kodicursor.execute("select coalesce(max(idShow),0) from tvshow")
|
||||
showid = kodicursor.fetchone()[0] + 1
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO tvshow(
|
||||
|
@ -1035,15 +1081,13 @@ class TVShows(Items):
|
|||
tags = [viewtag]
|
||||
kodi_db.addTags(showid, tags, "tvshow")
|
||||
|
||||
def refreshSeasonEntry(self, item, showid):
|
||||
API = PlexAPI.API(item)
|
||||
kodicursor = self.kodicursor
|
||||
kodi_db = self.kodi_db
|
||||
# Finally, refresh the all season entry
|
||||
seasonid = kodi_db.addSeason(showid, -1)
|
||||
# Process artwork for season
|
||||
allartworks = API.getAllArtwork()
|
||||
artwork.addArtwork(allartworks, seasonid, "season", kodicursor)
|
||||
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)
|
||||
all_episodes = emby.getEpisodesbyShow(itemid)
|
||||
self.added_episode(all_episodes['Items'], None)
|
||||
|
||||
def add_updateSeason(self, item, showid=None):
|
||||
|
||||
def add_updateSeason(self, item, viewid=None, viewtag=None):
|
||||
API = PlexAPI.API(item)
|
||||
|
@ -1109,6 +1153,17 @@ class TVShows(Items):
|
|||
kodicursor.execute("select coalesce(max(idEpisode),0) from episode")
|
||||
episodeid = kodicursor.fetchone()[0] + 1
|
||||
|
||||
else:
|
||||
# Verification the item is still in Kodi
|
||||
query = "SELECT * FROM episode WHERE idEpisode = ?"
|
||||
kodicursor.execute(query, (episodeid,))
|
||||
try:
|
||||
kodicursor.fetchone()[0]
|
||||
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)
|
||||
|
||||
# fileId information
|
||||
checksum = API.getChecksum()
|
||||
dateadded = API.getDateCreated()
|
||||
|
|
|
@ -299,6 +299,17 @@ class Kodidb_Functions():
|
|||
'''
|
||||
)
|
||||
cursor.execute(query, (actorid, kodiid, mediatype))
|
||||
|
||||
elif "Artist" in type:
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO actor_link(
|
||||
actor_id, media_id, media_type)
|
||||
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (actorid, kodiid, mediatype))
|
||||
# Kodi Helix
|
||||
else:
|
||||
query = ' '.join((
|
||||
|
@ -423,6 +434,17 @@ class Kodidb_Functions():
|
|||
|
||||
cursor.execute(query, (actorid, kodiid))
|
||||
|
||||
elif "Artist" in type:
|
||||
query = (
|
||||
'''
|
||||
INSERT OR REPLACE INTO artistlinkmusicvideo(
|
||||
idArtist, idMVideo)
|
||||
|
||||
VALUES (?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (actorid, kodiid))
|
||||
|
||||
# Add person image to art table
|
||||
if thumb:
|
||||
arttype = type.lower()
|
||||
|
|
|
@ -76,7 +76,7 @@ class KodiMonitor(xbmc.Monitor):
|
|||
kodiid = item['id']
|
||||
type = item['type']
|
||||
except (KeyError, TypeError):
|
||||
self.logMsg("Properties already set for item.", 1)
|
||||
self.logMsg("Item is invalid for playstate update.", 1)
|
||||
else:
|
||||
if ((utils.settings('useDirectPaths') == "1" and not type == "song") or
|
||||
(type == "song" and utils.settings('enableMusic') == "true")):
|
||||
|
@ -159,8 +159,11 @@ class KodiMonitor(xbmc.Monitor):
|
|||
|
||||
|
||||
elif method == "VideoLibrary.OnRemove":
|
||||
|
||||
try:
|
||||
# Removed function, because with plugin paths + clean library, it will wipe
|
||||
# entire library if user has permissions. Instead, use the emby context menu available
|
||||
# in Isengard and higher version
|
||||
pass
|
||||
'''try:
|
||||
kodiid = data['id']
|
||||
type = data['type']
|
||||
except (KeyError, TypeError):
|
||||
|
@ -176,7 +179,7 @@ class KodiMonitor(xbmc.Monitor):
|
|||
except TypeError:
|
||||
self.logMsg("Could not find itemid in emby database.", 1)
|
||||
else:
|
||||
if utils.settings('skipConfirmDelete') != "true":
|
||||
if utils.settings('skipContextMenu') != "true":
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
heading="Confirm delete",
|
||||
line1="Delete file on Emby Server?")
|
||||
|
@ -184,11 +187,12 @@ class KodiMonitor(xbmc.Monitor):
|
|||
self.logMsg("User skipped deletion.", 1)
|
||||
embycursor.close()
|
||||
return
|
||||
|
||||
url = "{server}/emby/Items/%s?format=json" % itemid
|
||||
self.logMsg("Deleting request: %s" % itemid)
|
||||
doUtils.downloadUrl(url, type="DELETE")
|
||||
finally:
|
||||
embycursor.close()
|
||||
embycursor.close()'''
|
||||
|
||||
|
||||
elif method == "System.OnWake":
|
||||
|
|
|
@ -213,7 +213,6 @@ class LibrarySync(Thread):
|
|||
self.__dict__ = self._shared_state
|
||||
|
||||
self.clientInfo = clientinfo.ClientInfo()
|
||||
self.doUtils = downloadutils.DownloadUtils()
|
||||
self.user = userclient.UserClient()
|
||||
self.emby = embyserver.Read_EmbyServer()
|
||||
self.vnodes = videonodes.VideoNodes()
|
||||
|
@ -369,7 +368,6 @@ class LibrarySync(Thread):
|
|||
message = "Repair sync"
|
||||
else:
|
||||
message = "Initial sync"
|
||||
utils.window('emby_initialScan', value="true")
|
||||
# Set new timestamp NOW because sync might take a while
|
||||
self.saveLastSync()
|
||||
starttotal = datetime.now()
|
||||
|
@ -393,8 +391,6 @@ class LibrarySync(Thread):
|
|||
return False
|
||||
else:
|
||||
elapsedTime = datetime.now() - startTime
|
||||
self.logMsg(
|
||||
"SyncDatabase (finished %s in: %s)"
|
||||
% (itemtype, str(elapsedTime).split('.')[0]), 1)
|
||||
|
||||
# Let kodi update the views in any case
|
||||
|
@ -803,7 +799,6 @@ class LibrarySync(Thread):
|
|||
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
|
||||
|
||||
views = emby_db.getView_byType('musicvideos')
|
||||
self.logMsg("Media folders: %s" % views, 1)
|
||||
|
||||
for view in views:
|
||||
|
||||
|
@ -817,7 +812,6 @@ class LibrarySync(Thread):
|
|||
if pdialog:
|
||||
pdialog.update(
|
||||
heading="Emby for Kodi",
|
||||
message="Gathering musicvideos from view: %s..." % viewName)
|
||||
|
||||
# Initial or repair sync
|
||||
all_embymvideos = emby.getMusicVideos(viewId, dialog=pdialog)
|
||||
|
@ -840,7 +834,6 @@ class LibrarySync(Thread):
|
|||
count += 1
|
||||
mvideos.add_update(embymvideo, viewName, viewId)
|
||||
else:
|
||||
self.logMsg("MusicVideos finished.", 2)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -1023,13 +1016,6 @@ class LibrarySync(Thread):
|
|||
viewName,
|
||||
viewId)
|
||||
|
||||
# Remove items from Kodi db?
|
||||
if self.compare:
|
||||
# Manual sync, process deletes
|
||||
with itemtypes.Music() as Music:
|
||||
for item in self.allKodiElementsId:
|
||||
if item not in self.allPlexElementsId:
|
||||
Music.remove(item)
|
||||
|
||||
def compareDBVersion(self, current, minimum):
|
||||
# It returns True is database is up to date. False otherwise.
|
||||
|
@ -1080,13 +1066,9 @@ class LibrarySync(Thread):
|
|||
if (utils.window('emby_dbCheck') != "true" and
|
||||
self.installSyncDone):
|
||||
# Verify the validity of the database
|
||||
currentVersion = utils.settings('dbCreatedWithVersion')
|
||||
minVersion = utils.window('emby_minDBVersion')
|
||||
uptoDate = self.compareDBVersion(currentVersion, minVersion)
|
||||
|
||||
if not uptoDate:
|
||||
self.logMsg(
|
||||
"Db version out of date: %s minimum version required: %s"
|
||||
% (currentVersion, minVersion), 0)
|
||||
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
|
@ -1103,7 +1085,6 @@ class LibrarySync(Thread):
|
|||
else:
|
||||
utils.reset()
|
||||
|
||||
utils.window('emby_dbCheck', value="true")
|
||||
|
||||
if not startupComplete:
|
||||
# Also runs when installed first
|
||||
|
@ -1113,8 +1094,6 @@ class LibrarySync(Thread):
|
|||
# Database does not exists
|
||||
self.logMsg(
|
||||
"The current Kodi version is incompatible "
|
||||
"with the" + self.addonName + " add-on. Please visit "
|
||||
"https://github.com/croneter/PlexKodiConnect "
|
||||
"to know which Kodi versions are supported.", 0)
|
||||
|
||||
xbmcgui.Dialog().ok(
|
||||
|
@ -1132,8 +1111,6 @@ class LibrarySync(Thread):
|
|||
startTime = datetime.now()
|
||||
librarySync = self.fullSync(manualrun=True)
|
||||
elapsedTime = datetime.now() - startTime
|
||||
self.logMsg(
|
||||
"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.
|
||||
|
|
|
@ -34,6 +34,7 @@ class PlaybackUtils():
|
|||
|
||||
self.clientInfo = clientinfo.ClientInfo()
|
||||
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)
|
||||
|
@ -44,7 +45,9 @@ class PlaybackUtils():
|
|||
|
||||
def play(self, itemid, dbid=None):
|
||||
|
||||
self.logMsg("Play called.", 1)
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
settings = utils.settings
|
||||
|
||||
item = self.item
|
||||
# Hack to get only existing entry in PMS response for THIS instance of
|
||||
|
@ -54,6 +57,7 @@ class PlaybackUtils():
|
|||
listitem = xbmcgui.ListItem()
|
||||
playutils = putils.PlayUtils(item[0])
|
||||
|
||||
log("Play called.", 1)
|
||||
playurl = playutils.getPlayUrl()
|
||||
if not playurl:
|
||||
return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
|
||||
|
@ -80,7 +84,7 @@ class PlaybackUtils():
|
|||
sizePlaylist = playlist.size()
|
||||
self.currentPosition = startPos
|
||||
|
||||
propertiesPlayback = utils.window('emby_playbackProps') == "true"
|
||||
propertiesPlayback = window('emby_playbackProps') == "true"
|
||||
introsPlaylist = False
|
||||
dummyPlaylist = False
|
||||
|
||||
|
@ -96,13 +100,13 @@ class PlaybackUtils():
|
|||
# Otherwise we get a loop.
|
||||
if not propertiesPlayback:
|
||||
|
||||
utils.window('emby_playbackProps', value="true")
|
||||
self.logMsg("Setting up properties in playlist.", 1)
|
||||
window('emby_playbackProps', value="true")
|
||||
log("Setting up properties in playlist.", 1)
|
||||
|
||||
if (not homeScreen and not seektime and
|
||||
utils.window('emby_customPlaylist') != "true"):
|
||||
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,8 +120,8 @@ class PlaybackUtils():
|
|||
|
||||
############### -- CHECK FOR INTROS ################
|
||||
|
||||
if (utils.settings('enableCinema') == "true" and not seektime and
|
||||
not utils.window('emby_customPlaylist') == "true"):
|
||||
if (settings('enableCinema') == "true" and not seektime and
|
||||
not window('emby_customPlaylist') == "true"):
|
||||
# if we have any play them when the movie/show is not being resumed
|
||||
xml = PF.GetPlexPlaylist(
|
||||
itemid,
|
||||
|
@ -130,7 +134,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,
|
||||
PF.GetKodiTypeFromPlex(API.getType()))
|
||||
|
@ -146,14 +150,14 @@ class PlaybackUtils():
|
|||
# Playlist items don't fail on their first call - skip them
|
||||
# here, otherwise we'll get two 1st parts
|
||||
if (counter == 0 and
|
||||
utils.window('emby_customPlaylist') == "true"):
|
||||
window('emby_customPlaylist') == "true"):
|
||||
continue
|
||||
# Set listitem and properties for each additional parts
|
||||
API.setPartNumber(counter)
|
||||
additionalListItem = xbmcgui.ListItem()
|
||||
additionalPlayurl = playutils.getPlayUrl(
|
||||
partNumber=counter)
|
||||
self.logMsg("Adding additional part: %s" % counter, 1)
|
||||
log("Adding additional part: %s" % counter, 1)
|
||||
|
||||
self.setProperties(additionalPlayurl, additionalListItem)
|
||||
self.setArtwork(additionalListItem)
|
||||
|
@ -168,42 +172,42 @@ 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)
|
||||
utils.window('emby_playbackProps', clear=True)
|
||||
log("Resetting properties playback flag.", 2)
|
||||
window('emby_playbackProps', clear=True)
|
||||
|
||||
#self.pl.verifyPlaylist()
|
||||
########## SETUP MAIN ITEM ##########
|
||||
|
||||
# For transcoding only, ask for audio/subs pref
|
||||
if utils.window('emby_%s.playmethod' % playurl) == "Transcode":
|
||||
utils.window('emby_%s.playmethod' % playurl, clear=True)
|
||||
if window('emby_%s.playmethod' % playurl) == "Transcode":
|
||||
window('emby_%s.playmethod' % playurl, clear=True)
|
||||
playurl = playutils.audioSubsPref(playurl, listitem)
|
||||
utils.window('emby_%s.playmethod' % playurl, value="Transcode")
|
||||
window('emby_%s.playmethod' % playurl, value="Transcode")
|
||||
|
||||
listitem.setPath(playurl)
|
||||
self.setProperties(playurl, listitem)
|
||||
|
||||
############### PLAYBACK ################
|
||||
|
||||
if homeScreen and seektime and utils.window('emby_customPlaylist') != "true":
|
||||
self.logMsg("Play as a widget item.", 1)
|
||||
if homeScreen and seektime and window('emby_customPlaylist') != "true":
|
||||
log("Play as a widget item.", 1)
|
||||
self.setListItem(listitem)
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
|
||||
|
||||
elif ((introsPlaylist and utils.window('emby_customPlaylist') == "true") or
|
||||
(homeScreen and not sizePlaylist)):
|
||||
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 AddTrailers(self, xml):
|
||||
|
@ -250,29 +254,31 @@ class PlaybackUtils():
|
|||
return True
|
||||
|
||||
def setProperties(self, playurl, listitem):
|
||||
|
||||
window = utils.window
|
||||
# Set all properties necessary for plugin path playback
|
||||
itemid = self.API.getRatingKey()
|
||||
itemtype = self.API.getType()
|
||||
resume, runtime = self.API.getRuntime()
|
||||
|
||||
embyitem = "emby_%s" % playurl
|
||||
utils.window('%s.runtime' % embyitem, value=str(runtime))
|
||||
utils.window('%s.type' % embyitem, value=itemtype)
|
||||
utils.window('%s.itemid' % embyitem, value=itemid)
|
||||
window('%s.runtime' % embyitem, value=str(runtime))
|
||||
window('%s.type' % embyitem, value=itemtype)
|
||||
window('%s.itemid' % embyitem, value=itemid)
|
||||
|
||||
# We need to keep track of playQueueItemIDs for Plex Companion
|
||||
utils.window(
|
||||
window(
|
||||
'plex_%s.playQueueItemID'
|
||||
% playurl, self.API.GetPlayQueueItemID())
|
||||
utils.window(
|
||||
window(
|
||||
'plex_%s.guid'
|
||||
% playurl, self.API.getGuid())
|
||||
|
||||
if itemtype == "episode":
|
||||
utils.window('%s.refreshid' % embyitem,
|
||||
window('%s.refreshid' % embyitem,
|
||||
value=self.API.getParentRatingKey())
|
||||
else:
|
||||
utils.window('%s.refreshid' % embyitem, value=itemid)
|
||||
window('%s.refreshid' % embyitem, value=itemid)
|
||||
|
||||
def externalSubs(self, playurl):
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ class Player(xbmc.Player):
|
|||
self.__dict__ = self._shared_state
|
||||
|
||||
self.clientInfo = clientinfo.ClientInfo()
|
||||
self.doUtils = downloadutils.DownloadUtils()
|
||||
self.doUtils = downloadutils.DownloadUtils().downloadUrl
|
||||
self.xbmcplayer = xbmc.Player()
|
||||
|
||||
self.logMsg("Starting playback monitor.", 2)
|
||||
|
@ -47,7 +47,10 @@ class Player(xbmc.Player):
|
|||
def GetPlayStats(self):
|
||||
return self.playStats
|
||||
|
||||
def onPlayBackStarted( self ):
|
||||
def onPlayBackStarted(self):
|
||||
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
# Will be called when xbmc starts playing a file
|
||||
xbmcplayer = self.xbmcplayer
|
||||
self.stopAll()
|
||||
|
@ -66,7 +69,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
|
||||
|
||||
|
@ -77,42 +80,37 @@ class Player(xbmc.Player):
|
|||
# Save currentFile for cleanup later
|
||||
utils.window('plex_lastPlayedFiled', value=currentFile)
|
||||
# We may need to wait for info to be set in kodi monitor
|
||||
itemId = utils.window("emby_%s.itemid" % currentFile)
|
||||
itemId = window("emby_%s.itemid" % currentFile)
|
||||
tryCount = 0
|
||||
while not itemId:
|
||||
|
||||
xbmc.sleep(200)
|
||||
itemId = utils.window("emby_%s.itemid" % currentFile)
|
||||
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:
|
||||
utils.window('Plex_currently_playing_itemid', value=itemId)
|
||||
self.logMsg("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId), 0)
|
||||
window('Plex_currently_playing_itemid', value=itemId)
|
||||
log("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId), 0)
|
||||
|
||||
# Only proceed if an itemId was found.
|
||||
embyitem = "emby_%s" % currentFile
|
||||
runtime = utils.window("%s.runtime" % embyitem)
|
||||
refresh_id = utils.window("%s.refreshid" % embyitem)
|
||||
playMethod = utils.window("%s.playmethod" % embyitem)
|
||||
itemType = utils.window("%s.type" % embyitem)
|
||||
utils.window('emby_skipWatched%s' % itemId, value="true")
|
||||
|
||||
# Suspend library sync thread while movie is playing
|
||||
self.logMsg("Playing itemtype is: %s" % itemType, 1)
|
||||
customseek = window('emby_customPlaylist.seektime')
|
||||
# Suspend library sync thread while movie is playing
|
||||
log("Playing itemtype is: %s" % itemType, 1)
|
||||
if itemType in ['movie', 'audio']:
|
||||
self.logMsg("Suspending library sync while playing", 1)
|
||||
utils.window('suspend_LibraryThread', value='true')
|
||||
log("Suspending library sync while playing", 1)
|
||||
window('suspend_LibraryThread', value='true')
|
||||
|
||||
if (utils.window('emby_customPlaylist') == "true" and
|
||||
utils.window('emby_customPlaylist.seektime')):
|
||||
if (window('emby_customPlaylist') == "true" and
|
||||
customseek)):
|
||||
# Start at, when using custom playlist (play to Kodi from webclient)
|
||||
seektime = utils.window('emby_customPlaylist.seektime')
|
||||
self.logMsg("Seeking to: %s" % seektime, 1)
|
||||
xbmcplayer.seekTime(int(seektime))
|
||||
utils.window('emby_customPlaylist.seektime', clear=True)
|
||||
log("Seeking to: %s" % customseek, 1)
|
||||
xbmcplayer.seekTime(int(customseek))
|
||||
window('emby_customPlaylist.seektime', clear=True)
|
||||
|
||||
seekTime = xbmcplayer.getTime()
|
||||
|
||||
|
@ -151,9 +149,8 @@ class Player(xbmc.Player):
|
|||
# Get the current audio track and subtitles
|
||||
if playMethod == "Transcode":
|
||||
# property set in PlayUtils.py
|
||||
postdata['AudioStreamIndex'] = utils.window("%sAudioStreamIndex" % currentFile)
|
||||
postdata['SubtitleStreamIndex'] = utils.window("%sSubtitleStreamIndex"
|
||||
% currentFile)
|
||||
postdata['AudioStreamIndex'] = window("%sAudioStreamIndex" % currentFile)
|
||||
postdata['SubtitleStreamIndex'] = window("%sSubtitleStreamIndex" % currentFile)
|
||||
else:
|
||||
# Get the current kodi audio and subtitles and convert to Emby equivalent
|
||||
tracks_query = {
|
||||
|
@ -194,11 +191,11 @@ class Player(xbmc.Player):
|
|||
|
||||
# Number of audiotracks to help get Emby Index
|
||||
audioTracks = len(xbmc.Player().getAvailableAudioStreams())
|
||||
mapping = utils.window("%s.indexMapping" % embyitem)
|
||||
mapping = window("%s.indexMapping" % embyitem)
|
||||
|
||||
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)):
|
||||
|
@ -216,15 +213,15 @@ class Player(xbmc.Player):
|
|||
|
||||
|
||||
# Post playback to server
|
||||
# self.logMsg("Sending POST play started: %s." % postdata, 2)
|
||||
# self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
|
||||
# log("Sending POST play started: %s." % postdata, 2)
|
||||
# self.doUtils(url, postBody=postdata, type="POST")
|
||||
|
||||
# Ensure we do have a runtime
|
||||
try:
|
||||
runtime = int(runtime)
|
||||
except ValueError:
|
||||
runtime = xbmcplayer.getTotalTime()
|
||||
self.logMsg("Runtime is missing, Kodi runtime: %s" % runtime, 1)
|
||||
log("Runtime is missing, Kodi runtime: %s" % runtime, 1)
|
||||
|
||||
playQueueVersion = utils.window(
|
||||
'playQueueVersion')
|
||||
|
@ -249,7 +246,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):
|
||||
|
@ -271,7 +268,7 @@ class Player(xbmc.Player):
|
|||
if not self.doNotify:
|
||||
return
|
||||
|
||||
self.logMsg("reportPlayback Called", 2)
|
||||
log("reportPlayback Called", 2)
|
||||
xbmcplayer = self.xbmcplayer
|
||||
|
||||
# Get current file
|
||||
|
@ -300,7 +297,7 @@ class Player(xbmc.Player):
|
|||
|
||||
"properties": ["volume", "muted"]
|
||||
}
|
||||
}
|
||||
}
|
||||
result = xbmc.executeJSONRPC(json.dumps(volume_query))
|
||||
result = json.loads(result)
|
||||
result = result.get('result')
|
||||
|
@ -389,7 +386,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)):
|
||||
|
@ -410,7 +407,7 @@ class Player(xbmc.Player):
|
|||
# Report progress via websocketclient
|
||||
# postdata = json.dumps(postdata)
|
||||
# self.ws.sendProgressUpdate(postdata)
|
||||
self.doUtils.downloadUrl(
|
||||
self.doUtils(
|
||||
"{server}/:/timeline?" + urlencode(postdata), type="GET")
|
||||
|
||||
def onPlayBackPaused( self ):
|
||||
|
@ -445,13 +442,16 @@ class Player(xbmc.Player):
|
|||
self.reportPlayback()
|
||||
|
||||
def onPlayBackStopped( self ):
|
||||
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
# Will be called when user stops xbmc playing a file
|
||||
self.logMsg("ONPLAYBACK_STOPPED", 2)
|
||||
utils.window('emby_customPlaylist', clear=True)
|
||||
utils.window('emby_customPlaylist.seektime', clear=True)
|
||||
utils.window('emby_playbackProps', clear=True)
|
||||
self.logMsg("Clear playlist properties.", 1)
|
||||
utils.window('suspend_LibraryThread', clear=True)
|
||||
log("ONPLAYBACK_STOPPED", 2)
|
||||
window('emby_customPlaylist', clear=True)
|
||||
window('emby_customPlaylist.seektime', clear=True)
|
||||
window('emby_playbackProps', clear=True)
|
||||
window('suspend_LibraryThread', clear=True)
|
||||
log("Clear playlist properties.", 1)
|
||||
self.stopAll()
|
||||
|
||||
def onPlayBackEnded( self ):
|
||||
|
@ -463,20 +463,24 @@ class Player(xbmc.Player):
|
|||
|
||||
def stopAll(self):
|
||||
|
||||
log = self.logMsg
|
||||
lang = utils.language
|
||||
settings = utils.settings
|
||||
|
||||
doUtils = self.doUtils
|
||||
|
||||
if not self.played_info:
|
||||
return
|
||||
|
||||
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']
|
||||
|
@ -493,9 +497,8 @@ class Player(xbmc.Player):
|
|||
# Runtime is 0.
|
||||
percentComplete = 0
|
||||
|
||||
markPlayedAt = float(utils.settings('markPlayed')) / 100
|
||||
self.logMsg(
|
||||
"Percent complete: %s Mark played at: %s"
|
||||
markPlayedAt = float(settings('markPlayed')) / 100
|
||||
log("Percent complete: %s Mark played at: %s"
|
||||
% (percentComplete, markPlayedAt), 1)
|
||||
|
||||
# Prevent manually mark as watched in Kodi monitor
|
||||
|
@ -504,36 +507,33 @@ class Player(xbmc.Player):
|
|||
self.stopPlayback(data)
|
||||
# Stop transcoding
|
||||
if playMethod == "Transcode":
|
||||
self.logMsg("Transcoding for %s terminated." % itemid, 1)
|
||||
log("Transcoding for %s terminated." % itemid, 1)
|
||||
url = "{server}/video/:/transcode/universal/stop?session=%s" % self.clientInfo.getDeviceId()
|
||||
doUtils.downloadUrl(url, type="GET")
|
||||
doUtils(url, type="GET")
|
||||
|
||||
# Send the delete action to the server.
|
||||
offerDelete = False
|
||||
|
||||
if type == "Episode" and utils.settings('deleteTV') == "true":
|
||||
if type == "Episode" and settings('deleteTV') == "true":
|
||||
offerDelete = True
|
||||
elif type == "Movie" and utils.settings('deleteMovies') == "true":
|
||||
elif type == "Movie" and settings('deleteMovies') == "true":
|
||||
offerDelete = True
|
||||
|
||||
if utils.settings('offerDelete') != "true":
|
||||
if settings('offerDelete') != "true":
|
||||
# Delete could be disabled, even if the subsetting is enabled.
|
||||
offerDelete = False
|
||||
|
||||
# Plex: never delete
|
||||
offerDelete = False
|
||||
if percentComplete >= markPlayedAt and offerDelete:
|
||||
if utils.settings('skipConfirmDelete') != "true":
|
||||
resp = xbmcgui.Dialog().yesno(
|
||||
heading="Confirm delete",
|
||||
line1="Delete file on Emby Server?")
|
||||
if not resp:
|
||||
self.logMsg("User skipped deletion.", 1)
|
||||
continue
|
||||
resp = xbmcgui.Dialog().yesno(lang(30091), lang(33015), autoclose=120000)
|
||||
if not resp:
|
||||
log("User skipped deletion.", 1)
|
||||
continue
|
||||
|
||||
url = "{server}/emby/Items/%s?format=json" % itemid
|
||||
self.logMsg("Deleting request: %s" % itemid)
|
||||
doUtils.downloadUrl(url, type="DELETE")
|
||||
log("Deleting request: %s" % itemid, 1)
|
||||
doUtils(url, type="DELETE")
|
||||
|
||||
# Clean the WINDOW properties
|
||||
for filename in self.played_info:
|
||||
|
@ -567,4 +567,4 @@ class Player(xbmc.Player):
|
|||
'duration': int(duration)
|
||||
}
|
||||
url = url + urlencode(args)
|
||||
self.doUtils.downloadUrl(url, type="GET")
|
||||
self.doUtils(url, type="GET")
|
||||
|
|
|
@ -28,19 +28,26 @@ class Playlist():
|
|||
self.emby = embyserver.Read_EmbyServer()
|
||||
|
||||
def playAll(self, itemids, startat):
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
|
||||
embyconn = utils.kodiSQL('emby')
|
||||
embycursor = embyconn.cursor()
|
||||
emby_db = embydb.Embydb_Functions(embycursor)
|
||||
|
||||
player = xbmc.Player()
|
||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
playlist.clear()
|
||||
|
||||
self.logMsg("---*** PLAY ALL ***---", 1)
|
||||
self.logMsg("Items: %s and start at: %s" % (itemids, startat))
|
||||
log("---*** PLAY ALL ***---", 1)
|
||||
log("Items: %s and start at: %s" % (itemids, startat), 1)
|
||||
|
||||
started = False
|
||||
utils.window('emby_customplaylist', value="true")
|
||||
window('emby_customplaylist', value="true")
|
||||
|
||||
if startat != 0:
|
||||
# Seek to the starting position
|
||||
utils.window('emby_customplaylist.seektime', str(startat))
|
||||
window('emby_customplaylist.seektime', str(startat))
|
||||
|
||||
with embydb.GetEmbyDB() as emby_db:
|
||||
for itemid in itemids:
|
||||
|
@ -67,12 +74,14 @@ class Playlist():
|
|||
|
||||
def modifyPlaylist(self, itemids):
|
||||
|
||||
log = self.logMsg
|
||||
|
||||
embyconn = utils.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)
|
||||
|
@ -90,7 +99,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()
|
||||
|
|
|
@ -37,11 +37,14 @@ class PlayUtils():
|
|||
Returns the playurl for the part with number partNumber
|
||||
(movie might consist of several files)
|
||||
"""
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
|
||||
self.API.setPartNumber(partNumber)
|
||||
playurl = None
|
||||
|
||||
if self.isDirectPlay():
|
||||
self.logMsg("File is direct playing.", 1)
|
||||
log("File is direct playing.", 1)
|
||||
playurl = self.API.getTranscodeVideoPath('DirectPlay')
|
||||
playurl = playurl.encode('utf-8')
|
||||
# Set playmethod property
|
||||
|
@ -55,7 +58,7 @@ class PlayUtils():
|
|||
# utils.window('emby_%s.playmethod' % playurl, "DirectStream")
|
||||
|
||||
elif self.isTranscoding():
|
||||
self.logMsg("File is transcoding.", 1)
|
||||
log("File is transcoding.", 1)
|
||||
quality = {
|
||||
'maxVideoBitrate': self.getBitrate(),
|
||||
'videoResolution': self.getResolution(),
|
||||
|
@ -64,7 +67,7 @@ class PlayUtils():
|
|||
playurl = self.API.getTranscodeVideoPath('Transcode',
|
||||
quality=quality)
|
||||
# Set playmethod property
|
||||
utils.window('emby_%s.playmethod' % playurl, value="Transcode")
|
||||
window('emby_%s.playmethod' % playurl, value="Transcode")
|
||||
|
||||
self.logMsg("The playurl is: %s" % playurl, 1)
|
||||
return playurl
|
||||
|
@ -128,24 +131,26 @@ class PlayUtils():
|
|||
|
||||
def fileExists(self):
|
||||
|
||||
log = self.logMsg
|
||||
|
||||
if 'Path' not in self.item:
|
||||
# File has no path defined in server
|
||||
return False
|
||||
|
||||
# Convert path to direct play
|
||||
path = self.directPlay()
|
||||
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.")
|
||||
log("Failed to find file.", 1)
|
||||
return False
|
||||
|
||||
def h265enabled(self):
|
||||
|
@ -186,7 +191,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
|
||||
|
||||
|
@ -298,6 +303,9 @@ class PlayUtils():
|
|||
return res[chosen]
|
||||
|
||||
def audioSubsPref(self, listitem, url, part=None):
|
||||
log = self.logMsg
|
||||
lang = utils.language
|
||||
dialog = xbmcgui.Dialog()
|
||||
# For transcoding only
|
||||
# Present the list of audio to select from
|
||||
audioStreamsList = []
|
||||
|
@ -365,7 +373,7 @@ class PlayUtils():
|
|||
subNum += 1
|
||||
|
||||
if audioNum > 1:
|
||||
resp = xbmcgui.Dialog().select("Choose the audio stream", audioStreams)
|
||||
resp = dialog.select(lang(33013), audioStreams)
|
||||
if resp > -1:
|
||||
# User selected audio
|
||||
playurlprefs['audioStreamID'] = audioStreamsList[resp]
|
||||
|
@ -378,7 +386,7 @@ class PlayUtils():
|
|||
playurlprefs['audioBoost'] = utils.settings('audioBoost')
|
||||
|
||||
if subNum > 1:
|
||||
resp = xbmcgui.Dialog().select("Choose the subtitle stream", subtitleStreams)
|
||||
resp = dialog.select(lang(33014), subtitleStreams)
|
||||
if resp == 0:
|
||||
# User selected no subtitles
|
||||
playurlprefs["skipSubtitles"] = 1
|
||||
|
|
|
@ -174,6 +174,8 @@ class Read_EmbyServer():
|
|||
|
||||
def getSection(self, parentid, itemtype=None, sortby="SortName", basic=False, dialog=None):
|
||||
|
||||
log = self.logMsg
|
||||
|
||||
doUtils = self.doUtils
|
||||
items = {
|
||||
|
||||
|
@ -199,7 +201,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
|
||||
|
@ -241,27 +243,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, 1)
|
||||
log("Set jump limit to recover: %s" % jump, 2)
|
||||
|
||||
retry = 0
|
||||
while utils.window('emby_online') != "true":
|
||||
# Wait server to come back online
|
||||
if retry == 3:
|
||||
self.logMsg("Unable to reconnect to server. Abort process.", 1)
|
||||
log("Unable to reconnect to server. Abort process.", 1)
|
||||
return
|
||||
|
||||
retry += 1
|
||||
|
@ -289,7 +291,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, type, root=False):
|
||||
|
|
|
@ -73,42 +73,46 @@ class UserClient(threading.Thread):
|
|||
|
||||
def getUserId(self):
|
||||
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
settings = utils.settings
|
||||
|
||||
username = self.getUsername()
|
||||
w_userId = utils.window('emby_userId%s' % username)
|
||||
s_userId = utils.settings('userId%s' % username)
|
||||
w_userId = window('emby_currUser')
|
||||
s_userId = settings('userId%s' % username)
|
||||
|
||||
# Verify the window property
|
||||
if w_userId:
|
||||
if not s_userId:
|
||||
# Save access token if it's missing from settings
|
||||
utils.settings('userId%s' % username, value=w_userId)
|
||||
self.logMsg(
|
||||
"Returning userId from WINDOW for username: %s UserId: %s"
|
||||
settings('userId%s' % username, value=w_userId)
|
||||
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):
|
||||
|
||||
alternate = utils.settings('altip') == "true"
|
||||
settings = utils.settings
|
||||
|
||||
alternate = settings('altip') == "true"
|
||||
if alternate:
|
||||
# Alternate host
|
||||
HTTPS = utils.settings('secondhttps') == "true"
|
||||
host = utils.settings('secondipaddress')
|
||||
port = utils.settings('secondport')
|
||||
HTTPS = settings('secondhttps') == "true"
|
||||
host = settings('secondipaddress')
|
||||
port = settings('secondport')
|
||||
else:
|
||||
# Original host
|
||||
HTTPS = utils.settings('https') == "true"
|
||||
host = utils.settings('ipaddress')
|
||||
port = utils.settings('port')
|
||||
HTTPS = settings('https') == "true"
|
||||
host = settings('ipaddress')
|
||||
port = settings('port')
|
||||
|
||||
server = host + ":" + port
|
||||
|
||||
|
@ -134,33 +138,40 @@ class UserClient(threading.Thread):
|
|||
|
||||
def getToken(self):
|
||||
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
settings = utils.settings
|
||||
|
||||
username = self.getUsername()
|
||||
w_token = utils.window('emby_accessToken%s' % username)
|
||||
s_token = utils.settings('accessToken')
|
||||
userId = self.getUserId()
|
||||
w_token = window('emby_accessToken%s' % userId)
|
||||
s_token = settings('accessToken')
|
||||
|
||||
# Verify the window property
|
||||
if w_token:
|
||||
if not s_token:
|
||||
# Save access token if it's missing from settings
|
||||
utils.settings('accessToken', value=w_token)
|
||||
self.logMsg("Returning accessToken from WINDOW for username: %s "
|
||||
"accessToken: xxxxx" % username, 2)
|
||||
settings('accessToken', value=w_token)
|
||||
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: xxxxx" % username, 2)
|
||||
utils.window('emby_accessToken%s' % username, value=s_token)
|
||||
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
|
||||
s_sslverify = utils.settings('sslverify')
|
||||
if utils.settings('altip') == "true":
|
||||
s_sslverify = utils.settings('secondsslverify')
|
||||
settings = utils.settings
|
||||
|
||||
s_sslverify = settings('sslverify')
|
||||
if settings('altip') == "true":
|
||||
s_sslverify = settings('secondsslverify')
|
||||
|
||||
if s_sslverify == "true":
|
||||
return True
|
||||
|
@ -169,9 +180,11 @@ class UserClient(threading.Thread):
|
|||
|
||||
def getSSL(self):
|
||||
# Client side certificate
|
||||
s_cert = utils.settings('sslcert')
|
||||
if utils.settings('altip') == "true":
|
||||
s_cert = utils.settings('secondsslcert')
|
||||
settings = utils.settings
|
||||
|
||||
s_cert = settings('sslcert')
|
||||
if settings('altip') == "true":
|
||||
s_cert = settings('secondsslcert')
|
||||
|
||||
if s_cert == "None":
|
||||
return None
|
||||
|
@ -204,10 +217,33 @@ class UserClient(threading.Thread):
|
|||
return False
|
||||
|
||||
def hasAccess(self):
|
||||
return True
|
||||
return True
|
||||
# hasAccess is verified in service.py
|
||||
log = self.logMsg
|
||||
window = utils.window
|
||||
|
||||
url = "{server}/emby/Users?format=json"
|
||||
result = self.doUtils.downloadUrl(url)
|
||||
|
||||
if result == False:
|
||||
# Access is restricted, set in downloadutils.py via exception
|
||||
log("Access is restricted.", 1)
|
||||
self.HasAccess = False
|
||||
|
||||
elif window('emby_online') != "true":
|
||||
# Server connection failed
|
||||
pass
|
||||
|
||||
elif window('emby_serverStatus') == "restricted":
|
||||
log("Access is granted.", 1)
|
||||
self.HasAccess = True
|
||||
window('emby_serverStatus', clear=True)
|
||||
xbmcgui.Dialog().notification("Emby for Kodi", utils.language(33007))
|
||||
|
||||
def loadCurrUser(self, authenticated=False):
|
||||
|
||||
window = utils.window
|
||||
|
||||
doUtils = self.doUtils
|
||||
username = self.getUsername()
|
||||
userId = self.getUserId()
|
||||
|
@ -259,6 +295,13 @@ class UserClient(threading.Thread):
|
|||
return True
|
||||
|
||||
def authenticate(self):
|
||||
|
||||
log = self.logMsg
|
||||
lang = utils.language
|
||||
window = utils.window
|
||||
settings = utils.settings
|
||||
dialog = xbmcgui.Dialog()
|
||||
|
||||
# Get /profile/addon_data
|
||||
plx = PlexAPI.PlexAPI()
|
||||
addondir = xbmc.translatePath(self.addon.getAddonInfo('profile')).decode('utf-8')
|
||||
|
@ -270,7 +313,7 @@ class UserClient(threading.Thread):
|
|||
|
||||
# If there's no settings.xml
|
||||
if not hasSettings:
|
||||
self.logMsg("No settings.xml found.", 0)
|
||||
log("No settings.xml found.", 1)
|
||||
self.auth = False
|
||||
return
|
||||
# If no user information
|
||||
|
@ -332,22 +375,20 @@ class UserClient(threading.Thread):
|
|||
self.logMsg("Error: user authentication failed.", -1)
|
||||
utils.settings('accessToken', value="")
|
||||
utils.settings('userId%s' % username, value="")
|
||||
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))
|
||||
|
||||
# Give 3 attempts at entering password / selecting user
|
||||
if self.retry == 3:
|
||||
utils.window('emby_serverStatus', value="Stop")
|
||||
xbmcgui.Dialog().ok(heading=self.addonName,
|
||||
line1="Failed to authenticate too many"
|
||||
"times.",
|
||||
line2="You can retry by resetting attempts"
|
||||
" in the addon settings.")
|
||||
|
||||
self.retry += 1
|
||||
self.auth = False
|
||||
|
||||
def resetClient(self):
|
||||
|
||||
self.logMsg("Reset UserClient authentication.", 1)
|
||||
username = self.getUsername()
|
||||
log = self.logMsg
|
||||
|
||||
utils.settings('accessToken', value="")
|
||||
utils.window('emby_accessToken%s' % username, clear=True)
|
||||
|
@ -359,7 +400,7 @@ class UserClient(threading.Thread):
|
|||
|
||||
def run(self):
|
||||
|
||||
self.logMsg("----===## Starting UserClient ##===----", 0)
|
||||
log("----===## Starting UserClient ##===----", 0)
|
||||
|
||||
while not self.threadStopped():
|
||||
while self.threadSuspended():
|
||||
|
@ -367,7 +408,7 @@ class UserClient(threading.Thread):
|
|||
break
|
||||
xbmc.sleep(3000)
|
||||
|
||||
status = utils.window('emby_serverStatus')
|
||||
status = window('emby_serverStatus')
|
||||
if status:
|
||||
# Verify the connection status to server
|
||||
if status == "restricted":
|
||||
|
@ -376,12 +417,12 @@ class UserClient(threading.Thread):
|
|||
|
||||
elif status == "401":
|
||||
# Unauthorized access, revoke token
|
||||
utils.window('emby_serverStatus', value="Auth")
|
||||
window('emby_serverStatus', value="Auth")
|
||||
self.resetClient()
|
||||
|
||||
if self.auth and (self.currUser is None):
|
||||
# Try to authenticate user
|
||||
status = utils.window('emby_serverStatus')
|
||||
status = window('emby_serverStatus')
|
||||
if not status or status == "Auth":
|
||||
# Set auth flag because we no longer need
|
||||
# to authenticate the user
|
||||
|
@ -393,13 +434,13 @@ class UserClient(threading.Thread):
|
|||
# If authenticate failed.
|
||||
server = self.getServer()
|
||||
username = self.getUsername()
|
||||
status = utils.window('emby_serverStatus')
|
||||
status = window('emby_serverStatus')
|
||||
|
||||
# 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
|
||||
|
||||
self.doUtils.stopSession()
|
||||
|
|
|
@ -17,7 +17,7 @@ import utils
|
|||
class VideoNodes(object):
|
||||
|
||||
def __init__(self):
|
||||
self.kodiversion = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
|
||||
self.kodiversion = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
|
||||
|
||||
def commonRoot(self, order, label, tagname, roottype=1):
|
||||
|
||||
|
@ -63,7 +63,7 @@ class VideoNodes(object):
|
|||
xbmcvfs.exists(path)
|
||||
|
||||
# Create the node directory
|
||||
if not xbmcvfs.exists(nodepath) and not mediatype=="photos":
|
||||
if not xbmcvfs.exists(nodepath) and not mediatype == "photos":
|
||||
# We need to copy over the default items
|
||||
xbmcvfs.mkdirs(nodepath)
|
||||
else:
|
||||
|
@ -90,7 +90,7 @@ class VideoNodes(object):
|
|||
window('Emby.nodes.%s.index' % indexnumber, value=path)
|
||||
|
||||
# Root
|
||||
if not mediatype=="photos":
|
||||
if not mediatype == "photos":
|
||||
root = self.commonRoot(order=0, label=tagname, tagname=tagname, roottype=0)
|
||||
try:
|
||||
utils.indent(root)
|
||||
|
@ -169,7 +169,7 @@ class VideoNodes(object):
|
|||
nodeXML = "%s%s_%s.xml" % (nodepath, cleantagname, nodetype)
|
||||
# Get label
|
||||
stringid = nodes[node]
|
||||
if node != '1':
|
||||
if node != "1":
|
||||
label = utils.language(stringid)
|
||||
if not label:
|
||||
label = xbmc.getLocalizedString(stringid)
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
<setting id="offerDelete" type="bool" label="30114" default="false" />
|
||||
<setting id="deleteTV" type="bool" label="30115" visible="eq(-1,true)" default="false" subsetting="true" />
|
||||
<setting id="deleteMovies" type="bool" label="30116" visible="eq(-2,true)" default="false" subsetting="true" />
|
||||
<setting id="skipConfirmDelete" type="bool" label="30520" visible="eq(-3,true)" default="false" subsetting="true" />
|
||||
<setting id="resumeJumpBack" type="slider" label="30521" default="10" range="0,1,120" option="int" />
|
||||
<setting type="sep" />
|
||||
<setting id="playFromStream" type="bool" label="30002" default="false" />
|
||||
|
@ -72,8 +71,9 @@
|
|||
<category label="30235"><!-- Extras -->
|
||||
<setting id="enableCoverArt" type="bool" label="30157" default="true" />
|
||||
<setting id="ignoreSpecialsNextEpisodes" type="bool" label="30527" default="false" />
|
||||
<setting id="skipContextMenu" type="bool" label="30520" default="false" />
|
||||
<setting id="additionalUsers" type="text" label="30528" default="" />
|
||||
<setting type="sep" />
|
||||
<setting type="lsep" label="30534" />
|
||||
<setting id="connectMsg" type="bool" label="30249" default="true" />
|
||||
<setting id="restartMsg" type="bool" label="30530" default="false" />
|
||||
<setting id="newContent" type="bool" label="30531" default="false" />
|
||||
|
|
14
service.py
14
service.py
|
@ -180,13 +180,13 @@ class Service():
|
|||
else:
|
||||
add = ""
|
||||
xbmcgui.Dialog().notification(
|
||||
heading=self.addonName,
|
||||
message="%s %s%s!"
|
||||
% (lang(33000), user.currUser, add),
|
||||
icon="special://home/addons/plugin.video."
|
||||
"plexkodiconnect/icon.png",
|
||||
time=2000,
|
||||
sound=False)
|
||||
heading=self.addonName,
|
||||
message=("%s %s%s!"
|
||||
% (lang(33000), user.currUser.decode('utf-8'),
|
||||
add.decode('utf-8'))),
|
||||
icon="special://home/addons/plugin.video.emby/icon.png",
|
||||
time=2000,
|
||||
sound=False)
|
||||
|
||||
# Start monitoring kodi events
|
||||
if not self.kodimonitor_running:
|
||||
|
|
Loading…
Reference in a new issue