SeriesPlugin 2.2.2: Bulk update
[enigma2-plugins.git] / seriesplugin / src / SeriesPluginInfoScreen.py
1 # -*- coding: utf-8 -*-
2 #######################################################################
3 #
4 #    Series Plugin for Enigma-2
5 #    Coded by betonme (c) 2012 <glaserfrank(at)gmail.com>
6 #    Support: http://www.i-have-a-dreambox.com/wbb2/thread.php?threadid=TBD
7 #
8 #    This program is free software; you can redistribute it and/or
9 #    modify it under the terms of the GNU General Public License
10 #    as published by the Free Software Foundation; either version 2
11 #    of the License, or (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #######################################################################
19
20 import os
21 import re
22
23
24 # for localized messages
25 from . import _
26 #from time import time
27 from datetime import datetime
28
29 # Config
30 from Components.config import config
31
32 from Screens.Screen import Screen
33 from Screens.Setup import SetupSummary
34 from Screens.MessageBox import MessageBox
35 from Screens.ChannelSelection import ChannelSelectionBase
36
37 from Components.AVSwitch import AVSwitch
38 from Components.ActionMap import ActionMap
39 from Components.Button import Button
40 from Components.Label import Label
41 from Components.ScrollLabel import ScrollLabel
42 from Components.Pixmap import Pixmap
43
44 from enigma import eEPGCache, eServiceReference, eServiceCenter, iServiceInformation, ePicLoad, eServiceEvent
45 from ServiceReference import ServiceReference
46
47 from RecordTimer import RecordTimerEntry, parseEvent, AFTEREVENT
48 from Screens.TimerEntry import TimerEntry
49 from Components.UsageConfig import preferredTimerPath
50 from Screens.TimerEdit import TimerSanityConflict
51
52 from Tools.BoundFunction import boundFunction
53 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
54
55
56 # Plugin internal
57 from SeriesPlugin import getInstance
58 from Logger import splog
59
60
61 # Constants
62 PIXMAP_PATH = os.path.join( resolveFilename(SCOPE_PLUGINS), "Extensions/SeriesPlugin/Logos/" )
63
64 instance = None
65
66
67 #######################################################
68 # Info screen
69 class SeriesPluginInfoScreen(Screen):
70         
71         skinfile = os.path.join( resolveFilename(SCOPE_PLUGINS), "Extensions/SeriesPlugin/skin.xml" )
72         skin = open(skinfile).read()
73         
74         def __init__(self, session, service=None, event=None):
75                 Screen.__init__(self, session)
76                 
77                 global instance
78                 instance = self
79                 
80                 self.session = session
81                 self.skinName = [ "SeriesPluginInfoScreen" ]
82                 
83                 self["logo"] = Pixmap()
84                 self["cover"] = Pixmap()
85                 self["state"] = Pixmap()
86                 
87                 self["event_title"] = Label()
88                 self["event_episode"] = Label()
89                 self["event_description"] = ScrollLabel()
90                 self["datetime"] = Label()
91                 self["channel"] = Label()
92                 self["duration"] = Label()
93                 
94                 self["key_red"] = Button("")                            # Rename or Record
95                 self["key_green"] = Button("")                  # Trakt Seen / Not Seen
96                 self["key_yellow"] = Button("")                 # Show all Episodes of current season
97                 self["key_blue"] = Button("")                           # Show all Seasons
98                 
99                 self.redButtonFunction = None
100                 
101                 #TODO HelpableActionMap
102                 self["actions"] = ActionMap(["OkCancelActions", "EventViewActions", "DirectionActions", "ColorActions"],
103                 {
104                         "cancel":    self.close,
105                         "ok":        self.close,
106                         "up":        self["event_description"].pageUp,
107                         "down":      self["event_description"].pageDown,
108                         "red":       self.redButton,
109                         "prevEvent": self.prevEpisode,
110                         "nextEvent": self.nextEpisode,
111                         
112                         #TODO
113                         #"pageUp":    self.pageUp,
114                         #"pageDown":  self.pageDown,
115                         #"openSimilarList": self.openSimilarList
116                 })
117                 
118                 splog("SPI: SeriesPluginInfo", service, event)
119                 self.service = service
120                 self.event = event
121                 
122                 self.name = ""
123                 self.short = ""
124                 self.data = None
125                 
126                 self.path = None
127                 self.eservice = None
128                 
129                 self.epg = eEPGCache.getInstance()
130                 self.serviceHandler = eServiceCenter.getInstance()
131                 self.seriesPlugin = getInstance()
132                 
133                 self.onLayoutFinish.append( self.layoutFinished )
134
135         def layoutFinished(self):
136                 self.setTitle( _("SeriesPlugin Info") )
137                 
138                 self.getEpisode()
139
140         def getEpisode(self):
141                 self.name = ""
142                 self.short = ""
143                 self.data = None
144                 begin, end, duration = 0, 0, 0
145                 ext, channel = "", ""
146                 
147                 today = True #OR future
148                 elapsed = False
149                 
150                 service = self.service
151                 
152                 ref = None
153                 
154                 if isinstance(service, eServiceReference):
155                         #ref = service  #Problem EPG
156                         self.eservice = service
157                         self.path = service.getPath()
158                         if self.path:
159                                 # Service is a movie reference
160                                 info = self.serviceHandler.info(service)
161                                 ref = info.getInfoString(service, iServiceInformation.sServiceref)
162                                 sref = ServiceReference(ref)
163                                 ref = sref.ref
164                                 channel = sref.getServiceName()
165                                 if not channel:
166                                         ref = str(ref)
167                                         ref = re.sub('::.*', ':', ref)
168                                         sref = ServiceReference(ref)
169                                         ref = sref.ref
170                                         channel = sref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')
171                                 #ref = eServiceReference(str(service))
172                                 #ref = service
173                                 # Get information from record meta files
174                                 self.event = info and info.getEvent(service)
175                                 today = False
176                                 elapsed = True
177                                 splog("SPI: eServiceReference movie", str(ref))
178                         else:
179                                 # Service is channel reference
180                                 #ref = eServiceReference(str(service))
181                                 ref = service
182                                 #channel = ServiceReference(ref).getServiceName() or ""
183                                 channel = ServiceReference(str(service)).getServiceName() or ""
184                                 #ref = eServiceReference(service.toString())#
185                                 # Get information from event
186                                 splog("SPI: eServiceReference channel", str(ref))
187                 
188                 elif isinstance(service, ServiceReference):
189                         ref = service.ref
190                         channel = service.getServiceName()
191                         splog("SPI: ServiceReference", str(ref))
192                 
193                 elif isinstance(service, ChannelSelectionBase):
194                         ref = service.getCurrentSelection()
195                         channel = ServiceReference(ref).getServiceName() or ""
196                         splog("SPI: ChannelSelectionBase", str(ref))
197                 
198                 # Fallbacks
199                 if ref is None:
200                         #ref = self.session and self.session.nav.getCurrentService()
201                         #ref = eServiceReference(ref.info().getInfoString(iServiceInformation.sServiceref))
202                         #ref = eServiceReference(str(ref))
203
204                         #service = self.session.nav.getCurrentService()
205                         #info = service and service.info()
206                         #event = info and info.getEvent(0)
207                         
208                         sref = self.session and self.session.nav.getCurrentlyPlayingServiceReference().toString()
209                         ref = eServiceReference(sref)
210                         channel = ServiceReference(str(ref)).getServiceName() or ""
211                         
212                         splog("SPI: Fallback ref", ref, str(ref), channel)
213                 
214                 if not isinstance(self.event, eServiceEvent):
215                         try:
216                                 self.event = ref.valid() and self.epg.lookupEventTime(ref, -1)
217                         except:
218                                 # Maybe it is an old reference
219                                 # Has the movie been renamed earlier?
220                                 # Refresh / reload the list?
221                                 self["event_episode"].setText( "No valid selection!" )
222                                 splog("SPI: No valid selection", str(ref))
223                                 return
224                         #num = event and event.getNumOfLinkageServices() or 0
225                         #for cnt in range(num):
226                         #       subservice = event.getLinkageService(sref, cnt)
227                         # Get information from epg
228                         today = True
229                         elapsed = False
230                         splog("SPI: Fallback event", self.event)
231                 
232                 self.service = ref
233                 
234                 if self.event:
235                         self.name = self.event.getEventName() or ""
236                         begin = self.event.getBeginTime() or 0
237                         duration = self.event.getDuration() or 0
238                         end = begin + duration or 0
239                         # We got the exact margins, no need to adapt it
240                         self.short = self.event.getShortDescription() or ""
241                         ext = self.event.getExtendedDescription() or ""
242                         splog("SPI: event")
243                 
244                 if not begin:
245                         info = self.serviceHandler.info(eServiceReference(str(ref)))
246                         #splog("SPI: info")
247                         if info:
248                                 #splog("SPI: if info")
249                                 begin = info.getInfo(ref, iServiceInformation.sTimeCreate) or 0
250                                 if begin:
251                                         duration = info.getLength(ref) or 0
252                                         end = begin + duration or 0
253                                         #splog("SPI: sTimeCreate")
254                                 else:
255                                         end = os.path.getmtime(ref.getPath()) or 0
256                                         duration = info.getLength(ref) or 0
257                                         begin = end - duration or 0
258                                         #splog("SPI: sTimeCreate else")
259                         elif ref:
260                                 path = ref.getPath()
261                                 #splog("SPI: getPath")
262                                 if path and os.path.exists(path):
263                                         begin = os.path.getmtime(path) or 0
264                                         #splog("SPI: getctime")
265                         #else:
266                                 #MAYBE we could also try to parse the filename
267                         # We don't know the exact margins, we will assume the E2 default margins
268                         begin = begin + (config.recording.margin_before.value * 60)
269                         end = end - (config.recording.margin_after.value * 60)
270                 
271                 self.updateScreen(self.name, _("Retrieving Season, Episode and Title..."), self.short, ext, begin, duration, channel)
272                 
273                 identifier = self.seriesPlugin.getIdentifier(False, today, elapsed)
274                 if identifier:
275                         path = os.path.join(PIXMAP_PATH, identifier+".png")
276                         if os.path.exists(path):
277                                 self.loadPixmap("logo", path )
278                 try:
279                         identifier = self.seriesPlugin.getEpisode(
280                                         self.episodeCallback, 
281                                         #self.name, begin, end, channel, today=today, elapsed=elapsed
282                                         #self.name, begin, end, self.service, today=today, elapsed=elapsed
283                                         self.name, begin, end, ref, today=today, elapsed=elapsed
284                                 )
285                 except Exception as e:
286                         splog("SPI: exception:", str(e))
287
288         def episodeCallback(self, data=None):
289                 #TODO episode list handling
290                 #store the list and just open the first one
291                 
292                 splog("SPI: episodeCallback", data)
293                 #splog(data)
294                 if data and len(data) == 4:
295                         # Episode data available
296                         season, episode, title, series = self.data = data
297                 
298                         if season == 0 and episode == 0:
299                                 custom = _("{title:s}").format( 
300                                                         **{'season': season, 'episode': episode, 'title': title} )
301                         elif season == 0:
302                                 custom = _("Episode: {episode:d}\n{title:s}").format( 
303                                                         **{'season': season, 'episode': episode, 'title': title} )
304                         elif episode == 0:
305                                 custom = _("Season: {season:d}\n{title:s}").format( 
306                                                         **{'season': season, 'episode': episode, 'title': title} )
307                         else:
308                                 custom = _("Season: {season:d}  Episode: {episode:d}\n{title:s}").format( 
309                                                         **{'season': season, 'episode': episode, 'title': title} )
310                         
311                         try:
312                                 self.setColorButtons()
313                         except Exception as e:
314                                 # Screen already closed
315                                 splog("SPI: exception:", str(e))
316                 elif data:
317                         custom = str( data )
318                 else:
319                         custom = _("No matching episode found")
320                 
321                 # Check if the dialog is already closed
322                 try:
323                         self["event_episode"].setText( custom )
324                 except Exception as e:
325                         # Screen already closed
326                         #splog("SPI: exception:", str(e))
327                         pass
328
329
330         def updateScreen(self, name, episode, short, ext, begin, duration, channel):
331                 # Adapted from EventView
332                 self["event_title"].setText( name )
333                 self["event_episode"].setText( episode )
334                 
335                 text = ""
336                 if short and short != name:
337                         text = short
338                 if ext:
339                         if text:
340                                 text += '\n'
341                         text += ext
342                 self["event_description"].setText(text)
343                 
344                 self["datetime"].setText( datetime.fromtimestamp(begin).strftime("%d.%m.%Y, %H:%M") )
345                 self["duration"].setText(_("%d min")%((duration)/60))
346                 self["channel"].setText(channel)
347
348         # Handle pixmaps
349         def loadPixmap(self, widget, path):
350                 sc = AVSwitch().getFramebufferScale()
351                 size = self[widget].instance.size()
352                 self.picload = ePicLoad()
353                 self.picload_conn = None
354                 try:
355                         self.picload_conn = self.picload.PictureData.connect( boundFunction(self.loadPixmapCallback, widget) )
356                 except:
357                         self.picload_conn = True
358                         self.picload.PictureData.get().append( boundFunction(self.loadPixmapCallback, widget) )
359                 if self.picload and self.picload_conn:
360                         self.picload.setPara((size.width(), size.height(), sc[0], sc[1], False, 1, "#00000000")) # Background dynamically
361                         if self.picload.startDecode(path) != 0:
362                                 del self.picload
363
364         def loadPixmapCallback(self, widget, picInfo=None):
365                 if self.picload and picInfo:
366                         ptr = self.picload.getData()
367                         if ptr != None:
368                                 self[widget].instance.setPixmap(ptr)
369                                 self[widget].show()
370                         del self.picload
371                         self.picload_conn = None
372
373         # Overwrite Screen close function
374         def close(self):
375                 
376                 global instance
377                 instance = None
378                 
379                 # Call baseclass function
380                 Screen.close(self)
381
382
383         def setColorButtons(self):
384                 splog("SPI: event eit", self.event and self.event.getEventId())
385                 if self.service and self.data:
386                         
387                         if self.path and os.path.exists(self.path):
388                                 # Record file exists
389                                 self["key_red"].setText(_("Rename"))
390                                 self.redButtonFunction = self.rename
391                         elif self.event and self.event.getEventId():
392                                 # Event exists
393                                 #if (not self.service.flags & eServiceReference.isGroup) and self.service.getPath() and self.service.getPath()[0] == '/'
394                                 #for timer in self.session.nav.RecordTimer.timer_list:
395                                 #       if timer.eit == eventid and timer.service_ref.ref.toString() == refstr:
396                                 #               cb_func = lambda ret : not ret or self.removeTimer(timer)
397                                 self["key_red"].setText(_("Record"))
398                                 self.redButtonFunction = self.record
399                         else:
400                                 self["key_red"].setText("")
401                                 self.redButtonFunction = None
402                 else:
403                         self["key_red"].setText("")
404                         self.redButtonFunction = None
405
406         def redButton(self):
407                 if callable(self.redButtonFunction):
408                         self.redButtonFunction()
409
410         def prevEpisode(self):
411                 if self.service and self.data:
412                         pass
413
414         def nextEpisode(self):
415                 if self.service and self.data:
416                         pass
417
418         def rename(self):
419                 ref = self.eservice
420                 if ref and self.data:
421                         path = ref.getPath()
422                         if path and os.path.exists(path):
423                                 from SeriesPluginRenamer import rename
424                                 if rename(path, self.name, self.short, self.data) is True:
425                                         self["key_red"].setText("")
426                                         self.redButtonFunction = None
427                                         self.session.open( MessageBox, _("Successfully renamed"), MessageBox.TYPE_INFO )
428                                 else:
429                                         self.session.open( MessageBox, _("Renaming failed"), MessageBox.TYPE_INFO )
430
431         # Adapted from EventView
432         def record(self):
433                 if self.event and self.service:
434                         event = self.event
435                         ref = self.service
436                         if event is None:
437                                 return
438                         eventid = event.getEventId()
439                         refstr = eServiceReference(str(self.service)).toString()
440                         for timer in self.session.nav.RecordTimer.timer_list:
441                                 if timer.eit == eventid and timer.service_ref.ref.toString() == refstr:
442                                         cb_func = lambda ret : not ret or self.removeTimer(timer)
443                                         self.session.openWithCallback(cb_func, MessageBox, _("Do you really want to delete %s?") % event.getEventName())
444                                         break
445                         else:
446                                 #newEntry = RecordTimerEntry(ServiceReference(ref), checkOldTimers = True, dirname = preferredTimerPath(), *parseEvent(self.event))
447                                 begin, end, name, description, eit = parseEvent(self.event)
448                                 
449                                 from SeriesPlugin import refactorTitle, refactorDescription
450                                 if self.data:
451                                         name = refactorTitle(name, self.data)
452                                         description = refactorDescription(description, self.data)
453                                 
454                                 newEntry = RecordTimerEntry(ServiceReference(ref), begin, end, name, description, eit, dirname = preferredTimerPath())
455                                 self.session.openWithCallback(self.finishedAdd, TimerEntry, newEntry)
456
457         def removeTimer(self, timer):
458                 splog("SPI: remove Timer")
459                 timer.afterEvent = AFTEREVENT.NONE
460                 self.session.nav.RecordTimer.removeEntry(timer)
461                 #self["key_green"].setText(_("Add timer"))
462                 #self.key_green_choice = self.ADD_TIMER
463
464         def finishedAdd(self, answer):
465                 splog("SPI: finished add")
466                 if answer[0]:
467                         entry = answer[1]
468                         simulTimerList = self.session.nav.RecordTimer.record(entry)
469                         if simulTimerList is not None:
470                                 for x in simulTimerList:
471                                         if x.setAutoincreaseEnd(entry):
472                                                 self.session.nav.RecordTimer.timeChanged(x)
473                                 simulTimerList = self.session.nav.RecordTimer.record(entry)
474                                 if simulTimerList is not None:
475                                         self.session.openWithCallback(self.finishSanityCorrection, TimerSanityConflict, simulTimerList)
476                         #self["key_green"].setText(_("Remove timer"))
477                         #self.key_green_choice = self.REMOVE_TIMER
478                 else:
479                         #self["key_green"].setText(_("Add timer"))
480                         #self.key_green_choice = self.ADD_TIMER
481                         splog("SPI: Timeredit aborted")
482
483         def finishSanityCorrection(self, answer):
484                 self.finishedAdd(answer)
485