SeriesPlugin 1.2.3: Fixed Wunschliste Series Lookup
[enigma2-plugins.git] / seriesplugin / src / plugin.py
1
2 import os, sys, traceback
3
4 # Localization
5 from . import _
6
7 from time import time
8
9 # GUI (Screens)
10 from Screens.MessageBox import MessageBox
11
12 # Config
13 from Components.config import config, ConfigSubsection, ConfigEnableDisable, ConfigNumber, ConfigSelection, ConfigYesNo, ConfigText, ConfigSelectionNumber
14
15 # Plugin
16 from Components.PluginComponent import plugins
17 from Plugins.Plugin import PluginDescriptor
18
19 # Plugin internal
20 from SeriesPluginTimer import SeriesPluginTimer
21 from SeriesPluginInfoScreen import SeriesPluginInfoScreen
22 from SeriesPluginRenamer import SeriesPluginRenamer
23 from SeriesPluginIndependent import startIndependent, runIndependent
24 from SeriesPluginConfiguration import SeriesPluginConfiguration
25 from Logger import splog
26
27
28 #######################################################
29 # Constants
30 NAME = "SeriesPlugin"
31 VERSION = "1.2.3_oe2.2"
32 DESCRIPTION = _("SeriesPlugin")
33 SHOWINFO = _("Show series info (SP)")
34 RENAMESERIES = _("Rename serie(s) (SP)")
35 CHECKTIMERS = _("Check timer list for series (SP)")
36 SUPPORT = "http://bit.ly/seriespluginihad"
37 DONATE = "http://bit.ly/seriespluginpaypal"
38 ABOUT = "\n  " + NAME + " " + VERSION + "\n\n" \
39                                 + _("  (C) 2012 by betonme @ IHAD \n\n") \
40                                 + _("  {lookups:d} successful lookups.\n") \
41                                 + _("  How much time have You saved?\n\n") \
42                                 + _("  Support: ") + SUPPORT + "\n" \
43                                 + _("  Feel free to donate. \n") \
44                                 + _("  PayPal: ") + DONATE
45 try:
46         from Tools.HardwareInfo import HardwareInfo
47         DEVICE = HardwareInfo().get_device_name().strip()
48         
49         # Get Box Info
50         #from Components.Network import iNetwork
51         #self.BoxID = iNetwork.getAdapterAttribute("eth0", "mac")
52         #self.DeviceName = HardwareInfo().get_device_name()
53         #from Components.About import about
54         #self.EnigmaVersion = about.getEnigmaVersionString()
55         #self.ImageVersion = about.getVersionString()
56 except:
57         DEVICE = ''
58
59 WHERE_EPGMENU     = 'WHERE_EPGMENU'
60
61
62 #######################################################
63 # Initialize Configuration
64 config.plugins.seriesplugin = ConfigSubsection()
65
66 config.plugins.seriesplugin.enabled                   = ConfigEnableDisable(default = False)
67
68 config.plugins.seriesplugin.menu_info                 = ConfigYesNo(default = True)
69 config.plugins.seriesplugin.menu_extensions           = ConfigYesNo(default = False)
70 config.plugins.seriesplugin.menu_epg                  = ConfigYesNo(default = False)
71 config.plugins.seriesplugin.menu_channel              = ConfigYesNo(default = True)
72 config.plugins.seriesplugin.menu_movie_info           = ConfigYesNo(default = True)
73 config.plugins.seriesplugin.menu_movie_rename         = ConfigYesNo(default = True)
74
75 #TODO config.plugins.seriesplugin.open MessageBox or TheTVDB  ConfigSelection if hasTheTVDB
76
77 config.plugins.seriesplugin.identifier_elapsed        = ConfigText(default = "", fixed_size = False)
78 config.plugins.seriesplugin.identifier_today          = ConfigText(default = "", fixed_size = False)
79 config.plugins.seriesplugin.identifier_future         = ConfigText(default = "", fixed_size = False)
80
81 #config.plugins.seriesplugin.manager                   = ConfigSelection(choices = [("", "")], default = "")
82 #config.plugins.seriesplugin.guide                     = ConfigSelection(choices = [("", "")], default = "")
83
84 config.plugins.seriesplugin.pattern_file              = ConfigText(default = "/etc/enigma2/seriesplugin_patterns.json", fixed_size = False)
85 config.plugins.seriesplugin.pattern_title             = ConfigText(default = "{org:s} S{season:02d}E{episode:02d} {title:s}", fixed_size = False)
86 config.plugins.seriesplugin.pattern_description       = ConfigText(default = "S{season:02d}E{episode:02d} {title:s} {org:s}", fixed_size = False)
87 #config.plugins.seriesplugin.pattern_record            = ConfigText(default = "{org:s} S{season:02d}E{episode:02d} {title:s}", fixed_size = False)
88
89 config.plugins.seriesplugin.channel_file              = ConfigText(default = "/etc/enigma2/seriesplugin_channels.xml", fixed_size = False)
90 config.plugins.seriesplugin.channel_popups            = ConfigYesNo(default = False)
91
92 config.plugins.seriesplugin.tidy_rename               = ConfigYesNo(default = False)
93 config.plugins.seriesplugin.rename_file               = ConfigYesNo(default = True)
94
95 config.plugins.seriesplugin.max_time_drift            = ConfigSelectionNumber(0, 600, 1, default = 15)
96 config.plugins.seriesplugin.search_depths             = ConfigSelectionNumber(0, 10, 1, default = 0)
97
98 config.plugins.seriesplugin.skip_during_records       = ConfigYesNo(default=False)
99
100 config.plugins.seriesplugin.autotimer_independent     = ConfigYesNo(default = False)
101 config.plugins.seriesplugin.independent_cycle         = ConfigSelectionNumber(5, 24*60, 5, default = 60)
102 config.plugins.seriesplugin.independent_retry         = ConfigYesNo(default = False)
103
104 config.plugins.seriesplugin.check_timer_list          = ConfigYesNo(default = False)
105
106 #NoTimerPopUpPossibleActually
107 #config.plugins.seriesplugin.timer_popups              = ConfigYesNo(default = True)
108
109 config.plugins.seriesplugin.caching                   = ConfigYesNo(default = True)
110
111 #config.plugins.seriesplugin.debug                     = ConfigYesNo(default = False)
112 config.plugins.seriesplugin.write_log                 = ConfigYesNo(default = False)
113 config.plugins.seriesplugin.log_file                  = ConfigText(default = "/tmp/seriesplugin.log", fixed_size = False)
114 config.plugins.seriesplugin.log_reply_user            = ConfigText(default = "Dreambox User", fixed_size = False)
115 config.plugins.seriesplugin.log_reply_mail            = ConfigText(default = "myemail@home.com", fixed_size = False)
116
117 config.plugins.seriesplugin.ganalytics                = ConfigYesNo(default = True)
118
119 # Internal
120 config.plugins.seriesplugin.lookup_counter            = ConfigNumber(default = 0)
121 #config.plugins.seriesplugin.uid                       = ConfigText(default = str(time()), fixed_size = False)
122
123
124 #######################################################
125 # Start
126 def start(reason, **kwargs):
127         if config.plugins.seriesplugin.enabled.value:
128                 # Startup
129                 if reason == 0:
130                         # Start on demand if it is requested
131                         if config.plugins.seriesplugin.autotimer_independent.value:
132                                 startIndependent()
133                         
134                 # Shutdown
135                 elif reason == 1:
136                         from SeriesPlugin import resetInstance
137                         resetInstance()
138
139
140 #######################################################
141 # Plugin configuration
142 def setup(session, *args, **kwargs):
143         try:
144                 session.open(SeriesPluginConfiguration)
145         except Exception as e:
146                 splog(_("SeriesPlugin setup exception ") + str(e))
147                 #exc_type, exc_value, exc_traceback = sys.exc_info()
148                 #splog( exc_type, exc_value, exc_traceback )
149
150
151 #######################################################
152 # Event Info
153 def info(session, service=None, event=None, *args, **kwargs):
154         if config.plugins.seriesplugin.enabled.value:
155                 try:
156         #TBD Because of E2 Update 05.2013
157                         session.open(SeriesPluginInfoScreen, service, event)
158                 except Exception as e:
159                         splog(_("SeriesPlugin info exception ") + str(e))
160                         #exc_type, exc_value, exc_traceback = sys.exc_info()
161                         #splog( exc_type, exc_value, exc_traceback )
162
163
164 #######################################################
165 # Extensions menu
166 def extension(session, *args, **kwargs):
167         if config.plugins.seriesplugin.enabled.value:
168                 try:
169         #TBD Because of E2 Update 05.2013
170                         session.open(SeriesPluginInfoScreen)
171                 except Exception as e:
172                         splog(_("SeriesPlugin extension exception ") + str(e))
173                         #exc_type, exc_value, exc_traceback = sys.exc_info()
174                         #splog( exc_type, exc_value, exc_traceback )
175
176
177 #######################################################
178 # Channel menu
179 def channel(session, service=None, *args, **kwargs):
180         if config.plugins.seriesplugin.enabled.value:
181                 try:
182                         from enigma import eServiceCenter
183                         info = eServiceCenter.getInstance().info(service)
184                         event = info.getEvent(service)
185                         session.open(SeriesPluginInfoScreen, service, event)
186                 except Exception as e:
187                         splog(_("SeriesPlugin extension exception ") + str(e))
188
189
190 #######################################################
191 # Timer
192 def checkTimers(session, *args, **kwargs):
193         runIndependent()
194
195
196 #######################################################
197 # Movielist menu rename
198 def movielist_rename(session, service, services=None, *args, **kwargs):
199         if config.plugins.seriesplugin.enabled.value:
200                 try:
201                         if services:
202                                 if not isinstance(services, list):
203                                         services = [services]   
204                         else:
205                                 services = [service]
206                         SeriesPluginRenamer(session, services)
207                 except Exception as e:
208                         splog(_("SeriesPlugin renamer exception ") + str(e))
209                         #exc_type, exc_value, exc_traceback = sys.exc_info()
210                         #splog( exc_type, exc_value, exc_traceback )
211
212
213 #######################################################
214 # Movielist menu info
215 def movielist_info(session, service, *args, **kwargs):
216         if config.plugins.seriesplugin.enabled.value:
217                 try:
218         #TBD Because of E2 Update 05.2013
219                         session.open(SeriesPluginInfoScreen, service)
220                 except Exception as e:
221                         splog(_("SeriesPlugin extension exception ") + str(e))
222                         #exc_type, exc_value, exc_traceback = sys.exc_info()
223                         #splog( exc_type, exc_value, exc_traceback )
224
225
226 #######################################################
227 # Timer renaming
228 def renameTimer(timer, name, begin, end, *args, **kwargs):
229         if config.plugins.seriesplugin.enabled.value:
230                 try:
231                         SeriesPluginTimer(timer, name, begin, end)
232                 except Exception as e:
233                         splog(_("SeriesPlugin label exception ") + str(e))
234                         #exc_type, exc_value, exc_traceback = sys.exc_info()
235                         #splog( exc_type, exc_value, exc_traceback )
236
237
238 # For compatibility reasons
239 def modifyTimer(timer, name, *args, **kwargs):
240         if config.plugins.seriesplugin.enabled.value:
241                 splog("SeriesPlugin modifyTimer is deprecated - Update Your AutoTimer!")
242                 try:
243                         SeriesPluginTimer(timer, name or timer.name, timer.begin, timer.end)
244                 except Exception as e:
245                         splog(_("SeriesPlugin label exception ") + str(e))
246                         #exc_type, exc_value, exc_traceback = sys.exc_info()
247                         #splog( exc_type, exc_value, exc_traceback )
248
249
250 # For compatibility reasons
251 def labelTimer(timer, begin=None, end=None, *args, **kwargs):
252         if config.plugins.seriesplugin.enabled.value:
253                 splog("SeriesPlugin labelTimer is deprecated - Update Your AutoTimer!")
254                 try:
255                         SeriesPluginTimer(timer, timer.name, timer.begin, timer.end)
256                 except Exception as e:
257                         splog(_("SeriesPlugin label exception ") + str(e))
258                         #exc_type, exc_value, exc_traceback = sys.exc_info()
259                         #splog( exc_type, exc_value, exc_traceback )
260
261
262 #######################################################
263 # Plugin main function
264 def Plugins(**kwargs):
265         descriptors = []
266         
267         #TODO icon
268         descriptors.append( PluginDescriptor(
269                                                                                         name = NAME + " " + _("Setup"),
270                                                                                         description = NAME + " " + _("Setup"),
271                                                                                         where = PluginDescriptor.WHERE_PLUGINMENU,
272                                                                                         fnc = setup,
273                                                                                         needsRestart = False) )
274         
275         if config.plugins.seriesplugin.enabled.value:
276                 
277                 overwriteAutoTimer()
278                 
279                 descriptors.append( PluginDescriptor(
280                                                                                                         #where = PluginDescriptor.WHERE_SESSIONSTART,
281                                                                                                         where = PluginDescriptor.WHERE_AUTOSTART,
282                                                                                                         needsRestart = False,
283                                                                                                         fnc = start) )
284
285                 if config.plugins.seriesplugin.menu_info.value:
286                         descriptors.append( PluginDescriptor(
287                                                                                                         name = SHOWINFO,
288                                                                                                         description = SHOWINFO,
289                                                                                                         where = PluginDescriptor.WHERE_EVENTINFO,
290                                                                                                         needsRestart = False,
291                                                                                                         fnc = info) )
292
293                 if config.plugins.seriesplugin.menu_extensions.value:
294                         descriptors.append(PluginDescriptor(
295                                                                                                         name = SHOWINFO,
296                                                                                                         description = SHOWINFO,
297                                                                                                         where = PluginDescriptor.WHERE_EXTENSIONSMENU,
298                                                                                                         fnc = extension,
299                                                                                                         needsRestart = False) )
300                         
301                 if config.plugins.seriesplugin.menu_channel.value:
302                         descriptors.append( PluginDescriptor(
303                                                                                                         name = SHOWINFO,
304                                                                                                         description = SHOWINFO,
305                                                                                                         where = PluginDescriptor.WHERE_CHANNEL_CONTEXT_MENU,
306                                                                                                         fnc = channel,
307                                                                                                         needsRestart = False) )
308                 
309                 if config.plugins.seriesplugin.check_timer_list.value:
310                         descriptors.append(PluginDescriptor(
311                                                                                                         name = CHECKTIMERS,
312                                                                                                         description = CHECKTIMERS,
313                                                                                                         where = PluginDescriptor.WHERE_EXTENSIONSMENU,
314                                                                                                         fnc = checkTimers,
315                                                                                                         needsRestart = False) )
316                 
317                 if config.plugins.seriesplugin.menu_movie_info.value:
318                         descriptors.append( PluginDescriptor(
319                                                                                                         name = SHOWINFO,
320                                                                                                         description = SHOWINFO,
321                                                                                                         where = PluginDescriptor.WHERE_MOVIELIST,
322                                                                                                         fnc = movielist_info,
323                                                                                                         needsRestart = False) )
324                 
325                 if config.plugins.seriesplugin.menu_movie_rename.value:
326                         descriptors.append( PluginDescriptor(
327                                                                                                         name = RENAMESERIES,
328                                                                                                         description = RENAMESERIES,
329                                                                                                         where = PluginDescriptor.WHERE_MOVIELIST,
330                                                                                                         fnc = movielist_rename,
331                                                                                                         needsRestart = False) )
332                 
333                 if config.plugins.seriesplugin.menu_epg.value:
334                         addSeriesPlugin(WHERE_EPGMENU, SHOWINFO)
335
336         return descriptors
337
338
339 #######################################################
340 # Override EPGSelection enterDateTime
341 EPGSelection_enterDateTime = None
342 #EPGSelection_openOutdatedEPGSelection = None
343 def SPEPGSelectionInit():
344         print "SeriesPlugin override EPGSelection"
345         global EPGSelection_enterDateTime, EPGSelection_openOutdatedEPGSelection
346         if EPGSelection_enterDateTime is None: # and EPGSelection_openOutdatedEPGSelection is None:
347                 from Screens.EpgSelection import EPGSelection
348                 EPGSelection_enterDateTime = EPGSelection.enterDateTime
349                 EPGSelection.enterDateTime = enterDateTime
350                 #EPGSelection_openOutdatedEPGSelection = EPGSelection.openOutdatedEPGSelection
351                 #EPGSelection.openOutdatedEPGSelection = openOutdatedEPGSelection
352                 EPGSelection.SPcloseafterfinish = closeafterfinish
353
354 def SPEPGSelectionUndo():
355         print "SeriesPlugin undo override EPGSelection"
356         global EPGSelection_enterDateTime, EPGSelection_openOutdatedEPGSelection
357         if EPGSelection_enterDateTime and EPGSelection_openOutdatedEPGSelection:
358                 from Screens.EpgSelection import EPGSelection
359                 EPGSelection.enterDateTime = EPGSelection_enterDateTime
360                 EPGSelection_enterDateTime = None
361                 #EPGSelection.openOutdatedEPGSelection = EPGSelection_openOutdatedEPGSelection
362                 #EPGSelection_openOutdatedEPGSelection = None
363
364 def enterDateTime(self):
365         from Screens.EpgSelection import EPG_TYPE_SINGLE,EPG_TYPE_MULTI,EPG_TYPE_SIMILAR
366         event = self["Event"].event
367         if self.type == EPG_TYPE_SINGLE:
368                 service = self.currentService
369         elif self.type == EPG_TYPE_MULTI:       
370                 service = self.services
371         elif self.type == EPG_TYPE_SIMILAR:
372                 service = self.currentService
373         if service and event:
374                 self.session.openWithCallback(self.SPcloseafterfinish, SeriesPluginInfoScreen, service, event) 
375                 return
376         EPGSelection_enterDateTime(self)
377
378 #def openOutdatedEPGSelection(self, reason=None):
379 #       if reason == 1:
380 #               EPGSelection_enterDateTime(self)
381
382 def closeafterfinish(self, retval=None):
383         self.close()
384
385
386 #######################################################
387 # Add / Remove menu functions
388 def addSeriesPlugin(menu, title, fnc=None):
389         # Add to menu
390         if( menu == WHERE_EPGMENU ):
391                 SPEPGSelectionInit()
392         else:
393                 from Components.PluginComponent import plugins
394                 if plugins:
395                         for p in plugins.getPlugins( where = menu ):
396                                 if p.name == title:
397                                         # Plugin is already in menu
398                                         break
399                         else:
400                                 # Plugin not in menu - add it
401                                 plugin = PluginDescriptor(
402                                                                                                                                 name = title,
403                                                                                                                                 description = title,
404                                                                                                                                 where = menu,
405                                                                                                                                 needsRestart = False,
406                                                                                                                                 fnc = fnc)
407                                 if menu in plugins.plugins:
408                                         plugins.plugins[ menu ].append(plugin)
409
410
411 def removeSeriesPlugin(menu, title):
412         # Remove from menu
413         if( menu == WHERE_EPGMENU ):
414                 SPEPGSelectionUndo()
415         else:
416                 from Components.PluginComponent import plugins
417                 if plugins:
418                         for p in plugins.getPlugins( where = menu ):
419                                 if p.name == title:
420                                         plugins.plugins[ menu ].remove(p)
421                                         break
422
423
424 #######################################################
425 # Overwrite AutoTimer support functions
426
427 try:
428         from Plugins.Extensions.AutoTimer.AutoTimer import AutoTimer
429         #from Plugins.Extensions.AutoTimer.plugin import autotimer as AutoTimer
430 except:
431         AutoTimer = None
432
433 ATmodifyTimer = None
434 ATcheckSimilarity = None
435
436
437 def overwriteAutoTimer():
438         try:
439                 global ATmodifyTimer, ATcheckSimilarity
440                 if AutoTimer:
441                         if ATmodifyTimer is None:
442                                 # Backup original function
443                                 ATmodifyTimer = AutoTimer.modifyTimer
444                                 # Overwrite function
445                                 AutoTimer.modifyTimer = SPmodifyTimer
446                         if ATcheckSimilarity is None:
447                                 # Backup original function
448                                 ATcheckSimilarity = AutoTimer.checkSimilarity
449                                 # Overwrite function
450                                 AutoTimer.checkSimilarity = SPcheckSimilarity
451         except:
452                 splog("SeriesPlugin found old AutoTimer")
453
454
455 def recoverAutoTimer():
456         try:
457                 global ATmodifyTimer, ATcheckSimilarity
458                 if AutoTimer:
459                         if ATmodifyTimer:
460                                 AutoTimer.modifyTimer = ATmodifyTimer
461                                 ATmodifyTimer = None
462                         if ATcheckSimilarity:
463                                 AutoTimer.checkSimilarity = ATcheckSimilarity
464                                 ATcheckSimilarity = None
465         except:
466                 splog("SeriesPlugin found old AutoTimer")
467
468
469 #######################################################
470 # Customized support functions
471
472 from difflib import SequenceMatcher
473 from ServiceReference import ServiceReference
474
475 def SPmodifyTimer(self, timer, name, shortdesc, begin, end, serviceref, eit=None):
476         # Never overwrite existing names, You will lose Your series informations
477         #timer.name = name
478         # Only overwrite non existing descriptions
479         timer.description = timer.description or shortdesc
480         timer.begin = int(begin)
481         timer.end = int(end)
482         timer.service_ref = ServiceReference(serviceref)
483         if eit:
484                 timer.eit = eit
485
486 def SPcheckSimilarity(self, timer, name1, name2, shortdesc1, shortdesc2, extdesc1, extdesc2, force=False):
487         # Check if the new title is part of the existing one
488         foundTitle = name1 in name2 or name2 in name1
489         
490         if timer.searchForDuplicateDescription > 0 or force:
491                 foundShort = (shortdesc1 in shortdesc2 or shortdesc2 in shortdesc1) if (timer.searchForDuplicateDescription > 0 or force) else True
492                 
493                 # NOTE: only check extended if short description already is a match because otherwise
494                 # it won't evaluate to True anyway
495                 if (timer.searchForDuplicateDescription > 0 or force) and foundShort and extdesc1 != extdesc2:
496                         # Some channels indicate replays in the extended descriptions
497                         # If the similarity percent is higher then 0.8 it is a very close match
498                         #if timer.series_labeling and (extdesc1 == "" or extdesc2 == ""):
499                         #       foundExt = True
500                         #else:
501                         foundExt = ( 0.8 < SequenceMatcher(lambda x: x == " ",extdesc1, extdesc2).ratio() )
502                 else:
503                         foundExt = True
504         else:
505                 foundShort = True
506                 foundExt = True
507         
508         return foundTitle and foundShort and foundExt