forked from lozioangie/addon
-
Notifications
You must be signed in to change notification settings - Fork 56
/
service.py
567 lines (464 loc) · 26.5 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
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
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
# -*- coding: utf-8 -*-
import ast
import datetime
import math
import os
import sys
import threading
import traceback
import xbmc
try:
from urllib.parse import urlsplit
except ImportError:
from urlparse import urlsplit
# on kodi 18 its xbmc.translatePath, on 19 xbmcvfs.translatePath
try:
import xbmcvfs
xbmc.translatePath = xbmcvfs.translatePath
except:
pass
from platformcode import config
librerias = xbmc.translatePath(os.path.join(config.get_runtime_path(), 'lib'))
sys.path.insert(0, librerias)
os.environ['TMPDIR'] = config.get_temp_file('')
from core import videolibrarytools, filetools, channeltools, httptools, scrapertools, db
from lib import schedule
from platformcode import logger, platformtools, updater, xbmc_videolibrary
from specials import videolibrary
from servers import torrent
# if this service need to be reloaded because an update changed it
needsReload = False
# list of threads
threads = []
def update(path, p_dialog, i, t, serie, overwrite):
logger.debug("Updating " + path)
insertados_total = 0
nfo_file = xbmc.translatePath(filetools.join(path, 'tvshow.nfo'))
head_nfo, it = videolibrarytools.read_nfo(nfo_file)
videolibrarytools.update_renumber_options(it, head_nfo, path)
if not serie.library_url: serie = it
category = serie.category
# logger.debug("%s: %s" %(serie.contentSerieName,str(list_canales) ))
for channel, url in serie.library_urls.items():
serie.channel = channel
module = __import__('channels.%s' % channel, fromlist=["channels.%s" % channel])
url = module.host + urlsplit(url).path
serie.url = url
try:
head_nfo, it = videolibrarytools.read_nfo(nfo_file) # Refresh the .nfo to collect updates
if it.emergency_urls:
serie.emergency_urls = it.emergency_urls
serie.category = category
except:
logger.error(traceback.format_exc())
channel_enabled = channeltools.is_enabled(serie.channel)
if channel_enabled:
heading = config.get_localized_string(20000)
p_dialog.update(int(math.ceil((i + 1) * t)), heading, config.get_localized_string(60389) % (serie.contentSerieName, serie.channel.capitalize()))
try:
pathchannels = filetools.join(config.get_runtime_path(), "channels", serie.channel + '.py')
logger.debug("loading channel: " + pathchannels + " " + serie.channel)
if serie.library_filter_show:
serie.show = serie.library_filter_show.get(serie.channel, serie.contentSerieName)
obj = __import__('channels.%s' % serie.channel, fromlist=[pathchannels])
itemlist = obj.episodios(serie)
try:
if int(overwrite) == 3:
# Overwrite all files (tvshow.nfo, 1x01.nfo, 1x01 [channel] .json, 1x01.strm, etc ...)
insertados, sobreescritos, fallidos, notusedpath = videolibrarytools.save_tvshow(serie, itemlist)
#serie= videolibrary.check_season_playcount(serie, serie.contentSeason)
#if filetools.write(path + '/tvshow.nfo', head_nfo + it.tojson()):
# serie.infoLabels['playcount'] = serie.playcount
else:
insertados, sobreescritos, fallidos = videolibrarytools.save_episodes(path, itemlist, serie,
silent=True,
overwrite=overwrite)
#it = videolibrary.check_season_playcount(it, it.contentSeason)
#if filetools.write(path + '/tvshow.nfo', head_nfo + it.tojson()):
# serie.infoLabels['playcount'] = serie.playcount
insertados_total += insertados
except:
logger.error("Error when saving the chapters of the series")
logger.error(traceback.format_exc())
except:
logger.error("Error in obtaining the episodes of: %s" % serie.show)
logger.error(traceback.format_exc())
else:
logger.debug("Channel %s not active is not updated" % serie.channel)
# Synchronize the episodes seen from the Kodi video library with that of KoD
try:
if config.is_xbmc(): # If it's Kodi, we do it
from platformcode import xbmc_videolibrary
xbmc_videolibrary.mark_content_as_watched_on_kod(filetools.join(path,'tvshow.nfo'))
except:
logger.error(traceback.format_exc())
return insertados_total > 0
def check_for_update(overwrite=True):
logger.debug("Update Series...")
p_dialog = None
serie_actualizada = False
update_when_finished = False
hoy = datetime.date.today()
estado_verify_playcount_series = False
try:
if overwrite or \
config.get_setting("update", "videolibrary") in [4, 5] or \
(config.get_setting("update", "videolibrary") not in [0, 4] and hoy.strftime('%Y-%m-%d') != config.get_setting('updatelibrary_last_check', 'videolibrary')):
config.set_setting("updatelibrary_last_check", hoy.strftime('%Y-%m-%d'), "videolibrary")
heading = config.get_localized_string(60601)
p_dialog = platformtools.dialog_progress_bg(config.get_localized_string(20000), heading)
p_dialog.update(0, '')
show_list = []
# show_ep_num = []
for path, folders, files in filetools.walk(videolibrarytools.TVSHOWS_PATH):
if 'tvshow.nfo' in files:
show_list.extend([filetools.join(path, f) for f in files if f == "tvshow.nfo"])
# show_ep_num.append(len([f for f in files if f.endswith('.nfo') and f != 'tvshow.nfo']))
if show_list:
t = float(100) / len(show_list)
for i, tvshow_file in enumerate(show_list):
head_nfo, serie = videolibrarytools.read_nfo(tvshow_file)
# ep_count = show_ep_num[i] + (len(serie.local_episodes_list) if serie.local_episodes_path else 0)
# if serie.infoLabels['status'].lower() == 'ended' and \
# ep_count >= serie.infoLabels['number_of_episodes']:
# serie.active = 0
# filetools.write(tvshow_file, head_nfo + serie.tojson())
path = filetools.dirname(tvshow_file)
logger.debug("serie=" + serie.contentSerieName)
# Check the status of the series.library_playcounts of the Series in case it is incomplete
try:
estado = False
# If we have not done the verification or do not have a playcount, we enter
estado = config.get_setting("verify_playcount", "videolibrary")
if not estado or estado == False or not serie.library_playcounts: # If it hasn't happened before, we do it now
serie, estado = videolibrary.verify_playcount_series(serie, path) # Also happens if a PlayCount is missing completely
except:
logger.error(traceback.format_exc())
else:
if estado: # If the update was successful ...
estado_verify_playcount_series = True # ... is checked to change the Video Library option
try:
interval = int(serie.active) # Could be the bool type
except:
interval = 1
if not serie.active:
# if the series is not active discard
if not overwrite:
# Synchronize the episodes seen from the Kodi video library with that of Alpha, even if the series is deactivated
try:
if config.is_xbmc(): # If it's Kodi, we do it
from platformcode import xbmc_videolibrary
xbmc_videolibrary.mark_content_as_watched_on_kod(filetools.join(path,'tvshow.nfo'))
except:
logger.error(traceback.format_exc())
continue
p_dialog.update(int(math.ceil((i + 1) * t)), heading, serie.contentSerieName)
# Obtain the update date and the next scheduled for this series
update_next = serie.update_next
if update_next:
y, m, d = update_next.split('-')
update_next = datetime.date(int(y), int(m), int(d))
else:
update_next = hoy
update_last = serie.update_last
if update_last:
y, m, d = update_last.split('-')
update_last = datetime.date(int(y), int(m), int(d))
else:
update_last = hoy
# if the series is active ...
if overwrite or config.get_setting("updatetvshows_interval", "videolibrary") == 0:
# ... force update regardless of interval
serie_actualizada = update(path, p_dialog, i, t, serie, overwrite)
if not serie_actualizada:
update_next = hoy + datetime.timedelta(days=interval)
elif interval == 1 and update_next <= hoy:
# ...daily update
serie_actualizada = update(path, p_dialog, i, t, serie, overwrite)
if not serie_actualizada and update_last <= hoy - datetime.timedelta(days=7):
# if it hasn't been updated for a week, change the interval to weekly
interval = 7
update_next = hoy + datetime.timedelta(days=interval)
elif interval == 7 and update_next <= hoy:
# ... weekly update
serie_actualizada = update(path, p_dialog, i, t, serie, overwrite)
if not serie_actualizada:
if update_last <= hoy - datetime.timedelta(days=14):
# if it has not been updated for 2 weeks, change the interval to monthly
interval = 30
update_next += datetime.timedelta(days=interval)
elif interval == 30 and update_next <= hoy:
# ... monthly update
serie_actualizada = update(path, p_dialog, i, t, serie, overwrite)
if not serie_actualizada:
update_next += datetime.timedelta(days=interval)
if serie_actualizada:
update_last = hoy
update_next = hoy + datetime.timedelta(days=interval)
head_nfo, serie = videolibrarytools.read_nfo(tvshow_file) # Reread the .nfo, which has been modified
if interval != int(serie.active) or update_next.strftime('%Y-%m-%d') != serie.update_next or update_last.strftime('%Y-%m-%d') != serie.update_last:
serie.update_last = update_last.strftime('%Y-%m-%d')
if update_next > hoy:
serie.update_next = update_next.strftime('%Y-%m-%d')
serie.active = interval
serie.channel = "videolibrary"
serie.action = "get_seasons"
filetools.write(tvshow_file, head_nfo + serie.tojson())
if serie_actualizada:
if config.get_setting("search_new_content", "videolibrary") == 0:
# We update the Kodi video library: Find content in the series folder
if config.is_xbmc() and config.get_setting("videolibrary_kodi"):
from platformcode import xbmc_videolibrary
xbmc_videolibrary.update(folder=filetools.basename(path))
else:
update_when_finished = True
if estado_verify_playcount_series: # If any playcount has been changed, ...
estado = config.set_setting("verify_playcount", True, "videolibrary") # ... we update the Videolibrary option
if config.get_setting("search_new_content", "videolibrary") == 1 and update_when_finished:
# We update the Kodi video library: Find content in all series
if config.is_xbmc() and config.get_setting("videolibrary_kodi"):
from platformcode import xbmc_videolibrary
xbmc_videolibrary.update()
p_dialog.close()
else:
logger.debug("Not update the video library, it is disabled")
except Exception as ex:
logger.error("An error occurred while updating the series")
template = "An exception of type %s occured. Arguments:\n%r"
message = template % (type(ex).__name__, ex.args)
logger.error(message)
if p_dialog:
p_dialog.close()
from core.item import Item
item_dummy = Item()
videolibrary.list_movies(item_dummy, silent=True)
if config.get_setting('trakt_sync'):
from core import trakt_tools
trakt_tools.update_all()
def updaterCheck():
global needsReload
# updater check
updated, needsReload = updater.check(background=True)
def get_ua_list():
# https://github.com/alfa-addon/addon/blob/master/plugin.video.alfa/platformcode/updater.py#L273
logger.info()
url = "https://chromiumdash.appspot.com/fetch_releases?channel=Stable&platform=Windows&num=6&offset=0"
try:
current_ver = config.get_setting("chrome_ua_version", default="").split(".")
data = httptools.downloadpage(url, alfa_s=True, ignore_response_code=True).data
data = ast.literal_eval(data)
new_ua_ver = data[0].get('version', '') if data and isinstance(data, list) else ''
if not new_ua_ver:
return
if not current_ver:
config.set_setting("chrome_ua_version", new_ua_ver)
else:
for pos, val in enumerate(new_ua_ver.split('.')):
if int(val) > int(current_ver[pos]):
config.set_setting("chrome_ua_version", new_ua_ver)
break
except:
logger.error(traceback.format_exc())
def run_threaded(job_func, args):
job_thread = threading.Thread(target=job_func, args=args)
job_thread.start()
threads.append(job_thread)
def join_threads():
logger.debug(threads)
for th in threads:
try:
th.join()
except:
logger.error(traceback.format_exc())
class AddonMonitor(xbmc.Monitor):
def __init__(self):
self.settings_pre = config.get_all_settings_addon()
self.updaterPeriod = None
self.update_setting = None
self.update_hour = None
self.scheduleScreenOnJobs()
self.scheduleUpdater()
self.scheduleUA()
if not needsReload: # do not run videolibrary update if service needs to be reloaded
# videolibrary wait
update_wait = [0, 10000, 20000, 30000, 60000]
wait = update_wait[int(config.get_setting("update_wait", "videolibrary"))]
if wait > 0:
xbmc.sleep(wait)
if not config.get_setting("update", "videolibrary") == 2:
run_threaded(check_for_update, (False,))
self.scheduleVideolibrary()
super(AddonMonitor, self).__init__()
def onSettingsChanged(self):
logger.debug('settings changed')
settings_post = config.get_all_settings_addon()
# sometimes kodi randomly return default settings (rare but happens), this if try to workaround this
if settings_post and settings_post.get('show_once', True):
from platformcode import xbmc_videolibrary
if self.settings_pre.get('downloadpath', None) != settings_post.get('downloadpath', None):
xbmc_videolibrary.update_sources(settings_post.get('downloadpath', None),
self.settings_pre.get('downloadpath', None))
# If the path of the video library has been changed, we call to check directories so that it creates it and automatically asks if to configure the video library
if self.settings_pre.get("videolibrarypath", None) and self.settings_pre.get("videolibrarypath", None) != settings_post.get("videolibrarypath", None) or \
self.settings_pre.get("folder_movies", None) and self.settings_pre.get("folder_movies", None) != settings_post.get("folder_movies", None) or \
self.settings_pre.get("folder_tvshows", None) and self.settings_pre.get("folder_tvshows", None) != settings_post.get("folder_tvshows", None):
videolibrary.move_videolibrary(self.settings_pre.get("videolibrarypath", ''),
settings_post.get("videolibrarypath", ''),
self.settings_pre.get("folder_movies", ''),
settings_post.get("folder_movies", ''),
self.settings_pre.get("folder_tvshows", ''),
settings_post.get("folder_tvshows", ''))
# if you want to autoconfigure and the video library directory had been created
if not self.settings_pre.get("videolibrary_kodi", None) and settings_post.get("videolibrary_kodi", None):
xbmc_videolibrary.ask_set_content(silent=True)
elif self.settings_pre.get("videolibrary_kodi", None) and not settings_post.get("videolibrary_kodi", None):
xbmc_videolibrary.clean()
if self.settings_pre.get('addon_update_timer') != settings_post.get('addon_update_timer'):
schedule.clear('updater')
self.scheduleUpdater()
if self.update_setting != config.get_setting("update", "videolibrary") or self.update_hour != config.get_setting("everyday_delay", "videolibrary") * 4:
schedule.clear('videolibrary')
self.scheduleVideolibrary()
if self.settings_pre.get('elementum_on_seed') != settings_post.get('elementum_on_seed') and settings_post.get('elementum_on_seed'):
if not platformtools.dialog_yesno(config.get_localized_string(70805), config.get_localized_string(70806)):
config.set_setting('elementum_on_seed', False)
if self.settings_pre.get("shortcut_key", '') != settings_post.get("shortcut_key", ''):
xbmc.executebuiltin('Action(reloadkeymaps)')
if self.settings_pre.get('downloadenabled') != settings_post.get('downloadenabled'):
platformtools.itemlist_refresh()
# backup settings
filetools.copy(os.path.join(config.get_data_path(), "settings.xml"),
os.path.join(config.get_data_path(), "settings.bak"), True)
logger.debug({k: self.settings_pre[k] for k in self.settings_pre
if k in settings_post and self.settings_pre[k] != settings_post[k]})
self.settings_pre = config.get_all_settings_addon()
def onNotification(self, sender, method, data):
# logger.debug('METHOD', method, sender, data)
if method == 'Playlist.OnAdd':
from core import db
db['OnPlay']['addon'] = True
db.close()
elif method == 'Player.OnStop':
from core import db
db['OnPlay']['addon'] = False
db.close()
elif method == 'VideoLibrary.OnUpdate':
xbmc_videolibrary.set_watched_on_kod(data)
logger.debug('AGGIORNO')
def onScreensaverActivated(self):
logger.debug('screensaver activated, un-scheduling screen-on jobs')
schedule.clear('screenOn')
def onScreensaverDeactivated(self):
logger.debug('screensaver deactivated, re-scheduling screen-on jobs')
self.scheduleScreenOnJobs()
def scheduleUpdater(self):
if not config.dev_mode():
updaterCheck()
self.updaterPeriod = config.get_setting('addon_update_timer')
schedule.every(self.updaterPeriod).hours.do(updaterCheck).tag('updater')
logger.debug('scheduled updater every ' + str(self.updaterPeriod) + ' hours')
def scheduleUA(self):
get_ua_list()
schedule.every(1).day.do(get_ua_list)
def scheduleVideolibrary(self):
self.update_setting = config.get_setting("update", "videolibrary")
# 2 = Daily, 3 = When Kodi starts and daily, 5 = Each time you start Kodi and daily
if self.update_setting in [2, 3, 5]:
self.update_hour = config.get_setting("everyday_delay", "videolibrary") * 4
schedule.every().day.at(str(self.update_hour).zfill(2) + ':00').do(run_threaded, check_for_update, (False,)).tag('videolibrary')
logger.debug('scheduled videolibrary at ' + str(self.update_hour).zfill(2) + ':00')
def scheduleScreenOnJobs(self):
schedule.every().second.do(platformtools.viewmodeMonitor).tag('screenOn')
schedule.every().second.do(torrent.elementum_monitor).tag('screenOn')
def onDPMSActivated(self):
logger.debug('DPMS activated, un-scheduling screen-on jobs')
schedule.clear('screenOn')
def onDPMSDeactivated(self):
logger.debug('DPMS deactivated, re-scheduling screen-on jobs')
self.scheduleScreenOnJobs()
if __name__ == "__main__":
logger.info('Starting KoD service')
# Test if all the required directories are created
config.verify_directories_created()
if config.get_setting('autostart'):
xbmc.executebuiltin('RunAddon(plugin.video.' + config.PLUGIN_NAME + ')')
# port old db to new
old_db_name = filetools.join(config.get_data_path(), "kod_db.sqlite")
if filetools.isfile(old_db_name):
try:
import sqlite3
old_db_conn = sqlite3.connect(old_db_name, timeout=15)
old_db = old_db_conn.cursor()
old_db.execute('select * from viewed')
for ris in old_db.fetchall():
if ris[1]: # tvshow
show = db['viewed'].get(ris[0], {})
show[str(ris[1]) + 'x' + str(ris[2])] = ris[3]
db['viewed'][ris[0]] = show
else: # film
db['viewed'][ris[0]] = ris[3]
except:
pass
finally:
filetools.remove(old_db_name, True, False)
# replace tvdb to tmdb for series
if config.get_setting('videolibrary_kodi') and config.get_setting('show_once'):
nun_records, records = xbmc_videolibrary.execute_sql_kodi('select * from path where strPath like "' +
filetools.join(config.get_setting('videolibrarypath'), config.get_setting('folder_tvshows')) +
'%" and strScraper="metadata.tvdb.com"')
if nun_records:
import xbmcaddon
# change language
tvdbLang = xbmcaddon.Addon(id="metadata.tvdb.com").getSetting('language')
newLang = tvdbLang + '-' + tvdbLang.upper()
xbmcaddon.Addon(id="metadata.tvshows.themoviedb.org").setSetting('language', newLang)
updater.refreshLang()
# prepare to replace strSettings
path_settings = xbmc.translatePath("special://profile/addon_data/metadata.tvshows.themoviedb.org/settings.xml")
settings_data = filetools.read(path_settings)
strSettings = ' '.join(settings_data.split()).replace("> <", "><")
strSettings = strSettings.replace("\"", "\'")
# update db
nun_records, records = xbmc_videolibrary.execute_sql_kodi(
'update path set strScraper="metadata.tvshows.themoviedb.org", strSettings="' + strSettings + '" where strPath like "' +
filetools.join(config.get_setting('videolibrarypath'), config.get_setting('folder_tvshows')) +
'%" and strScraper="metadata.tvdb.com"')
# scan new info
xbmc.executebuiltin('UpdateLibrary(video)')
xbmc.executebuiltin('CleanLibrary(video)')
# while xbmc.getCondVisibility('Library.IsScanningVideo()'):
# xbmc.sleep(1000)
# check if the user has any connection problems
from platformcode.checkhost import test_conn
run_threaded(test_conn, (True, not config.get_setting('resolver_dns'), True, [], [], True))
monitor = AddonMonitor()
# mark as stopped all downloads (if we are here, probably kodi just started)
from specials.downloads import stop_all
try:
stop_all()
except:
logger.error(traceback.format_exc())
while True:
try:
schedule.run_pending()
except:
logger.error(traceback.format_exc())
if needsReload:
join_threads()
db.close()
logger.info('Relaunching service.py')
xbmc.executeJSONRPC(
'{"jsonrpc": "2.0", "id":1, "method": "Addons.SetAddonEnabled", "params": { "addonid": "plugin.video.kod", "enabled": false }}')
xbmc.executeJSONRPC(
'{"jsonrpc": "2.0", "id":1, "method": "Addons.SetAddonEnabled", "params": { "addonid": "plugin.video.kod", "enabled": true }}')
logger.debug(threading.enumerate())
break
if monitor.waitForAbort(1): # every second
logger.debug('KoD service EXIT')
# db need to be closed when not used, it will cause freezes
join_threads()
logger.debug('Close Threads')
db.close()
logger.debug('Close DB')
break
logger.debug('KoD service STOPPED')