SeriesPlugin 1.2.8: Fixed multiple timer / movie handling
[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.8_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 config.plugins.seriesplugin.rename_popups             = ConfigYesNo(default = True)
95 config.plugins.seriesplugin.rename_popups_success     = ConfigYesNo(default = False)
96
97 config.plugins.seriesplugin.max_time_drift            = ConfigSelectionNumber(0, 600, 1, default = 15)
98 config.plugins.seriesplugin.search_depths             = ConfigSelectionNumber(0, 10, 1, default = 0)
99
100 config.plugins.seriesplugin.skip_during_records       = ConfigYesNo(default=False)
101
102 config.plugins.seriesplugin.autotimer_independent     = ConfigYesNo(default = False)
103 config.plugins.seriesplugin.independent_cycle         = ConfigSelectionNumber(5, 24*60, 5, default = 60)
104 config.plugins.seriesplugin.independent_retry         = ConfigYesNo(default = False)
105
106 config.plugins.seriesplugin.check_timer_list          = ConfigYesNo(default = False)
107
108 config.plugins.seriesplugin.timer_popups              = ConfigYesNo(default = True)
109 config.plugins.seriesplugin.timer_popups_success      = ConfigYesNo(default = False)
110
111 config.plugins.seriesplugin.caching                   = ConfigYesNo(default = True)
112
113 #config.plugins.seriesplugin.debug                     = ConfigYesNo(default = False)
114 config.plugins.seriesplugin.write_log                 = ConfigYesNo(default = False)
115 config.plugins.seriesplugin.log_file                  = ConfigText(default = "/tmp/seriesplugin.log", fixed_size = False)
116 config.plugins.seriesplugin.log_reply_user            = ConfigText(default = "Dreambox User", fixed_size = False)
117 config.plugins.seriesplugin.log_reply_mail            = ConfigText(default = "myemail@home.com", fixed_size = False)
118
119 config.plugins.seriesplugin.ganalytics                = ConfigYesNo(default = True)
120
121 # Internal
122 config.plugins.seriesplugin.lookup_counter            = ConfigNumber(default = 0)
123 #config.plugins.seriesplugin.uid                       = ConfigText(default = str(time()), fixed_size = False)
124
125
126 #######################################################
127 # Start
128 def start(reason, **kwargs):
129         if config.plugins.seriesplugin.enabled.value:
130                 # Startup
131                 if reason == 0:
132                         # Start on demand if it is requested
133                         if config.plugins.seriesplugin.autotimer_independent.value:
134                                 startIndependent()
135                         
136                 # Shutdown
137                 elif reason == 1:
138                         from SeriesPlugin import resetInstance
139                         resetInstance()
140
141
142 #######################################################
143 # Plugin configuration
144 def setup(session, *args, **kwargs):
145         try:
146                 session.open(SeriesPluginConfiguration)
147         except Exception as e:
148                 splog(_("SeriesPlugin setup exception ") + str(e))
149                 #exc_type, exc_value, exc_traceback = sys.exc_info()
150                 #splog( exc_type, exc_value, exc_traceback )
151
152
153 #######################################################
154 # Event Info
155 def info(session, service=None, event=None, *args, **kwargs):
156         if config.plugins.seriesplugin.enabled.value:
157                 try:
158                         session.open(SeriesPluginInfoScreen, service, event)
159                 except Exception as e:
160                         splog(_("SeriesPlugin info exception ") + str(e))
161                         #exc_type, exc_value, exc_traceback = sys.exc_info()
162                         #splog( exc_type, exc_value, exc_traceback )
163
164
165 #######################################################
166 # Extensions menu
167 def extension(session, *args, **kwargs):
168         if config.plugins.seriesplugin.enabled.value:
169                 try:
170                         session.open(SeriesPluginInfoScreen)
171                 except Exception as e:
172                         splog(_("SeriesPlugin extension exception ") + str(e))
173
174
175 #######################################################
176 # Channel menu
177 def channel(session, service=None, *args, **kwargs):
178         if config.plugins.seriesplugin.enabled.value:
179                 try:
180                         from enigma import eServiceCenter
181                         info = eServiceCenter.getInstance().info(service)
182                         event = info.getEvent(service)
183                         session.open(SeriesPluginInfoScreen, service, event)
184                 except Exception as e:
185                         splog(_("SeriesPlugin extension exception ") + str(e))
186
187
188 #######################################################
189 # Timer
190 def checkTimers(session, *args, **kwargs):
191         runIndependent()
192
193
194 #######################################################
195 # Movielist menu rename
196 def movielist_rename(session, service, services=None, *args, **kwargs):
197         if config.plugins.seriesplugin.enabled.value:
198                 try:
199                         if services:
200                                 if not isinstance(services, list):
201                                         services = [services]   
202                         else:
203                                 services = [service]
204                         SeriesPluginRenamer(session, services)
205                 except Exception as e:
206                         splog(_("SeriesPlugin renamer exception ") + str(e))
207
208
209 #######################################################
210 # Movielist menu info
211 def movielist_info(session, service, *args, **kwargs):
212         if config.plugins.seriesplugin.enabled.value:
213                 try:
214                         session.open(SeriesPluginInfoScreen, service)
215                 except Exception as e:
216                         splog(_("SeriesPlugin extension exception ") + str(e))
217
218
219 #######################################################
220 # Timer renaming
221 def renameTimer(timer, name, begin, end, *args, **kwargs):
222         if config.plugins.seriesplugin.enabled.value:
223                 try:
224                         SeriesPluginTimer(timer, name, begin, end)
225                 except Exception as e:
226                         splog(_("SeriesPlugin label exception ") + str(e))
227
228
229 # For compatibility reasons
230 def modifyTimer(timer, name, *args, **kwargs):
231         if config.plugins.seriesplugin.enabled.value:
232                 splog("SeriesPlugin modifyTimer is deprecated - Update Your AutoTimer!")
233                 try:
234                         SeriesPluginTimer(timer, name or timer.name, timer.begin, timer.end)
235                 except Exception as e:
236                         splog(_("SeriesPlugin label exception ") + str(e))
237
238
239 # For compatibility reasons
240 def labelTimer(timer, begin=None, end=None, *args, **kwargs):
241         if config.plugins.seriesplugin.enabled.value:
242                 splog("SeriesPlugin labelTimer is deprecated - Update Your AutoTimer!")
243                 try:
244                         SeriesPluginTimer(timer, timer.name, timer.begin, timer.end)
245                 except Exception as e:
246                         splog(_("SeriesPlugin label exception ") + str(e))
247
248
249 #######################################################
250 # Plugin main function
251 def Plugins(**kwargs):
252         descriptors = []
253         
254         #TODO icon
255         descriptors.append( PluginDescriptor(
256                                                                                         name = NAME + " " + _("Setup"),
257                                                                                         description = NAME + " " + _("Setup"),
258                                                                                         where = PluginDescriptor.WHERE_PLUGINMENU,
259                                                                                         fnc = setup,
260                                                                                         needsRestart = False) )
261         
262         if config.plugins.seriesplugin.enabled.value:
263                 
264                 overwriteAutoTimer()
265                 
266                 descriptors.append( PluginDescriptor(
267                                                                                                         #where = PluginDescriptor.WHERE_SESSIONSTART,
268                                                                                                         where = PluginDescriptor.WHERE_AUTOSTART,
269                                                                                                         needsRestart = False,
270                                                                                                         fnc = start) )
271
272                 if config.plugins.seriesplugin.menu_info.value:
273                         descriptors.append( PluginDescriptor(
274                                                                                                         name = SHOWINFO,
275                                                                                                         description = SHOWINFO,
276                                                                                                         where = PluginDescriptor.WHERE_EVENTINFO,
277                                                                                                         needsRestart = False,
278                                                                                                         fnc = info) )
279
280                 if config.plugins.seriesplugin.menu_extensions.value:
281                         descriptors.append(PluginDescriptor(
282                                                                                                         name = SHOWINFO,
283                                                                                                         description = SHOWINFO,
284                                                                                                         where = PluginDescriptor.WHERE_EXTENSIONSMENU,
285                                                                                                         fnc = extension,
286                                                                                                         needsRestart = False) )
287                         
288                 if config.plugins.seriesplugin.menu_channel.value:
289                         descriptors.append( PluginDescriptor(
290                                                                                                         name = SHOWINFO,
291                                                                                                         description = SHOWINFO,
292                                                                                                         where = PluginDescriptor.WHERE_CHANNEL_CONTEXT_MENU,
293                                                                                                         fnc = channel,
294                                                                                                         needsRestart = False) )
295                 
296                 if config.plugins.seriesplugin.check_timer_list.value:
297                         descriptors.append(PluginDescriptor(
298                                                                                                         name = CHECKTIMERS,
299                                                                                                         description = CHECKTIMERS,
300                                                                                                         where = PluginDescriptor.WHERE_EXTENSIONSMENU,
301                                                                                                         fnc = checkTimers,
302                                                                                                         needsRestart = False) )
303                 
304                 if config.plugins.seriesplugin.menu_movie_info.value:
305                         descriptors.append( PluginDescriptor(
306                                                                                                         name = SHOWINFO,
307                                                                                                         description = SHOWINFO,
308                                                                                                         where = PluginDescriptor.WHERE_MOVIELIST,
309                                                                                                         fnc = movielist_info,
310                                                                                                         needsRestart = False) )
311                 
312                 if config.plugins.seriesplugin.menu_movie_rename.value:
313                         descriptors.append( PluginDescriptor(
314                                                                                                         name = RENAMESERIES,
315                                                                                                         description = RENAMESERIES,
316                                                                                                         where = PluginDescriptor.WHERE_MOVIELIST,
317                                                                                                         fnc = movielist_rename,
318                                                                                                         needsRestart = False) )
319                 
320                 if config.plugins.seriesplugin.menu_epg.value:
321                         addSeriesPlugin(WHERE_EPGMENU, SHOWINFO)
322
323         return descriptors
324
325
326 #######################################################
327 # Override EPGSelection enterDateTime
328 EPGSelection_enterDateTime = None
329 #EPGSelection_openOutdatedEPGSelection = None
330 def SPEPGSelectionInit():
331         print "SeriesPlugin override EPGSelection"
332         global EPGSelection_enterDateTime, EPGSelection_openOutdatedEPGSelection
333         if EPGSelection_enterDateTime is None: # and EPGSelection_openOutdatedEPGSelection is None:
334                 from Screens.EpgSelection import EPGSelection
335                 EPGSelection_enterDateTime = EPGSelection.enterDateTime
336                 EPGSelection.enterDateTime = enterDateTime
337                 #EPGSelection_openOutdatedEPGSelection = EPGSelection.openOutdatedEPGSelection
338                 #EPGSelection.openOutdatedEPGSelection = openOutdatedEPGSelection
339                 EPGSelection.SPcloseafterfinish = closeafterfinish
340
341 def SPEPGSelectionUndo():
342         print "SeriesPlugin undo override EPGSelection"
343         global EPGSelection_enterDateTime, EPGSelection_openOutdatedEPGSelection
344         if EPGSelection_enterDateTime and EPGSelection_openOutdatedEPGSelection:
345                 from Screens.EpgSelection import EPGSelection
346                 EPGSelection.enterDateTime = EPGSelection_enterDateTime
347                 EPGSelection_enterDateTime = None
348                 #EPGSelection.openOutdatedEPGSelection = EPGSelection_openOutdatedEPGSelection
349                 #EPGSelection_openOutdatedEPGSelection = None
350
351 def enterDateTime(self):
352         from Screens.EpgSelection import EPG_TYPE_SINGLE,EPG_TYPE_MULTI,EPG_TYPE_SIMILAR
353         event = self["Event"].event
354         if self.type == EPG_TYPE_SINGLE:
355                 service = self.currentService
356         elif self.type == EPG_TYPE_MULTI:       
357                 service = self.services
358         elif self.type == EPG_TYPE_SIMILAR:
359                 service = self.currentService
360         if service and event:
361                 self.session.openWithCallback(self.SPcloseafterfinish, SeriesPluginInfoScreen, service, event) 
362                 return
363         EPGSelection_enterDateTime(self)
364
365 #def openOutdatedEPGSelection(self, reason=None):
366 #       if reason == 1:
367 #               EPGSelection_enterDateTime(self)
368
369 def closeafterfinish(self, retval=None):
370         self.close()
371
372
373 #######################################################
374 # Add / Remove menu functions
375 def addSeriesPlugin(menu, title, fnc=None):
376         # Add to menu
377         if( menu == WHERE_EPGMENU ):
378                 SPEPGSelectionInit()
379         else:
380                 from Components.PluginComponent import plugins
381                 if plugins:
382                         for p in plugins.getPlugins( where = menu ):
383                                 if p.name == title:
384                                         # Plugin is already in menu
385                                         break
386                         else:
387                                 # Plugin not in menu - add it
388                                 plugin = PluginDescriptor(
389                                                                                                                                 name = title,
390                                                                                                                                 description = title,
391                                                                                                                                 where = menu,
392                                                                                                                                 needsRestart = False,
393                                                                                                                                 fnc = fnc)
394                                 if menu in plugins.plugins:
395                                         plugins.plugins[ menu ].append(plugin)
396
397
398 def removeSeriesPlugin(menu, title):
399         # Remove from menu
400         if( menu == WHERE_EPGMENU ):
401                 SPEPGSelectionUndo()
402         else:
403                 from Components.PluginComponent import plugins
404                 if plugins:
405                         for p in plugins.getPlugins( where = menu ):
406                                 if p.name == title:
407                                         plugins.plugins[ menu ].remove(p)
408                                         break
409
410
411 #######################################################
412 # Overwrite AutoTimer support functions
413
414 try:
415         from Plugins.Extensions.AutoTimer.AutoTimer import AutoTimer
416         #from Plugins.Extensions.AutoTimer.plugin import autotimer as AutoTimer
417 except:
418         AutoTimer = None
419
420 ATmodifyTimer = None
421 ATcheckSimilarity = None
422
423
424 def overwriteAutoTimer():
425         try:
426                 global ATmodifyTimer, ATcheckSimilarity
427                 if AutoTimer:
428                         if ATmodifyTimer is None:
429                                 # Backup original function
430                                 ATmodifyTimer = AutoTimer.modifyTimer
431                                 # Overwrite function
432                                 AutoTimer.modifyTimer = SPmodifyTimer
433                         if ATcheckSimilarity is None:
434                                 # Backup original function
435                                 ATcheckSimilarity = AutoTimer.checkSimilarity
436                                 # Overwrite function
437                                 AutoTimer.checkSimilarity = SPcheckSimilarity
438         except:
439                 splog("SeriesPlugin found old AutoTimer")
440
441
442 def recoverAutoTimer():
443         try:
444                 global ATmodifyTimer, ATcheckSimilarity
445                 if AutoTimer:
446                         if ATmodifyTimer:
447                                 AutoTimer.modifyTimer = ATmodifyTimer
448                                 ATmodifyTimer = None
449                         if ATcheckSimilarity:
450                                 AutoTimer.checkSimilarity = ATcheckSimilarity
451                                 ATcheckSimilarity = None
452         except:
453                 splog("SeriesPlugin found old AutoTimer")
454
455
456 #######################################################
457 # Customized support functions
458
459 from difflib import SequenceMatcher
460 from ServiceReference import ServiceReference
461
462 def SPmodifyTimer(self, timer, name, shortdesc, begin, end, serviceref, eit=None):
463         # Never overwrite existing names, You will lose Your series informations
464         #timer.name = name
465         # Only overwrite non existing descriptions
466         timer.description = timer.description or shortdesc
467         timer.begin = int(begin)
468         timer.end = int(end)
469         timer.service_ref = ServiceReference(serviceref)
470         if eit:
471                 timer.eit = eit
472
473 def SPcheckSimilarity(self, timer, name1, name2, shortdesc1, shortdesc2, extdesc1, extdesc2, force=False):
474         # Check if the new title is part of the existing one
475         foundTitle = name1 in name2 or name2 in name1
476         
477         if timer.searchForDuplicateDescription > 0 or force:
478                 foundShort = (shortdesc1 in shortdesc2 or shortdesc2 in shortdesc1) if (timer.searchForDuplicateDescription > 0 or force) else True
479                 
480                 # NOTE: only check extended if short description already is a match because otherwise
481                 # it won't evaluate to True anyway
482                 if (timer.searchForDuplicateDescription > 0 or force) and foundShort and extdesc1 != extdesc2:
483                         # Some channels indicate replays in the extended descriptions
484                         # If the similarity percent is higher then 0.8 it is a very close match
485                         #if timer.series_labeling and (extdesc1 == "" or extdesc2 == ""):
486                         #       foundExt = True
487                         #else:
488                         foundExt = ( 0.8 < SequenceMatcher(lambda x: x == " ",extdesc1, extdesc2).ratio() )
489                 else:
490                         foundExt = True
491         else:
492                 foundShort = True
493                 foundExt = True
494         
495         return foundTitle and foundShort and foundExt