forked from oe-mirrors/e2m3u2bouquet-plugin
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathplugin.py
383 lines (306 loc) · 13.8 KB
/
plugin.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
from __future__ import absolute_import, print_function
import errno
import os
import time
import six
import twisted.python.runtime
from Components.config import ConfigClock, ConfigEnableDisable, ConfigSelection, ConfigSelectionNumber, ConfigSubsection, ConfigText, ConfigYesNo, config
from Components.PluginComponent import plugins
from enigma import eEPGCache, eTimer
from Plugins.Plugin import PluginDescriptor
from Screens.MessageBox import MessageBox
from twisted.internet import threads
from . import _, log
try:
import Plugins.Extensions.EPGImport.EPGConfig as EPGConfig
import Plugins.Extensions.EPGImport.EPGImport as EPGImport
except ImportError:
EPGImport = None
EPGConfig = None
# Global variable
autoStartTimer = None
_session = None
providers_list = {}
# Set default configuration
config.plugins.e2m3u2b = ConfigSubsection()
config.plugins.e2m3u2b.debug = ConfigEnableDisable(default=False)
config.plugins.e2m3u2b.autobouquetupdate = ConfigYesNo(default=False)
config.plugins.e2m3u2b.scheduletype = ConfigSelection(default='interval', choices=['interval', 'fixed time'])
config.plugins.e2m3u2b.updateinterval = ConfigSelectionNumber(default=6, min=2, max=48, stepwidth=1)
config.plugins.e2m3u2b.schedulefixedtime = ConfigClock(default=0)
config.plugins.e2m3u2b.autobouquetupdateatboot = ConfigYesNo(default=False)
config.plugins.e2m3u2b.last_update = ConfigText()
config.plugins.e2m3u2b.extensions = ConfigYesNo(default=False)
config.plugins.e2m3u2b.mainmenu = ConfigYesNo(default=False)
config.plugins.e2m3u2b.do_epgimport = ConfigYesNo(default=True)
from . import e2m3u2bouquet
from .menu import E2m3u2b_Check, E2m3u2b_Menu
class AutoStartTimer:
def __init__(self, session):
self.session = session
self.timer = eTimer()
try:
self.timer_conn = self.timer.timeout.connect(self.on_timer)
except:
self.timer.callback.append(self.on_timer)
self.update()
def get_wake_time(self):
print('[e2m3u2b] AutoStartTimer -> get_wake_time', file=log)
if config.plugins.e2m3u2b.autobouquetupdate.value:
if config.plugins.e2m3u2b.scheduletype.value == 'interval':
interval = int(config.plugins.e2m3u2b.updateinterval.value)
nowt = time.time()
# set next wakeup value to now + interval
return int(nowt) + (interval * 60 * 60)
elif config.plugins.e2m3u2b.scheduletype.value == 'fixed time':
# convert the config clock to a time
fixed_time_clock = config.plugins.e2m3u2b.schedulefixedtime.value
now = time.localtime(time.time())
fixed_wake_time = int(time.mktime((now.tm_year, now.tm_mon, now.tm_mday, fixed_time_clock[0],
fixed_time_clock[1], now.tm_sec, now.tm_wday, now.tm_yday, now.tm_isdst)))
print(('fixed schedule time: ', time.asctime(time.localtime(fixed_wake_time))))
return fixed_wake_time
else:
return -1
def update(self):
print('[e2m3u2b] AutoStartTimer -> update', file=log)
self.timer.stop()
wake = self.get_wake_time()
nowt = time.time()
now = int(nowt)
if wake > 0:
if wake <= now:
# new wake time is in past set to the future time / interval
if config.plugins.e2m3u2b.scheduletype.value == 'interval':
interval = int(config.plugins.e2m3u2b.updateinterval.value)
wake += interval * 60 * 60 # add interval in hours if wake up time is in past
elif config.plugins.e2m3u2b.scheduletype.value == 'fixed time':
wake += 60 * 60 * 24 # add 1 day to fixed time if wake up time is in past
next_wake = wake - now
self.timer.startLongTimer(next_wake)
else:
wake = -1
# print>> log, '[e2m3u2b] next wake up time {} (now={})'.format(wake, now)
print('[e2m3u2b] next wake up time {} (now={})'.format(time.asctime(time.localtime(wake)), time.asctime(time.localtime(now))), file=log)
return wake
def on_timer(self):
self.timer.stop()
now = int(time.time())
wake = now
print('[e2m3u2b] on_timer occured at {}'.format(now), file=log)
print('[e2m3u2b] Stating bouquet update because auto update bouquet schedule is enabled', file=log)
if config.plugins.e2m3u2b.scheduletype.value == 'fixed time':
wake = self.get_wake_time()
# if close enough to wake time do bouquet update
if wake - now < 60:
try:
start_update()
except Exception as e:
print("[e2m3u2b] on_timer Error:", e, file=log)
if config.plugins.e2m3u2b.debug.value:
raise
self.update()
def get_status(self):
print('[e2m3u2b] AutoStartTimer -> getStatus', file=log)
def start_update(epgimport=None):
"""Run m3u channel update
"""
print('start_update called')
e2m3u2b_config = e2m3u2bouquet.Config()
if os.path.isfile(os.path.join(e2m3u2bouquet.CFGPATH, 'config.xml')):
e2m3u2b_config.read_config(os.path.join(e2m3u2bouquet.CFGPATH, 'config.xml'))
providers_to_process = []
epgimport_sourcefiles = []
for key, provider_config in six.iteritems(e2m3u2b_config.providers):
if provider_config.enabled and not provider_config.name.startswith('Supplier Name'):
providers_to_process.append(provider_config)
epgimport_sourcefilename = os.path.join(e2m3u2bouquet.EPGIMPORTPATH, 'suls_iptv_{}.sources.xml'
.format(e2m3u2bouquet.get_safe_filename(provider_config.name)))
epgimport_sourcefiles.append(epgimport_sourcefilename)
if twisted.python.runtime.platform.supportsThreads():
d = threads.deferToThread(start_process_providers, providers_to_process, e2m3u2b_config)
d.addCallback(start_update_callback, epgimport_sourcefiles, int(time.time()), epgimport)
else:
start_process_providers(providers_to_process, e2m3u2b_config)
start_update_callback(None, epgimport_sourcefiles, int(time.time()), epgimport)
def start_update_callback(result, epgimport_sourcefiles, start_time, epgimport=None):
elapsed_secs = (int(time.time())) - start_time
msg = 'Finished bouquet update in {}s'.format(str(elapsed_secs))
e2m3u2bouquet.Status.message = msg
print('[e2m3u2b] {}'.format(msg), file=log)
# Attempt automatic epg import is option enabled and epgimport plugin detected
if EPGImport and config.plugins.e2m3u2b.do_epgimport.value is True:
if epgimport is None:
epgimport = EPGImport.EPGImport(eEPGCache.getInstance(), lambda x: True)
sources = [s for s in epgimport_sources(epgimport_sourcefiles)]
sources.reverse()
epgimport.sources = sources
epgimport.onDone = epgimport_done
epgimport.beginImport(longDescUntil=time.time() + (5 * 24 * 3600))
def start_process_providers(providers_to_process, e2m3u2b_config):
providers_updated = False
for provider_config in providers_to_process:
provider = e2m3u2bouquet.Provider(provider_config)
if int(time.time()) - int(provider.config.last_provider_update) > 21600:
# wait at least 6 hours (21600s) between update checks
providers_updated = provider.provider_update()
print('[e2m3u2b] Starting backend script {}'.format(provider.config.name), file=log)
provider.process_provider()
print('[e2m3u2b] Finished backend script {}'.format(provider.config.name), file=log)
if providers_updated:
e2m3u2b_config.write_config()
localtime = time.asctime(time.localtime(time.time()))
config.plugins.e2m3u2b.last_update.value = localtime
config.plugins.e2m3u2b.last_update.save()
e2m3u2bouquet.reload_bouquets()
def epgimport_sources(sourcefiles):
for sourcefile in sourcefiles:
try:
for s in EPGConfig.enumSourcesFile(sourcefile):
yield s
except Exception as e:
print('[e2m3u2b] Failed to open epg source ', sourcefile, ' Error: ', e, file=log)
def epgimport_done(reboot=False, epgfile=None):
print('[e2m3u2b] Automatic epg import finished', file=log)
def do_reset():
"""Reset bouquets and
epg importer config by running the script uninstall method
"""
print('do_reset called')
e2m3u2bouquet.uninstaller()
e2m3u2bouquet.reload_bouquets()
def main(session, **kwargs):
check_cfg_folder()
# Show message if EPG Import is not detected
if not EPGImport:
session.openWithCallback(open_menu(session), E2m3u2b_Check)
else:
open_menu(session)
def open_menu(session):
session.open(E2m3u2b_Menu)
def check_cfg_folder():
"""Make config folder if it doesn't exist
"""
try:
os.makedirs(e2m3u2bouquet.CFGPATH)
except OSError as e: # race condition guard
if e.errno != errno.EEXIST:
print("[e2m3u2b] unable to create config dir:", e, file=log)
if config.plugins.e2m3u2b.debug.value:
raise
def done_configuring():
"""Check for new config values for auto start
"""
print('[e2m3u2b] Done configuring', file=log)
if autoStartTimer is not None:
autoStartTimer.update()
def on_boot_start_check():
"""This will only execute if the
config option autobouquetupdateatboot is true
"""
now = int(time.time())
# TODO Skip if there is an upcoming scheduled update
print('[e2m3u2b] Stating bouquet update because auto update bouquet at start enabled', file=log)
try:
start_update()
except Exception as e:
print("[e2m3u2b] on_boot_start_check Error:", e, file=log)
if config.plugins.e2m3u2b.debug.value:
raise
def autostart(reason, session=None, **kwargs):
# reason is 0 at start and 1 at shutdown
# these globals need declared as they are reassigned here
global autoStartTimer
global _session
print('[e2m3u2b] autostart {} occured at {}'.format(reason, time.time()), file=log)
if reason == 0 and _session is None:
if session is not None:
_session = session
if autoStartTimer is None:
autoStartTimer = AutoStartTimer(session)
if config.plugins.e2m3u2b.autobouquetupdateatboot.value:
on_boot_start_check()
else:
print('[e2m3u2b] stop', file=log)
def get_next_wakeup():
# don't enable waking from deep standby for now
print('[e2m3u2b] get_next_wakeup', file=log)
return -1
def menuHook(menuid):
""" Called whenever a menu is created"""
if menuid == "mainmenu":
return [(plugin_name, quick_import_menu, plugin_name, 45)]
return[]
def extensions_menu(session, **kwargs):
""" Needed for the extension menu descriptor
"""
main(session, **kwargs)
def quick_import_menu(session, **kwargs):
session.openWithCallback(quick_import_callback, MessageBox, _("Update of channels will start. This may take a few minutes.\nProceed?"), MessageBox.TYPE_YESNO, timeout=15, default=True)
def quick_import_callback(confirmed):
if not confirmed:
return
try:
start_update()
except Exception as e:
print("[e2m3u2b] manual_update_callback Error:", e, file=log)
if config.plugins.e2m3u2b.debug.value:
raise
def update_extensions_menu(cfg_el):
print('[e2m3u2b] update extensions menu', file=log)
try:
if cfg_el.value:
plugins.addPlugin(extDescriptorQuick)
else:
plugins.removePlugin(extDescriptorQuick)
except Exception as e:
print('[e2m3u2b] Failed to update extensions menu: ', e, file=log)
def update_main_menu(cfg_el):
print('[e2m3u2b] update main menu', file=log)
try:
if cfg_el.value:
plugins.addPlugin(extDescriptorQuickMain)
else:
plugins.removePlugin(extDescriptorQuickMain)
except Exception as e:
print('[e2m3u2b] Failed to update main menu: ', e, file=log)
plugin_name = 'IPTV Bouquet Maker'
plugin_description = _("IPTV for Enigma2 - E2m3u2bouquet plugin")
print('[e2m3u2b] add notifier')
extDescriptor = PluginDescriptor(name=plugin_name, description=plugin_description, where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=extensions_menu)
extDescriptorQuick = PluginDescriptor(name=plugin_name, description=plugin_description, where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=quick_import_menu)
extDescriptorQuickMain = PluginDescriptor(name=plugin_name, description=plugin_description, where=PluginDescriptor.WHERE_MENU, fnc=menuHook)
config.plugins.e2m3u2b.extensions.addNotifier(update_extensions_menu, initial_call=False)
config.plugins.e2m3u2b.mainmenu.addNotifier(update_main_menu, initial_call=False)
def Plugins(**kwargs):
result = [
PluginDescriptor(
name=plugin_name,
description=plugin_description,
where=[
PluginDescriptor.WHERE_AUTOSTART,
PluginDescriptor.WHERE_SESSIONSTART,
],
fnc=autostart,
wakeupfnc=get_next_wakeup
),
PluginDescriptor(
name=plugin_name,
description=plugin_description,
where=PluginDescriptor.WHERE_PLUGINMENU,
icon='images/e2m3ubouquetlogo.png',
fnc=main
) # ,
#PluginDescriptor(
# name=plugin_name,
# description=plugin_description,
# where=PluginDescriptor.WHERE_MENU,
# icon='images/e2m3ubouquetlogo.png',
# fnc=menuHook
#)
]
if config.plugins.e2m3u2b.extensions.value:
result.append(extDescriptorQuick)
if config.plugins.e2m3u2b.mainmenu.value:
result.append(extDescriptorQuickMain)
return result