summaryrefslogtreecommitdiff
path: root/plugin.video.embycon/service.py
blob: 60b1575e1621442a13f3833d21e0aaf7afcceacc (plain)
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
# coding=utf-8
# Gnu General Public License - see LICENSE.TXT

import xbmc
import xbmcaddon
import xbmcgui
import time
import json
import traceback

from resources.lib.downloadutils import DownloadUtils
from resources.lib.simple_logging import SimpleLogging
from resources.lib.play_utils import playFile
from resources.lib.kodi_utils import HomeWindow
from resources.lib.translation import i18n
from resources.lib.widgets import checkForNewContent

# clear user and token when logging in
home_window = HomeWindow()
home_window.clearProperty("userid")
home_window.clearProperty("AccessToken")
home_window.clearProperty("Params")

log = SimpleLogging('service')
download_utils = DownloadUtils()

# auth the service
try:
    download_utils.authenticate()
except Exception as error:
    log.error("Error with initial service auth: " + str(error))


def hasData(data):
    if data is None or len(data) == 0 or data == "None":
        return False
    else:
        return True


def sendProgress():
    playing_file = xbmc.Player().getPlayingFile()
    play_data = monitor.played_information.get(playing_file)

    if play_data is None:
        return

    log.debug("Sending Progress Update")

    play_time = xbmc.Player().getTime()
    play_data["currentPossition"] = play_time

    item_id = play_data.get("item_id")
    if item_id is None:
        return

    ticks = int(play_time * 10000000)
    paused = play_data.get("paused", False)
    playback_type = play_data.get("playback_type")
    play_session_id = play_data.get("play_session_id")

    postdata = {
        'QueueableMediaTypes': "Video",
        'CanSeek': True,
        'ItemId': item_id,
        'MediaSourceId': item_id,
        'PositionTicks': ticks,
        'IsPaused': paused,
        'IsMuted': False,
        'PlayMethod': playback_type,
        'PlaySessionId': play_session_id
    }

    log.debug("Sending POST progress started: %s." % postdata)

    url = "{server}/emby/Sessions/Playing/Progress"
    download_utils.downloadUrl(url, postBody=postdata, method="POST")

def promptForStopActions(item_id, current_possition):

    settings = xbmcaddon.Addon(id='plugin.video.embycon')

    prompt_next_percentage = int(settings.getSetting('promptPlayNextEpisodePercentage'))
    play_prompt = settings.getSetting('promptPlayNextEpisodePercentage_prompt') == "true"
    prompt_delete_episode_percentage = int(settings.getSetting('promptDeleteEpisodePercentage'))
    prompt_delete_movie_percentage = int(settings.getSetting('promptDeleteMoviePercentage'))

    # everything is off so return
    if prompt_next_percentage == 100 and prompt_delete_episode_percentage == 100 and prompt_delete_movie_percentage == 100:
        return

    jsonData = download_utils.downloadUrl("{server}/emby/Users/{userid}/Items/" +
                                          item_id + "?format=json",
                                          suppress=False, popup=1)
    result = json.loads(jsonData)
    prompt_to_delete = False
    runtime = result.get("RunTimeTicks", 0)

    # if no runtime we cant calculate perceantge so just return
    if runtime == 0:
        log.debug("No runtime so returing")
        return

    # item percentage complete
    percenatge_complete = int(((current_possition * 10000000) / runtime) * 100)
    log.debug("Episode Percentage Complete: %s" % percenatge_complete)

    if (prompt_delete_episode_percentage < 100 and
                result.get("Type", "na") == "Episode" and
                percenatge_complete > prompt_delete_episode_percentage):
            prompt_to_delete = True

    if (prompt_delete_movie_percentage < 100 and
                result.get("Type", "na") == "Movie" and
                percenatge_complete > prompt_delete_movie_percentage):
            prompt_to_delete = True

    if prompt_to_delete:
        log.debug("Prompting for delete")
        resp = xbmcgui.Dialog().yesno(i18n('confirm_file_delete'), i18n('file_delete_confirm'), autoclose=10000)
        if resp:
            log.debug("Deleting item: %s" % item_id)
            url = "{server}/emby/Items/%s?format=json" % item_id
            download_utils.downloadUrl(url, method="DELETE")
            xbmc.executebuiltin("Container.Refresh")

    # prompt for next episode
    if (prompt_next_percentage < 100 and
                result.get("Type", "na") == "Episode" and
                percenatge_complete > prompt_next_percentage):
        parendId = result.get("ParentId", "na")
        item_index = result.get("IndexNumber", -1)

        if parendId == "na":
            log.debug("No parent id, can not prompt for next episode")
            return

        if item_index == -1:
            log.debug("No episode number, can not prompt for next episode")
            return

        jsonData = download_utils.downloadUrl('{server}/emby/Users/{userid}/Items?' +
                             '?Recursive=true' +
                             '&ParentId=' + parendId +
                             #'&Filters=IsUnplayed,IsNotFolder' +
                             '&IsVirtualUnaired=false' +
                             '&IsMissing=False' +
                             '&IncludeItemTypes=Episode' +
                             '&ImageTypeLimit=1' +
                             '&format=json',
                              suppress=False, popup=1)

        items_result = json.loads(jsonData)
        log.debug("Prompt Next Item Details: %s" % items_result)
        # find next episode
        item_list = items_result.get("Items", [])
        for item in item_list:
            index = item.get("IndexNumber", -1)
            if index == item_index + 1: # find the very next episode in the season

                resp = True
                if play_prompt:
                    #next_epp_name = str(index) + " of " + str(item_list[-1].get("IndexNumber", -1)) + " - " + item.get("Name", "n/a")
                    next_epp_name = ("%02d - " % (index,)) + item.get("Name", "n/a")
                    resp = xbmcgui.Dialog().yesno(i18n("play_next_title"), i18n("play_next_question"), next_epp_name, autoclose=10000)

                if resp:
                    next_item_id = item.get("Id")
                    log.debug("Playing Next Episode: %s" % next_item_id)

                    play_info = {}
                    play_info["item_id"] = next_item_id
                    play_info["auto_resume"] = "-1"
                    play_info["force_transcode"] = False
                    play_data = json.dumps(play_info)

                    home_window = HomeWindow()
                    home_window.setProperty("item_id", next_item_id)
                    home_window.setProperty("play_item_message", play_data)

                break


def stopAll(played_information):
    if len(played_information) == 0:
        return

    log.debug("played_information : " + str(played_information))

    for item_url in played_information:
        data = played_information.get(item_url)
        if data is not None:
            log.debug("item_url  : " + item_url)
            log.debug("item_data : " + str(data))

            current_possition = data.get("currentPossition", 0)
            emby_item_id = data.get("item_id")

            if hasData(emby_item_id):
                log.debug("Playback Stopped at: " + str(int(current_possition * 10000000)))

                url = "{server}/emby/Sessions/Playing/Stopped"
                postdata = {
                    'ItemId': emby_item_id,
                    'MediaSourceId': emby_item_id,
                    'PositionTicks': int(current_possition * 10000000)
                }
                download_utils.downloadUrl(url, postBody=postdata, method="POST")

                promptForStopActions(emby_item_id, current_possition)

    played_information.clear()


class Service(xbmc.Player):
    played_information = {}

    def __init__(self, *args):
        log.debug("Starting monitor service: " + str(args))
        self.played_information = {}

    def onPlayBackStarted(self):
        # Will be called when xbmc starts playing a file
        stopAll(self.played_information)

        current_playing_file = xbmc.Player().getPlayingFile()
        log.debug("onPlayBackStarted: " + current_playing_file)

        home_window = HomeWindow()
        emby_item_id = home_window.getProperty("item_id")
        playback_type = home_window.getProperty("PlaybackType_" + emby_item_id)
        play_session_id = home_window.getProperty("PlaySessionId_" + emby_item_id)

        # if we could not find the ID of the current item then return
        if emby_item_id is None or len(emby_item_id) == 0:
            return

        log.debug("Sending Playback Started")
        postdata = {
            'QueueableMediaTypes': "Video",
            'CanSeek': True,
            'ItemId': emby_item_id,
            'MediaSourceId': emby_item_id,
            'PlayMethod': playback_type,
            'PlaySessionId': play_session_id
        }

        log.debug("Sending POST play started: %s." % postdata)

        url = "{server}/emby/Sessions/Playing"
        download_utils.downloadUrl(url, postBody=postdata, method="POST")

        data = {}
        data["item_id"] = emby_item_id
        data["paused"] = False
        data["playback_type"] = playback_type
        data["play_session_id"] = play_session_id
        self.played_information[current_playing_file] = data

        log.debug("ADDING_FILE : " + current_playing_file)
        log.debug("ADDING_FILE : " + str(self.played_information))

    def onPlayBackEnded(self):
        # Will be called when kodi stops playing a file
        log.debug("EmbyCon Service -> onPlayBackEnded")
        home_window = HomeWindow()
        home_window.clearProperty("item_id")
        stopAll(self.played_information)

    def onPlayBackStopped(self):
        # Will be called when user stops kodi playing a file
        log.debug("onPlayBackStopped")
        home_window = HomeWindow()
        home_window.clearProperty("item_id")
        stopAll(self.played_information)

    def onPlayBackPaused(self):
        # Will be called when kodi pauses the video
        log.debug("onPlayBackPaused")
        current_file = xbmc.Player().getPlayingFile()
        play_data = monitor.played_information.get(current_file)

        if play_data is not None:
            play_data['paused'] = True
            sendProgress()

    def onPlayBackResumed(self):
        # Will be called when kodi resumes the video
        log.debug("onPlayBackResumed")
        current_file = xbmc.Player().getPlayingFile()
        play_data = monitor.played_information.get(current_file)

        if play_data is not None:
            play_data['paused'] = False
            sendProgress()

    def onPlayBackSeek(self, time, seekOffset):
        # Will be called when kodi seeks in video
        log.debug("onPlayBackSeek")
        sendProgress()


monitor = Service()
home_window = HomeWindow()
last_progress_update = time.time()
last_content_check = time.time()

# monitor.abortRequested() is causes issues, it currently triggers for all addon cancelations which causes
# the service to exit when a user cancels an addon load action. This is a bug in Kodi.
# I am switching back to xbmc.abortRequested approach until kodi is fixed or I find a work arround

while not xbmc.abortRequested:

    try:
        if xbmc.Player().isPlaying():
            # if playing every 10 seconds updated the server with progress
            if (time.time() - last_progress_update) > 10:
                last_progress_update = time.time()
                sendProgress()
        else:
            # if we have a play item them trigger playback
            play_data = home_window.getProperty("play_item_message")
            if play_data:
                home_window.clearProperty("play_item_message")
                play_info = json.loads(play_data)
                playFile(play_info)

            # if not playing every 60 seonds check for new widget content
            if (time.time() - last_content_check) > 60:
                last_content_check = time.time()
                checkForNewContent()

    except Exception as error:
        log.error("Exception in Playback Monitor : " + str(error))
        log.error(traceback.format_exc())

    xbmc.sleep(1000)

# clear user and token when loggin off
home_window.clearProperty("userid")
home_window.clearProperty("AccessToken")
home_window.clearProperty("Params")

log.error("Service shutting down")