2015-03-14 08:24:59 +11:00
import xbmcaddon
import xbmcplugin
import xbmc
import xbmcgui
import os
import threading
import json
2015-03-21 07:00:17 +11:00
import inspect
2015-04-29 08:23:26 +10:00
2015-03-14 08:24:59 +11:00
import KodiMonitor
import Utils as utils
2015-04-29 08:23:26 +10:00
2015-03-14 08:24:59 +11:00
from DownloadUtils import DownloadUtils
2015-04-19 03:10:06 +10:00
from WebSocketClient import WebSocketThread
2015-03-14 08:24:59 +11:00
from PlayUtils import PlayUtils
from ClientInformation import ClientInformation
from LibrarySync import LibrarySync
2015-03-28 12:34:09 +11:00
from PlaybackUtils import PlaybackUtils
2015-04-08 00:21:54 +10:00
from ReadEmbyDB import ReadEmbyDB
from API import API
2015-03-14 08:24:59 +11:00
librarySync = LibrarySync ( )
# service class for playback monitoring
class Player ( xbmc . Player ) :
2015-04-29 08:23:26 +10:00
# Borg - multiple instances, shared state
_shared_state = { }
xbmcplayer = xbmc . Player ( )
doUtils = DownloadUtils ( )
clientInfo = ClientInformation ( )
ws = WebSocketThread ( )
addonName = clientInfo . getAddonName ( )
addonId = clientInfo . getAddonId ( )
addon = xbmcaddon . Addon ( id = addonId )
WINDOW = xbmcgui . Window ( 10000 )
2015-03-14 08:24:59 +11:00
logLevel = 0
played_information = { }
settings = None
playStats = { }
def __init__ ( self , * args ) :
2015-04-29 08:23:26 +10:00
self . __dict__ = self . _shared_state
self . logMsg ( " Starting playback monitor service " , 1 )
2015-03-14 08:24:59 +11:00
2015-04-29 08:23:26 +10:00
def logMsg ( self , msg , lvl = 1 ) :
self . className = self . __class__ . __name__
utils . logMsg ( " %s %s " % ( self . addonName , self . className ) , msg , int ( lvl ) )
2015-03-14 08:24:59 +11:00
def hasData ( self , data ) :
if ( data == None or len ( data ) == 0 or data == " None " ) :
return False
else :
return True
def stopAll ( self ) :
2015-04-29 08:23:26 +10:00
self . ws . processPendingActions ( )
2015-03-14 08:24:59 +11:00
if ( len ( self . played_information ) == 0 ) :
return
2015-03-26 04:37:21 +11:00
addonSettings = xbmcaddon . Addon ( id = ' plugin.video.emby ' )
2015-04-29 08:23:26 +10:00
self . logMsg ( " emby Service -> played_information : " + str ( self . played_information ) )
2015-03-14 08:24:59 +11:00
for item_url in self . played_information :
data = self . played_information . get ( item_url )
2015-04-29 08:23:26 +10:00
if ( data is not None ) :
self . logMsg ( " emby Service -> item_url : " + item_url )
self . logMsg ( " emby Service -> item_data : " + str ( data ) )
2015-03-14 08:24:59 +11:00
runtime = data . get ( " runtime " )
currentPosition = data . get ( " currentPosition " )
item_id = data . get ( " item_id " )
refresh_id = data . get ( " refresh_id " )
currentFile = data . get ( " currentfile " )
2015-03-20 04:40:29 +11:00
type = data . get ( " Type " )
2015-03-23 16:24:52 +11:00
2015-03-14 08:24:59 +11:00
if ( currentPosition != None and self . hasData ( runtime ) ) :
runtimeTicks = int ( runtime )
2015-04-29 08:23:26 +10:00
self . logMsg ( " emby Service -> runtimeticks: " + str ( runtimeTicks ) )
2015-03-14 08:24:59 +11:00
percentComplete = ( currentPosition * 10000000 ) / runtimeTicks
markPlayedAt = float ( 90 ) / 100
2015-04-29 08:23:26 +10:00
self . logMsg ( " emby Service -> Percent Complete: " + str ( percentComplete ) + " Mark Played At: " + str ( markPlayedAt ) )
2015-03-14 08:24:59 +11:00
self . stopPlayback ( data )
2015-05-03 00:49:47 +10:00
#if(refresh_id != None):
2015-03-22 14:19:26 +11:00
#report updates playcount and resume status to Kodi and MB3
2015-05-03 00:49:47 +10:00
#librarySync.updatePlayCount(item_id)
2015-04-08 00:21:54 +10:00
2015-03-14 08:24:59 +11:00
self . played_information . clear ( )
# stop transcoding - todo check we are actually transcoding?
clientInfo = ClientInformation ( )
txt_mac = clientInfo . getMachineId ( )
2015-04-29 08:23:26 +10:00
url = " {server} /mediabrowser/Videos/ActiveEncodings "
2015-03-14 08:24:59 +11:00
url = url + ' ?DeviceId= ' + txt_mac
2015-04-29 08:23:26 +10:00
self . doUtils . downloadUrl ( url , type = " DELETE " )
2015-03-14 08:24:59 +11:00
def stopPlayback ( self , data ) :
2015-04-29 08:23:26 +10:00
self . logMsg ( " stopPlayback called " , 2 )
2015-03-14 08:24:59 +11:00
item_id = data . get ( " item_id " )
audioindex = data . get ( " AudioStreamIndex " )
subtitleindex = data . get ( " SubtitleStreamIndex " )
playMethod = data . get ( " playmethod " )
currentPosition = data . get ( " currentPosition " )
2015-04-29 08:23:26 +10:00
positionTicks = int ( currentPosition * 10000000 )
url = " {server} /mediabrowser/Sessions/Playing/Stopped "
2015-04-14 04:56:36 +10:00
2015-04-29 08:23:26 +10:00
postdata = {
' QueueableMediaTypes ' : " Video " ,
' CanSeek ' : True ,
' ItemId ' : item_id ,
' MediaSourceId ' : item_id ,
' PlayMethod ' : playMethod ,
' PositionTicks ' : positionTicks
}
2015-04-14 04:56:36 +10:00
2015-04-29 08:23:26 +10:00
if audioindex :
postdata [ ' AudioStreamIndex ' ] = audioindex
2015-03-14 08:24:59 +11:00
2015-04-29 08:23:26 +10:00
if subtitleindex :
postdata [ ' SubtitleStreamIndex ' ] = subtitleindex
2015-03-14 08:24:59 +11:00
2015-04-29 08:23:26 +10:00
self . doUtils . downloadUrl ( url , postBody = postdata , type = " POST " )
2015-03-14 08:24:59 +11:00
def reportPlayback ( self ) :
2015-04-29 08:23:26 +10:00
self . logMsg ( " reportPlayback Called " , 2 )
xbmcplayer = self . xbmcplayer
2015-05-04 02:26:04 +10:00
if not xbmcplayer . isPlaying ( ) :
self . logMsg ( " reportPlayback: Not playing anything so returning " , 0 )
return
2015-04-29 08:23:26 +10:00
currentFile = xbmcplayer . getPlayingFile ( )
2015-03-14 08:24:59 +11:00
data = self . played_information . get ( currentFile )
2015-04-29 08:23:26 +10:00
2015-03-26 04:37:21 +11:00
# only report playback if emby has initiated the playback (item_id has value)
2015-04-29 08:23:26 +10:00
if ( data is not None ) and ( data . get ( " item_id " ) is not None ) :
# Get playback information
2015-03-14 08:24:59 +11:00
item_id = data . get ( " item_id " )
audioindex = data . get ( " AudioStreamIndex " )
subtitleindex = data . get ( " SubtitleStreamIndex " )
2015-04-29 08:23:26 +10:00
playTime = data . get ( " currentPosition " )
2015-03-14 08:24:59 +11:00
playMethod = data . get ( " playmethod " )
paused = data . get ( " paused " )
2015-04-29 08:23:26 +10:00
if paused is None :
paused = False
2015-03-14 08:24:59 +11:00
2015-04-29 08:23:26 +10:00
#url = "{server}/mediabrowser/Sessions/Playing/Progress"
postdata = {
' QueueableMediaTypes ' : " Video " ,
' CanSeek ' : True ,
' ItemId ' : item_id ,
' MediaSourceId ' : item_id ,
' IsPaused ' : paused ,
' PlayMethod ' : playMethod
}
if playTime :
postdata [ ' PositionTicks ' ] = int ( playTime * 10000000 )
if audioindex :
postdata [ ' AudioStreamIndex ' ] = audioindex
if subtitleindex :
postdata [ ' SubtitleStreamIndex ' ] = subtitleindex
postdata = json . dumps ( postdata )
2015-05-03 16:06:44 +10:00
self . logMsg ( " Report: %s " % postdata , 2 )
2015-04-29 08:23:26 +10:00
self . ws . sendProgressUpdate ( postdata )
2015-03-14 08:24:59 +11:00
def onPlayBackPaused ( self ) :
currentFile = xbmc . Player ( ) . getPlayingFile ( )
2015-04-29 08:23:26 +10:00
self . logMsg ( " PLAYBACK_PAUSED : " + currentFile , 2 )
2015-03-14 08:24:59 +11:00
if ( self . played_information . get ( currentFile ) != None ) :
self . played_information [ currentFile ] [ " paused " ] = " true "
self . reportPlayback ( )
def onPlayBackResumed ( self ) :
currentFile = xbmc . Player ( ) . getPlayingFile ( )
2015-04-29 08:23:26 +10:00
self . logMsg ( " PLAYBACK_RESUMED : " + currentFile , 2 )
2015-03-14 08:24:59 +11:00
if ( self . played_information . get ( currentFile ) != None ) :
self . played_information [ currentFile ] [ " paused " ] = " false "
self . reportPlayback ( )
def onPlayBackSeek ( self , time , seekOffset ) :
2015-04-29 08:23:26 +10:00
self . logMsg ( " PLAYBACK_SEEK " , 2 )
2015-03-14 08:24:59 +11:00
self . reportPlayback ( )
def onPlayBackStarted ( self ) :
# Will be called when xbmc starts playing a file
2015-04-29 08:23:26 +10:00
WINDOW = self . WINDOW
xbmcplayer = self . xbmcplayer
2015-03-14 08:24:59 +11:00
self . stopAll ( )
2015-03-23 00:02:38 +11:00
if xbmcplayer . isPlaying ( ) :
2015-05-03 02:36:06 +10:00
currentFile = " "
try :
currentFile = xbmcplayer . getPlayingFile ( )
except : pass
2015-04-29 08:23:26 +10:00
self . logMsg ( " onPlayBackStarted: %s " % currentFile , 0 )
2015-03-30 21:01:26 +11:00
# we may need to wait until the info is available
item_id = WINDOW . getProperty ( currentFile + " item_id " )
tryCount = 0
while ( item_id == None or item_id == " " ) :
xbmc . sleep ( 500 )
item_id = WINDOW . getProperty ( currentFile + " item_id " )
tryCount + = 1
if ( tryCount == 20 ) : # try 20 times or about 10 seconds
return
xbmc . sleep ( 500 )
2015-03-14 08:24:59 +11:00
# grab all the info about this item from the stored windows props
# only ever use the win props here, use the data map in all other places
runtime = WINDOW . getProperty ( currentFile + " runtimeticks " )
refresh_id = WINDOW . getProperty ( currentFile + " refresh_id " )
audioindex = WINDOW . getProperty ( currentFile + " AudioStreamIndex " )
subtitleindex = WINDOW . getProperty ( currentFile + " SubtitleStreamIndex " )
playMethod = WINDOW . getProperty ( currentFile + " playmethod " )
itemType = WINDOW . getProperty ( currentFile + " type " )
seekTime = WINDOW . getProperty ( currentFile + " seektime " )
2015-04-29 08:23:26 +10:00
username = WINDOW . getProperty ( ' currUser ' )
sessionId = WINDOW . getProperty ( ' sessionId %s ' % username )
2015-03-14 08:24:59 +11:00
if seekTime != " " :
2015-03-28 12:34:09 +11:00
PlaybackUtils ( ) . seekToPosition ( int ( seekTime ) )
2015-03-14 08:24:59 +11:00
2015-04-29 08:23:26 +10:00
if ( not item_id ) or ( len ( item_id ) == 0 ) :
self . logMsg ( " onPlayBackStarted: No info for current playing file " , 0 )
2015-03-14 08:24:59 +11:00
return
2015-04-14 04:56:36 +10:00
2015-04-29 08:23:26 +10:00
url = " {server} /mediabrowser/Sessions/Playing "
postdata = {
' QueueableMediaTypes ' : " Video " ,
' CanSeek ' : True ,
' ItemId ' : item_id ,
' MediaSourceId ' : item_id ,
' PlayMethod ' : playMethod
}
2015-04-14 04:56:36 +10:00
2015-04-29 08:23:26 +10:00
if audioindex :
postdata [ ' AudioStreamIndex ' ] = audioindex
2015-03-14 08:24:59 +11:00
2015-04-29 08:23:26 +10:00
if subtitleindex :
postdata [ ' SubtitleStreamIndex ' ] = subtitleindex
2015-03-14 08:24:59 +11:00
2015-04-29 08:23:26 +10:00
self . logMsg ( " Sending POST play started. " , 1 )
#self.logMsg("emby Service -> Sending Post Play Started : " + url, 0)
self . doUtils . downloadUrl ( url , postBody = postdata , type = " POST " )
2015-03-22 14:19:26 +11:00
2015-03-14 08:24:59 +11:00
# save data map for updates and position calls
data = { }
data [ " runtime " ] = runtime
data [ " item_id " ] = item_id
data [ " refresh_id " ] = refresh_id
2015-03-23 16:24:52 +11:00
data [ " currentfile " ] = currentFile
2015-03-14 08:24:59 +11:00
data [ " AudioStreamIndex " ] = audioindex
data [ " SubtitleStreamIndex " ] = subtitleindex
data [ " playmethod " ] = playMethod
data [ " Type " ] = itemType
2015-03-23 16:24:52 +11:00
self . played_information [ currentFile ] = data
2015-03-14 08:24:59 +11:00
2015-04-29 08:23:26 +10:00
self . logMsg ( " emby Service -> ADDING_FILE : " + currentFile , 0 )
self . logMsg ( " emby Service -> ADDING_FILE : " + str ( self . played_information ) , 0 )
2015-03-14 08:24:59 +11:00
# log some playback stats
if ( itemType != None ) :
if ( self . playStats . get ( itemType ) != None ) :
count = self . playStats . get ( itemType ) + 1
self . playStats [ itemType ] = count
else :
self . playStats [ itemType ] = 1
if ( playMethod != None ) :
if ( self . playStats . get ( playMethod ) != None ) :
count = self . playStats . get ( playMethod ) + 1
self . playStats [ playMethod ] = count
else :
self . playStats [ playMethod ] = 1
# reset in progress position
self . reportPlayback ( )
def GetPlayStats ( self ) :
return self . playStats
def onPlayBackEnded ( self ) :
# Will be called when xbmc stops playing a file
2015-04-29 08:23:26 +10:00
self . logMsg ( " onPlayBackEnded " , 0 )
2015-03-22 14:19:26 +11:00
#workaround when strm files are launched through the addon - mark watched when finished playing
#TODO --> mark watched when 95% is played of the file
WINDOW = xbmcgui . Window ( 10000 )
if WINDOW . getProperty ( " virtualstrm " ) != " " :
try :
id = WINDOW . getProperty ( " virtualstrm " )
type = WINDOW . getProperty ( " virtualstrmtype " )
2015-04-29 08:23:26 +10:00
watchedurl = " {server} /mediabrowser/Users/ {UserId} /PlayedItems/ %s " % id
self . doUtils . downloadUrl ( watchedurl , postBody = " " , type = " POST " )
2015-04-18 12:28:39 +10:00
librarySync . updatePlayCount ( id )
2015-03-22 14:19:26 +11:00
except : pass
WINDOW . clearProperty ( " virtualstrm " )
2015-03-14 08:24:59 +11:00
self . stopAll ( )
def onPlayBackStopped ( self ) :
# Will be called when user stops xbmc playing a file
2015-04-29 08:23:26 +10:00
self . logMsg ( " onPlayBackStopped " , 0 )
2015-03-14 08:24:59 +11:00
self . stopAll ( )
2015-04-09 22:29:48 +10:00
def autoPlayPlayback ( self ) :
currentFile = xbmc . Player ( ) . getPlayingFile ( )
data = self . played_information . get ( currentFile )
# only report playback if emby has initiated the playback (item_id has value)
if ( data != None and data . get ( " item_id " ) != None ) :
addonSettings = xbmcaddon . Addon ( id = ' plugin.video.emby ' )
item_id = data . get ( " item_id " )
type = data . get ( " Type " )
# if its an episode see if autoplay is enabled
if addonSettings . getSetting ( " autoPlaySeason " ) == " true " and type == " Episode " :
2015-04-14 04:56:36 +10:00
WINDOW = xbmcgui . Window ( 10000 )
username = WINDOW . getProperty ( ' currUser ' )
userid = WINDOW . getProperty ( ' userId %s ' % username )
server = WINDOW . getProperty ( ' server %s ' % username )
2015-04-09 22:29:48 +10:00
# add remaining unplayed episodes if applicable
MB3Episode = ReadEmbyDB ( ) . getItem ( item_id )
userData = MB3Episode [ " UserData " ]
if userData != None and userData [ " Played " ] == True :
pDialog = xbmcgui . DialogProgress ( )
seasonId = MB3Episode [ " SeasonId " ]
2015-04-29 08:23:26 +10:00
url = " {server} /mediabrowser/Users/ {UserId} /Items?ParentId= %s &ImageTypeLimit=1&Limit=1&SortBy=SortName&SortOrder=Ascending&Filters=IsUnPlayed&IncludeItemTypes=Episode&IsVirtualUnaired=false&Recursive=true&IsMissing=False&format=json " % seasonId
jsonData = self . doUtils . downloadUrl ( url )
2015-05-04 02:26:04 +10:00
2015-04-09 22:29:48 +10:00
if ( jsonData != " " ) :
2015-05-04 02:26:04 +10:00
seasonData = jsonData
2015-04-09 22:29:48 +10:00
if seasonData . get ( " Items " ) != None :
item = seasonData . get ( " Items " ) [ 0 ]
pDialog . create ( " Auto Play next episode " , str ( item . get ( " ParentIndexNumber " ) ) + " x " + str ( item . get ( " IndexNumber " ) ) + " . " + item [ " Name " ] + " found " , " Cancel to stop automatic play " )
count = 0
while ( pDialog . iscanceled ( ) == False and count < 10 ) :
xbmc . sleep ( 1000 )
count + = 1
progress = count * 10
remainingsecs = 10 - count
pDialog . update ( progress , str ( item . get ( " ParentIndexNumber " ) ) + " x " + str ( item . get ( " IndexNumber " ) ) + " . " + item [ " Name " ] + " found " , " Cancel to stop automatic play " , str ( remainingsecs ) + " second(s) until auto dismiss " )
pDialog . close ( )
if pDialog . iscanceled ( ) == False :
playTime = xbmc . Player ( ) . getTime ( )
totalTime = xbmc . Player ( ) . getTotalTime ( )
while xbmc . Player ( ) . isPlaying ( ) and ( totalTime - playTime > 2 ) :
xbmc . sleep ( 500 )
playTime = xbmc . Player ( ) . getTime ( )
totalTime = xbmc . Player ( ) . getTotalTime ( )
2015-05-04 02:26:04 +10:00
2015-04-14 04:56:36 +10:00
PlaybackUtils ( ) . PLAYAllEpisodes ( seasonData . get ( " Items " ) )