-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdefault.py
402 lines (352 loc) · 14.3 KB
/
default.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# -*- coding: utf-8 -*-
'''
XBMC Playback Resumer
Copyright (C) 2014 BradVido
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import xbmc
import xbmcaddon
import xbmcgui
import os
import json
from time import time
from random import randint
__addon__ = xbmcaddon.Addon()
__cwd__ = __addon__.getAddonInfo('path')
__scriptname__ = __addon__.getAddonInfo('name')
__version__ = __addon__.getAddonInfo('version')
__icon__ = __addon__.getAddonInfo('icon')
__ID__ = __addon__.getAddonInfo('id')
__language__ = __addon__.getLocalizedString
__profile__ = xbmc.translatePath( __addon__.getAddonInfo('profile') ).decode('utf-8')#addon_data folder
__resource__ = xbmc.translatePath( os.path.join( __cwd__, 'resources', 'lib' ) )
sys.path.append (__resource__)
saveintervalsecs = 60 #default. Configured in settings.
resumeonstartup = False #default. Configured in sttings
autoplayrandom = False #default. Configured in settings
currentPlayingFilePath = '' #The str full path of the video file currently playing
typeOfVideo = 'unknown' #The type of video currently playing (episode, movie, musicvideo, etc.)
libraryId = -1 #The id of the video currently playing (if its in the library)
videoTypesInLibrary={"movies": True, "episodes": True, "musicvideos": True} #init as true. will become false if they are not found
#create the addon_settings dir if not exists
if not os.path.exists(__profile__):
os.makedirs(__profile__)
#two files to persistenly track the last played file and the resume point
lastPlayedTrackerFilePath = os.path.join(__profile__,"lastplayed.txt")
resumePointTrackerFilePath = os.path.join(__profile__,"resumepoint.txt")
def log(msg):
xbmc.log("### [%s] - %s" % (__scriptname__,msg,),level=xbmc.LOGDEBUG )
log( "[%s] - Version: %s Started" % (__scriptname__,__version__))
# helper function to get string type from settings
def getSetting(setting):
return __addon__.getSetting(setting).strip()
# helper function to get bool type from settings
def getSettingAsBool(setting):
return getSetting(setting).lower() == "true"
# check exclusion settings for filename passed as argument
def isExcluded(fullpath):
if not fullpath:
return True
log("isExcluded(): Checking exclusion settings for '%s'." % fullpath)
if (fullpath.find("pvr://") > -1) and getSettingAsBool('ExcludeLiveTV'):
log("isExcluded(): Video is playing via Live TV, which is currently set as excluded location.")
return True
if (fullpath.find("http://") > -1) and getSettingAsBool('ExcludeHTTP'):
log("isExcluded(): Video is playing via HTTP source, which is currently set as excluded location.")
return True
ExcludePath = getSetting('ExcludePath')
if ExcludePath and getSettingAsBool('ExcludePathOption'):
if (fullpath.find(ExcludePath) > -1):
log("isExcluded(): Video is playing from '%s', which is currently set as excluded path 1." % ExcludePath)
return True
ExcludePath2 = getSetting('ExcludePath2')
if ExcludePath2 and getSettingAsBool('ExcludePathOption2'):
if (fullpath.find(ExcludePath2) > -1):
log("isExcluded(): Video is playing from '%s', which is currently set as excluded path 2." % ExcludePath2)
return True
ExcludePath3 = getSetting('ExcludePath3')
if ExcludePath3 and getSettingAsBool('ExcludePathOption3'):
if (fullpath.find(ExcludePath3) > -1):
log("isExcluded(): Video is playing from '%s', which is currently set as excluded path 3." % ExcludePath3)
return True
return False
def updateResumePoint(seconds):
seconds = int(seconds)
global currentPlayingFilePath
global libraryId
if currentPlayingFilePath == '':
log("No valid currentPlayingFilePath found -- not setting resume point")
return
if seconds < 0:#-1 indicates that the video has stopped playing
for i in range(0, 30):#check if xbmc is acually shutting down (abortRequested happens slightly after onPlayBackStopped, hence the sleep/wait/check)
if xbmc.abortRequested:
log("Since XBMC is shutting down, will save resume point")
return#xbmc is shutting down while playing a video. We want to keep the resume point.
if xbmc.Player().isPlaying():
break#a new video has started playing. XBMC is not shutting down
xbmc.sleep(100)
#update the resume point in the file
log("Setting custom resume seconds to %d" % seconds)
f = open(resumePointTrackerFilePath, 'w+')
f.write(str(seconds))
f.close()
#update the native XBMC resume point via JSON-RPC API (Gotham/13+)
#This won't cause trouble on non-gotham, it just won't be successful
try:
xbmc_version = xbmc.getInfoLabel( "System.BuildVersion" )
if int(xbmc_version[:2]) < 13: #e.g. "13.0-ALPHA11 Git:20131231-8eb49b3"
log("Will not update XBMC native resume point because XBMC Version is < 13: "+xbmc_version)
return;
except:
log("Cannot determin XBMC version. Will not update XBMC native resume point!")
return
if libraryId < 0:
log("Will not update XBMC native resume point because the file is not in the library: "+ currentPlayingFilePath)
return;
if (seconds == -2):
log("Will not update XBMC native resume point because the file was stopped normally")
return;
if seconds < 0:
seconds = 0#zero indicates to JSON-RPC to remove the bookmark
log("Setting XBMC native resume point to "+("be removed" if seconds == 0 else str(seconds)+" seconds")+" for "+typeOfVideo +" id "+ str(libraryId))
#determine the JSON-RPC setFooDetails method to use and what the library id name is based of the type of video
method = ''
idname = ''
if typeOfVideo == 'episode':
method = 'SetEpisodeDetails'
idname = 'episodeid'
elif typeOfVideo == 'movie':
method = 'SetMovieDetails'
idname = 'movieid'
else:#music video
method = 'SetMusicVideoDetails'
idname = 'musicvideoid'
#https://github.com/xbmc/xbmc/commit/408ceb032934b3148586500cc3ffd34169118fea
query = {
"jsonrpc": "2.0",
"id": "setResumePoint",
"method": "VideoLibrary."+method,
"params": {
idname: libraryId,
"resume": {
"position": seconds,
#"total": 0 #Not needed: http://forum.xbmc.org/showthread.php?tid=161912&pid=1596436#pid1596436
}
}
}
log("Executing JSON-RPC: " + json.dumps(query))
jsonResponse = json.loads(xbmc.executeJSONRPC(json.dumps(query)))
log("VideoLibrary."+method+" response: "+json.dumps(jsonResponse))
def updatecurrentPlayingFilePath(filepath):
global currentPlayingFilePath
global libraryId
global typeOfVideo
if isExcluded(filepath):
log("Skipping excluded filepath: "+filepath)
currentPlayingFilePath = ''
return
currentPlayingFilePath = filepath
#write the full path to a file for persistant tracking
f = open(lastPlayedTrackerFilePath, 'w+')
f.write(filepath)
f.close()
log('Last played file set to: "%s"' % filepath)
#check if its a library video and get the libraryId and typeOfVideo
query = {
"jsonrpc": "2.0",
"method": "Files.GetFileDetails",
"params": {
"file": filepath,
"media": "video",
"properties": [
"playcount",
"runtime"
]
},
"id": "fileDetailsCheck"
}
log("Executing JSON-RPC: " + json.dumps(query))
jsonResponse = json.loads(xbmc.executeJSONRPC(json.dumps(query)))
log("Files.GetFileDetails response: "+json.dumps(jsonResponse))
typeOfVideo = 'unknown'
try:
typeOfVideo = jsonResponse['result']['filedetails']['type']
except:
libraryId = -1
log("Could not determine type of video; assuming video is not in XBMC's library: "+currentPlayingFilePath)
if typeOfVideo == 'episode' or typeOfVideo == 'movie' or typeOfVideo == 'musicvideo':
libraryId = jsonResponse['result']['filedetails']['id']
log("The libraryid for this "+typeOfVideo+" is " + str(libraryId))
else:
libraryId = -1
log("This type of video is not supported for resume points because it is not in XBMC's library: "+ typeOfVideo +": "+currentPlayingFilePath)
resumePoint = 0
def resumeIfWasPlaying():
global resumeonstartup
global resumePoint
if resumeonstartup and os.path.exists(resumePointTrackerFilePath) and os.path.exists(lastPlayedTrackerFilePath):
f = open(resumePointTrackerFilePath, 'r')
resumePoint = float(f.read())
f.close()
if resumePoint <0:#neg 1 means the video wasn't playing when xbmc ended
log("Not resuming playback because nothing was playing when XBMC ended")
return False
f = open(lastPlayedTrackerFilePath, 'r')
fullPath = f.read()
f.close()
strTimestamp = str(int(resumePoint / 60))+":"+("%02d" % (resumePoint % 60))
log("Will resume playbaRck at "+ strTimestamp+" of "+ fullPath)
try:
xbmc.Player().play(fullPath)
#for i in range(0, 1000):#wait up to 10 secs for the video to start playing befor we try to seek
# if not xbmc.Player().isPlayingVideo() and not xbmc.abortRequested:
# xbmc.sleep(10000)
# else:
# log("main resuming playback at "+ strTimestamp+" of "+ fullPath)
# xbmc.executebuiltin('Notification(Resuming Playback!,At '+strTimestamp+',3000)')
# xbmc.Player().seekTime(resumePoint)
# log("main seeked")
# return True
#log("fallback resuming playback at "+ strTimestamp+" of "+ fullPath)
# xbmc.Player().seekTime(resumePoint)
##log("fallback seekedX")
except:
log("Error in play-->seek")
return True
return False
def getRandomLibraryVideo():
global videoTypesInLibrary
if not videoTypesInLibrary['episodes'] and not videoTypesInLibrary['movies'] and not not videoTypesInLibrary['musicvideos']:
log("No episodes, movies, or music videos exist in the XBMC library. Cannot autoplay a random video")
return
rint = randint(0,2)
if rint == 0:
resultType = 'episodes'
method = "GetEpisodes"
elif rint == 1:
resultType = 'movies'
method = "GetMovies"
elif rint == 2:
resultType = 'musicvideos'
method = "GetMusicVideos"
if not videoTypesInLibrary[resultType]:
return getRandomLibraryVideo()#get a different one
log("Getting next random video from "+ resultType)
query = {
"jsonrpc": "2.0",
"id": "randomLibraryVideo",
"method": "VideoLibrary."+method,
"params": {
"limits": {
"end": 1
},
"sort": {
"method": "random"
},
"properties": [
"file"
]
}
}
log("Executing JSON-RPC: " + json.dumps(query))
jsonResponse = json.loads(xbmc.executeJSONRPC(json.dumps(query)))
log("VideoLibrary."+method+" response: "+json.dumps(jsonResponse))
if jsonResponse['result']['limits']['total'] > 0: #found a video
videoTypesInLibrary[resultType] = True
return jsonResponse['result'][resultType][0]['file']
else: #no videos of this type
log("There are no "+ resultType +" in the library")
videoTypesInLibrary[resultType] = False
return getRandomLibraryVideo()
def autoplayrandomIfEnabled():
if autoplayrandom:
log("autoplayrandom is enabled, will play a new random video now")
videoPlaylist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
#make sure the current playlist has finished completely
if not xbmc.Player().isPlayingVideo() and (videoPlaylist.getposition() == -1 or videoPlaylist.getposition() == videoPlaylist.size()):
fullpath = getRandomLibraryVideo()
log("auto-playing next random video because nothing is playing and playlist is empty: "+fullpath)
xbmc.Player().play(fullpath)
xbmc.executebuiltin('Notification(Auto-playing random video!,'+fullpath+',3000)')
else:
log("Not autoplaying a new random video because we are not at the end of the playlist or something is already playing: currentPosition="+str(videoPlaylist.getposition())+", size=" + str(videoPlaylist.size()))
def loadSettings():
global saveintervalsecs
global resumeonstartup
global autoplayrandom
saveintervalsecs = int(float(__addon__.getSetting("saveintervalsecs")))
resumeonstartup = getSettingAsBool("resumeonstartup")
autoplayrandom = getSettingAsBool("autoplayrandom")
log('Settings loaded! saveintervalsecs=%d, resumeonstartup=%s, autoplayrandom=%s' % (saveintervalsecs, str(resumeonstartup), str(autoplayrandom)))
class MyPlayer( xbmc.Player ):
def __init__( self, *args ):
xbmc.Player.__init__( self )
log('MyPlayer - init')
def onPlayBackPaused( self ):
global g_pausedTime
g_pausedTime = time()
log('Paused. Time: %d' % g_pausedTime)
def onPlayBackEnded( self ):#video ended normally (user didn't stop it)
log("Playback ended")
updateResumePoint(-1)
#global resumePoint
#resumePoint=0
autoplayrandomIfEnabled()
def onPlayBackStopped( self ):
log("Playback stopped")
updateResumePoint(-2)
#global resumePoint
#resumePoint = 0
#autoplayrandomIfEnabled() #if user stopped video, they probably don't want a new random one to start
def onPlayBackSeek( self, time, seekOffset ):
log("Playback seeked (time)")
updateResumePoint(xbmc.Player().getTime())
def onPlayBackSeekChapter( self, chapter ):
log("Playback seeked (chapter)")
updateResumePoint(xbmc.Player().getTime())
def onPlayBackStarted( self ):
log("Playback started")
global saveintervalsecs
global resumePoint
if not xbmc.Player().isPlayingVideo():
log("Not playing a video - skipping: "+xbmc.Player().getPlayingFile())
return
if resumeonstartup and resumePoint > 0:
xbmc.Player().seekTime(resumePoint)
resumePoint = 0
xbmc.sleep(10000)#give it a bit to start playing and let the stopped method finish
updatecurrentPlayingFilePath(xbmc.Player().getPlayingFile())
while xbmc.Player().isPlaying() and not xbmc.abortRequested:
updateResumePoint(xbmc.Player().getTime())
for i in range(0, saveintervalsecs):
if(xbmc.abortRequested or not xbmc.Player().isPlaying()):
return
xbmc.sleep(1000)
try:
class MyMonitor( xbmc.Monitor ):
def __init__( self, *args, **kwargs ):
xbmc.Monitor.__init__( self )
log('MyMonitor - init')
def onSettingsChanged( self ):
loadSettings()
def onAbortRequested(self):
log("Abort Requested!")
xbmc_monitor = MyMonitor()
except:
log('Using Eden API - you need to restart addon for changing settings')
loadSettings()
player_monitor = MyPlayer()
resumedPlayback = resumeIfWasPlaying()
if not resumedPlayback and not xbmc.Player().isPlayingVideo():
autoplayrandomIfEnabled()
while not xbmc.abortRequested:
xbmc.sleep(100)