-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathservice.py
376 lines (317 loc) · 17 KB
/
service.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
# -*- coding: utf-8 -*-
import sys
import os
import traceback
import xbmc
import xbmcvfs
import xbmcaddon
import xbmcgui
# Add JSON support for queries
if sys.version_info < (2, 7):
import simplejson
else:
import json as simplejson
# Import the common settings
from resources.lib.settings import Settings
from resources.lib.settings import log
from resources.lib.settings import os_path_join
from resources.lib.settings import dir_exists
# Load the core Video Extras classes
from resources.lib.core import VideoExtrasBase
# Load the cache cleaner
from resources.lib.CacheCleanup import CacheCleanup
ADDON = xbmcaddon.Addon(id='script.videoextras')
CWD = ADDON.getAddonInfo('path').decode("utf-8")
PROFILE_DIR = xbmc.translatePath(ADDON.getAddonInfo('profile')).decode("utf-8")
RES_DIR = xbmc.translatePath(os.path.join(CWD, 'resources').encode("utf-8")).decode("utf-8")
#####################################
# Main class for the Extras Service
#####################################
class VideoExtrasService():
LIST_TAG = "_list"
def __init__(self):
# special://skin - This path points to the currently active skin's root directory.
skinExtrasOverlayBase = xbmc.translatePath("special://skin").decode("utf-8")
skinExtrasOverlayBase = os_path_join(skinExtrasOverlayBase, "media")
# Now check to see if the user has defines a different overlay image in the
# settings, as that is the main one that will be used
self.skinExtrasOverlay = Settings.getCustomOverlayImage()
self.skinExtrasOverlayList = Settings.getCustomListImage()
# Next check the skin specific overlay
if self.skinExtrasOverlay in [None, '']:
self.skinExtrasOverlay = os_path_join(skinExtrasOverlayBase, "videoextras_overlay.png")
if self.skinExtrasOverlayList in [None, '']:
self.skinExtrasOverlayList = os_path_join(skinExtrasOverlayBase, "videoextras_overlay_list.png")
log("VideoExtrasService: Looking for image overlay file: %s" % self.skinExtrasOverlay)
if not xbmcvfs.exists(self.skinExtrasOverlay):
log("VideoExtrasService: No custom image, using default")
# Add default image setting to skinExtrasOverlay
self.skinExtrasOverlay = os_path_join(RES_DIR, "skins")
self.skinExtrasOverlay = os_path_join(self.skinExtrasOverlay, "icons")
self.skinExtrasOverlay = os_path_join(self.skinExtrasOverlay, "overlay1.png")
log("VideoExtrasService: Looking for list image overlay file: %s" % self.skinExtrasOverlayList)
if not xbmcvfs.exists(self.skinExtrasOverlayList):
log("VideoExtrasService: No custom wide image, using default")
# Add default image setting to skinExtrasOverlay
self.skinExtrasOverlayList = os_path_join(RES_DIR, "skins")
self.skinExtrasOverlayList = os_path_join(self.skinExtrasOverlayList, "icons")
self.skinExtrasOverlayList = os_path_join(self.skinExtrasOverlayList, "list1.png")
self.forceOverlayOverwrite = False
# We now know the file that we are going to use for the overlay
# Check to see if this is different from the last overlay file used
filename = os_path_join(PROFILE_DIR, "overlay_image_used.txt")
try:
previousOverlay = None
if xbmcvfs.exists(filename):
fileHandle = xbmcvfs.File(filename, 'r')
previousOverlay = fileHandle.read()
fileHandle.close()
# Check if the overlay has changed
if self.skinExtrasOverlay != previousOverlay:
self.forceOverlayOverwrite = True
# Update the record of the file we are now using
if xbmcvfs.exists(filename):
xbmcvfs.delete(filename)
fileHandle = xbmcvfs.File(filename, 'w')
fileHandle.write(self.skinExtrasOverlay.encode("UTF-8"))
fileHandle.close()
except:
log("VideoExtrasService: Failed to write: %s" % filename, xbmc.LOGERROR)
log("VideoExtrasService: %s" % traceback.format_exc(), xbmc.LOGERROR)
# Regenerates all of the cached extras
def cacheAllExtras(self):
if not (xbmc.abortRequested or xbmc.getCondVisibility("Window.IsVisible(shutdownmenu)")):
self.createExtrasCache('GetMovies', Settings.MOVIES, 'movieid')
if not (xbmc.abortRequested or xbmc.getCondVisibility("Window.IsVisible(shutdownmenu)")):
self.createExtrasCache('GetTVShows', Settings.TVSHOWS, 'tvshowid')
if not (xbmc.abortRequested or xbmc.getCondVisibility("Window.IsVisible(shutdownmenu)")):
self.createExtrasCache('GetMusicVideos', Settings.MUSICVIDEOS, 'musicvideoid')
# Checks all the given movies/TV/music videos to see if they have any extras
# and if they do, then cretaes a cached file containing the titles of the video
# that owns them
def createExtrasCache(self, jsonGet, target, dbid):
log("VideoExtrasService: Creating cache for %s" % target)
json_query = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.%s", "params": { "properties": ["title", "file"] }, "id": 1}' % jsonGet)
json_query = unicode(json_query, 'utf-8', errors='ignore')
json_query = simplejson.loads(json_query)
extrasCacheString = ""
if ("result" in json_query) and (target in json_query['result']):
# Get the list of movies paths from the movie list returned
items = json_query['result'][target]
for item in items:
# Check to see if exit has been called, if so stop
if xbmc.abortRequested or xbmc.getCondVisibility("Window.IsVisible(shutdownmenu)"):
return
log("VideoExtrasService: %s detected: %s = %s" % (target, item['title'], item['file']))
videoExtras = VideoExtrasBase(item['file'], target, item['title'])
# Only checking for the existence of extras - no need for DB or default Fanart
firstExtraFile = videoExtras.findExtras(True)
del videoExtras
# Check if any extras exist for this movie
if firstExtraFile:
log("VideoExtrasService: Extras found for (%d) %s" % (item[dbid], item['title']))
extrasCacheString = ("%s[%d]%s" % (extrasCacheString, item[dbid], os.linesep))
# Add the overlay image for this item
self._createOverlayFile(target, item[dbid], self.skinExtrasOverlay)
self._createOverlayFile(target, item[dbid], self.skinExtrasOverlayList, VideoExtrasService.LIST_TAG)
else:
# No extras so remove the file if it exists
self._removeOverlayFile(target, item[dbid])
self._removeOverlayFile(target, item[dbid], VideoExtrasService.LIST_TAG)
# Calculates where a given overlay file should be
def _createTargetPath(self, target, dbid, postfix=''):
# Get the path where the file exists
rootPath = os_path_join(PROFILE_DIR, target)
if not dir_exists(rootPath):
# Directory does not exist yet, create one
xbmcvfs.mkdirs(rootPath)
# Generate the name of the file that the overlay will be copied to
targetFile = os_path_join(rootPath, ("%d%s.png" % (dbid, postfix)))
return targetFile
# Creates the overlay file in the expected location
def _createOverlayFile(self, target, dbid, srcfile, postfix=''):
# Generate the name of the file that the overlay will be copied to
targetFile = self._createTargetPath(target, dbid, postfix)
# Check if the file exists
if xbmcvfs.exists(targetFile) and not self.forceOverlayOverwrite:
return
try:
# Now the path exists, need to copy the file over to it, giving it the name of the DBID
xbmcvfs.copy(srcfile, targetFile)
except:
log("VideoExtrasService: Failed to create file: %s" % targetFile, xbmc.LOGERROR)
log("VideoExtrasService: %s" % traceback.format_exc(), xbmc.LOGERROR)
# Removes an overlay
def _removeOverlayFile(self, target, dbid, postfix=''):
# Generate the name of the file that the overlay will be removed from
targetFile = self._createTargetPath(target, dbid, postfix)
if xbmcvfs.exists(targetFile):
try:
# Now the path exists, need to copy the file over to it, giving it the name of the DBID
xbmcvfs.delete(targetFile)
except:
log("VideoExtrasService: Failed to delete file: %s" % targetFile, xbmc.LOGERROR)
log("VideoExtrasService: %s" % traceback.format_exc(), xbmc.LOGERROR)
# Check theYouTube settings are correct, we will automatically disable the
# option if the YouTube addon is not installed
def checkYouTubeSettings():
# Check to see if the YouTube support is enabled
if Settings.isYouTubeSearchSupportEnabled():
# It is enabled in settings, but we should check to ensure that the
# YouTube addon is actually installed
youtubeInstalled = False
try:
youtubeAddon = xbmcaddon.Addon(id='plugin.video.youtube')
if youtubeAddon not in [None, ""]:
youtubeInstalled = True
except:
# We will get an exception if we can not find the YouTube addon
youtubeInstalled = False
if not youtubeInstalled:
# There is no YouTube addon installed, so disable this option in settings
log("VideoExtrasService: Disabling YouTube support as addon not installed")
Settings.disableYouTubeSearchSupport()
# Check Vimeo settings are correct, we will automatically disable the
# option if the Vimeo addon is not installed
def checkVimeoSettings():
# Check to see if the Vimeo support is enabled
if Settings.isVimeoSearchSupportEnabled():
# It is enabled in settings, but we should check to ensure that the
# Vimeo addon is actually installed
vimeoInstalled = False
try:
vimeoAddon = xbmcaddon.Addon(id='plugin.video.vimeo')
if vimeoAddon not in [None, ""]:
vimeoInstalled = True
except:
# We will get an exception if we can not find the Vimeo addon
vimeoInstalled = False
if not vimeoInstalled:
# There is no Vimeo addon installed, so disable this option in settings
log("VideoExtrasService: Disabling Vimeo support as addon not installed")
Settings.disableVimeoSearchSupport()
# Monitor the video player for completed videos
class VideoExtrasPlayerMonitor(xbmc.Player):
def onPlayBackStopped(self):
self.checkIfVideoExtrasDisplay()
def onPlayBackEnded(self):
self.checkIfVideoExtrasDisplay()
def checkIfVideoExtrasDisplay(self):
# Check if the item that was played was a movie
if xbmc.getInfoLabel("ListItem.dbtype") != 'movie':
log("VideoExtrasPlayerMonitor: Was not a movie playing")
return
dbid = xbmc.getInfoLabel("ListItem.DBID")
if dbid in [None, ""]:
log("VideoExtrasPlayerMonitor: No DBID")
return
# Get the details for the extras
title = xbmc.getInfoLabel("ListItem.Title")
file = xbmc.getInfoLabel("ListItem.FilenameAndPath")
if file in [None, ""]:
file = xbmc.getInfoLabel("ListItem.Path")
if file in [None, ""]:
log("VideoExtrasPlayerMonitor: Unable to find playing file")
return
log("VideoExtrasPlayerMonitor: searching for: %s = %s" % (title, file))
videoExtras = VideoExtrasBase(file, Settings.MOVIES, title)
# Only checking for the existence of extras - no need for DB or default Fanart
firstExtraFile = videoExtras.findExtras(True)
del videoExtras
if not firstExtraFile:
log("VideoExtrasPlayerMonitor: No extras for %s" % file)
return
# So there are extras, so now check to see if the movie was actually
# completely viewed, we only want to display the extras for the movie
# if the whole thing was viewed
# It can take a little while for the database to be updated, so we need
# to keep trying for a little while (3 seconds)
playcount = None
resumePosition = None
lastResumeValue = None
i = 30
while (i > 0) and (not xbmc.abortRequested):
json_query = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovieDetails", "params": {"movieid":%s, "properties": ["playcount", "resume"] }, "id": 1}' % str(dbid))
json_query = unicode(json_query, 'utf-8', errors='ignore')
json_query = simplejson.loads(json_query)
if ("result" in json_query) and ('moviedetails' in json_query['result']):
# Get the movie details from the response
movieDetails = json_query['result']['moviedetails']
if movieDetails in [None, ""]:
return
log("VideoExtrasPlayerMonitor: Database details: %s" % str(movieDetails))
# Get the playcount
playcount = movieDetails['playcount']
if playcount not in [None, "", 0, "0"]:
# As well as the playcount, we want to check if there is any resume data
resumePosition = movieDetails['resume']['position']
# May take a little while for the resume to be updated, so we can wait for either
# it changing or the timeout expires
if lastResumeValue in [None, ""]:
lastResumeValue = resumePosition
elif lastResumeValue != resumePosition:
if resumePosition not in ["0", 0]:
playcount = None
break
i = i - 1
xbmc.sleep(100)
if (playcount in [None, "", 0, "0"]) or (resumePosition not in [None, "0", 0]):
log("VideoExtrasPlayerMonitor: Movie was not completed, no need to show extras")
return
# If we reach here, then we should show the extras
log("VideoExtras: Showing extras for %s" % file)
cmd = 'RunScript(script.videoextras,display,"%s")' % file
xbmc.executebuiltin(cmd)
###################################
# Main of the Video Extras Service
###################################
if __name__ == '__main__':
log("VideoExtrasService: Starting service (version %s)" % ADDON.getAddonInfo('version'))
# Record if the Context menu should be displayed
if Settings.showOnContextMenu():
xbmcgui.Window(10025).setProperty("VideoExtrasShowContextMenu", "true")
else:
xbmcgui.Window(10025).clearProperty("VideoExtrasShowContextMenu")
log("VideoExtrasService: Directory for overlay images is %s" % PROFILE_DIR)
# This is a bit of a hack, but we want to force the default paths for the
# images if they are not set. This way it will point to the directory containing
# all the overlay images to start with, meaning that it will be the directory
# shown to the user if they choose to change the icons
if ADDON.getSetting("useCustomImages") != "true":
if ADDON.getSetting('overlayImage') in [None, '']:
skinExtrasOverlay = os_path_join(RES_DIR, "skins")
skinExtrasOverlay = os_path_join(skinExtrasOverlay, "icons")
skinExtrasOverlay = os_path_join(skinExtrasOverlay, "overlay1.png")
ADDON.setSetting('overlayImage', skinExtrasOverlay)
if ADDON.getSetting('listImage') in [None, '']:
skinExtrasList = os_path_join(RES_DIR, "skins")
skinExtrasList = os_path_join(skinExtrasList, "icons")
skinExtrasList = os_path_join(skinExtrasList, "list1.png")
ADDON.setSetting('listImage', skinExtrasList)
# Check the YouTube settings are correct, we will automatically disable the
# option if the YouTube addon is not installed
checkYouTubeSettings()
# Check the Vimeo settings are correct, we will automatically disable the
# option if the Vimeo addon is not installed
checkVimeoSettings()
# Make sure that the service option is enabled
if Settings.isServiceEnabled():
try:
# Construct the service class
service = VideoExtrasService()
# Refresh the caches
service.cacheAllExtras()
del service
except:
log("VideoExtrasService: %s" % traceback.format_exc(), xbmc.LOGERROR)
else:
# Service not enabled
log("VideoExtrasService: Service disabled in settings")
# Clean any cached extras
CacheCleanup.removeAllCachedFiles()
# Track if videos finish, and auto display the video extras if required
if Settings.showExtrasAfterMovie():
playerMonitor = VideoExtrasPlayerMonitor()
while not xbmc.abortRequested:
xbmc.sleep(1000)
del playerMonitor