check if event is available
[enigma2-plugins.git] / yttrailer / src / plugin.py
1
2 #  YTTrailer
3 #
4 #  Coded by Dr.Best (c) 2011
5 #  Support: www.dreambox-tools.info
6 #
7 #  All Files of this Software are licensed under the Creative Commons 
8 #  Attribution-NonCommercial-ShareAlike 3.0 Unported 
9 #  License if not stated otherwise in a Files Head. To view a copy of this license, visit
10 #  http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative
11 #  Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
12
13 #  Additionally, this plugin may only be distributed and executed on hardware which
14 #  is licensed by Dream Multimedia GmbH.
15
16 #  This plugin is NOT free software. It is open source, you are allowed to
17 #  modify it (if you keep the license), but it may not be commercially 
18 #  distributed other than under the conditions noted above.
19 #  This applies to the source code as a whole as well as to parts of it, unless
20 #  explicitely stated otherwise.
21
22 from Screens.Screen import Screen
23 from Plugins.Plugin import PluginDescriptor
24 from Components.ActionMap import ActionMap, HelpableActionMap
25 from Components.PluginComponent import plugins
26 from Plugins.Plugin import PluginDescriptor
27 from Components.Sources.StaticText import StaticText
28 from Components.GUIComponent import GUIComponent
29 from enigma import eServiceReference,  RT_WRAP, RT_VALIGN_CENTER, RT_HALIGN_LEFT, gFont, eListbox, eListboxPythonMultiContent
30
31 import gdata.youtube
32 import gdata.youtube.service
33 from socket import gaierror, error as sorcket_error
34 from urllib2 import Request, URLError, urlopen as urlopen2
35 from urllib import unquote_plus
36 from httplib import HTTPException
37 from urlparse import parse_qs
38
39 from Components.config import config, ConfigSubsection, ConfigSelection, getConfigListEntry, configfile, ConfigText, ConfigInteger
40 from Components.ConfigList import ConfigListScreen
41
42 from Screens.InfoBarGenerics import InfoBarShowHide, InfoBarSeek, InfoBarAudioSelection, InfoBarNotifications, InfoBarServiceNotifications, InfoBarPVRState, InfoBarMoviePlayerSummarySupport
43 from Components.ServiceEventTracker import InfoBarBase
44
45 # for localized messages
46 from . import _
47
48 config.plugins.yttrailer = ConfigSubsection()
49 config.plugins.yttrailer.best_resolution = ConfigSelection(default="2", choices = [("0", _("1080p")),("1", _("720p")), ("2", _("No HD streaming"))])
50 config.plugins.yttrailer.ext_descr = ConfigText(default="german", fixed_size = False)
51 config.plugins.yttrailer.max_results =  ConfigInteger(5,limits = (1, 10))
52
53 from Screens.EventView import EventViewBase
54 baseEventViewBase__init__ = None
55
56 from Screens.EpgSelection import EPGSelection
57 baseEPGSelection__init__ = None
58
59
60
61
62 def autostart(reason, **kwargs):
63         global baseEventViewBase__init__, baseEPGSelection__init__
64         if baseEventViewBase__init__ is None:
65                 baseEventViewBase__init__ = EventViewBase.__init__
66         EventViewBase.__init__ = EventViewBase__init__
67         EventViewBase.showTrailer = showTrailer
68         EventViewBase.showTrailerList = showTrailerList
69         EventViewBase.showConfig = showConfig
70
71         if baseEPGSelection__init__ is None:
72                 baseEPGSelection__init__ = EPGSelection.__init__
73         EPGSelection.__init__ = EPGSelection__init__
74         EPGSelection.showTrailer = showTrailer
75         EPGSelection.showConfig = showConfig
76         EPGSelection.showTrailerList = showTrailerList
77
78
79 def setup(session,**kwargs):
80         session.open(YTTrailerSetup)
81
82 def Plugins(**kwargs):
83
84         list = [PluginDescriptor(where = PluginDescriptor.WHERE_SESSIONSTART, fnc = autostart)] 
85         list.append(PluginDescriptor(name="YTTrailerSetup", description=_("YT-Trailer Setup"), where = [PluginDescriptor.WHERE_PLUGINMENU], fnc=setup))
86         return list
87
88 def EventViewBase__init__(self, Event, Ref, callback=None, similarEPGCB=None):
89         baseEventViewBase__init__(self, Event, Ref, callback, similarEPGCB)
90         self["trailerActions"] = ActionMap(["InfobarActions", "InfobarTeletextActions"],
91         {
92                 "showTv": self.showTrailer,
93                 "showRadio": self.showTrailerList,
94                 "startTeletext": self.showConfig
95         })
96         
97
98 def EPGSelection__init__(self, session, service, zapFunc=None, eventid=None, bouquetChangeCB=None, serviceChangeCB=None):
99         baseEPGSelection__init__(self, session, service, zapFunc, eventid, bouquetChangeCB, serviceChangeCB)
100         self["trailerActions"] = ActionMap(["InfobarActions", "InfobarTeletextActions"],
101         {
102                 "showTv": self.showTrailer,
103                 "showRadio": self.showTrailerList,
104                 "startTeletext": self.showConfig
105         })
106
107 def showConfig(self):
108         self.session.open(YTTrailerSetup)
109
110 def showTrailer(self):
111         eventname = ""
112         if isinstance(self, EventViewBase):
113                 if self.event:
114                         eventname = self.event.getEventName()
115         else:
116                 cur = self["list"].getCurrent()
117                 if cur and cur[0]:
118                         event = cur[0]
119                         eventname = event.getEventName()
120
121         ytTrailer = YTTrailer(self.session)
122         ytTrailer.showTrailer(eventname)
123
124 def showTrailerList(self):
125         eventname = ""
126         if isinstance(self, EventViewBase):
127                 if self.event:
128                         eventname = self.event.getEventName()
129         else:
130                 cur = self["list"].getCurrent()
131                 if cur and cur[0]:
132                         event = cur[0]
133                         eventname = event.getEventName()
134
135         self.session.open(YTTrailerList, eventname)
136
137 class YTTrailer:
138         def __init__(self, session):
139                 self.session = session
140
141         def showTrailer(self, eventname):
142                 if eventname:
143                         feeds = self.getYTFeeds(eventname, 1)
144                         if feeds and len(feeds.entry) >= 1:
145                                 ref = self.setServiceReference(feeds.entry[0])
146                                 if ref:
147                                         self.session.open(TrailerPlayer, ref)
148
149         def getYTFeeds(self, eventname, max_results):
150                 yt_service = gdata.youtube.service.YouTubeService()
151                 # developer key and client id taken from mytube-plugin with permission from acid_burn.
152                 yt_service.developer_key = 'AI39si4AjyvU8GoJGncYzmqMCwelUnqjEMWTFCcUtK-VUzvWygvwPO-sadNwW5tNj9DDCHju3nnJEPvFy4WZZ6hzFYCx8rJ6Mw'
153                 yt_service.client_id = 'ytapi-dream-MyTubePlayer-i0kqrebg-0'
154                 query = gdata.youtube.service.YouTubeVideoQuery()
155                 if int(config.plugins.yttrailer.best_resolution.value) <= 1:
156                         shd = "HD"
157                 else:
158                         shd = ""
159                 query.vq = "%s %s Trailer %s" % (eventname, shd, config.plugins.yttrailer.ext_descr.value)
160                 query.max_results = max_results
161                 try:
162                         feeds = yt_service.YouTubeQuery(query)
163                 except gaierror:
164                         feeds = None
165                 return feeds
166
167         def setServiceReference(self, entry):
168                 url = self.getVideoUrl(entry)
169                 if url:
170                         ref = eServiceReference(4097,0,url)
171                         ref.setName(entry.media.title.text)
172                 else:
173                         ref = None
174                 return ref
175
176         def getTubeId(self, entry):
177                 ret = None
178                 if entry.media.player:
179                         split = entry.media.player.url.split("=")
180                         ret = split.pop()
181                         if ret.startswith('youtube_gdata'):
182                                 tmpval=split.pop()
183                                 if tmpval.endswith("&feature"):
184                                         tmp = tmpval.split("&")
185                                         ret = tmp.pop(0)
186                 return ret
187
188         def getVideoUrl(self, entry):
189                 std_headers = {
190                         'User-Agent': 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100627 Firefox/3.6.6',
191                         'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
192                         'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
193                         'Accept-Language': 'en-us,en;q=0.5',
194                 }
195
196                 VIDEO_FMT_PRIORITY_MAP = {
197                         '18' : 4, #MP4 360p
198                         '35' : 5, #FLV 480p
199                         '34' : 6, #FLV 360p
200                 }
201
202                 if int(config.plugins.yttrailer.best_resolution.value) <= 1:
203                         VIDEO_FMT_PRIORITY_MAP["38"] = 1 #MP4 Original (HD)
204                         VIDEO_FMT_PRIORITY_MAP["22"] = 3 #MP4 720p (HD)
205
206                         if int(config.plugins.yttrailer.best_resolution.value) == 0:
207                                 VIDEO_FMT_PRIORITY_MAP["37"] = 2 #MP4 1080p (HD)
208
209                 video_url = None
210                 video_id = str(self.getTubeId(entry))
211
212                 # Getting video webpage
213                 #URLs for YouTube video pages will change from the format http://www.youtube.com/watch?v=ylLzyHk54Z0 to http://www.youtube.com/watch#!v=ylLzyHk54Z0.
214                 watch_url = 'http://www.youtube.com/watch?v=%s&gl=US&hl=en' % video_id
215                 watchrequest = Request(watch_url, None, std_headers)
216                 try:
217                         print "[YTTrailer] trying to find out if a HD Stream is available",watch_url
218                         watchvideopage = urlopen2(watchrequest).read()
219                 except (URLError, HTTPException, socket_error), err:
220                         print "[YTTrailer] Error: Unable to retrieve watchpage - Error code: ", str(err)
221                         return video_url
222
223                 # Get video info
224                 for el in ['&el=embedded', '&el=detailpage', '&el=vevo', '']:
225                         info_url = ('http://www.youtube.com/get_video_info?&video_id=%s%s&ps=default&eurl=&gl=US&hl=en' % (video_id, el))
226                         request = Request(info_url, None, std_headers)
227                         try:
228                                 infopage = urlopen2(request).read()
229                                 videoinfo = parse_qs(infopage)
230                                 if 'fmt_url_map' in videoinfo:
231                                         break
232                         except (URLError, HTTPException, socket_error), err:
233                                 print "[YTTrailer] Error: unable to download video infopage",str(err)
234                                 return video_url
235
236                 if 'fmt_url_map' not in videoinfo:
237                         # Attempt to see if YouTube has issued an error message
238                         if 'reason' not in videoinfo:
239                                 print '[YTTrailer] Error: unable to extract "fmt_url_map" parameter for unknown reason'
240                         else:
241                                 reason = unquote_plus(videoinfo['reason'][0])
242                                 print '[YTTrailer] Error: YouTube said: %s' % reason.decode('utf-8')
243                         return video_url
244
245                 video_fmt_map = {}
246                 fmt_infomap = {}
247                 tmp_fmtUrlDATA = videoinfo['fmt_url_map'][0].split(',')
248                 for fmtstring in tmp_fmtUrlDATA:
249                         (fmtid,fmturl) = fmtstring.split('|')
250                         if VIDEO_FMT_PRIORITY_MAP.has_key(fmtid):
251                                 video_fmt_map[VIDEO_FMT_PRIORITY_MAP[fmtid]] = { 'fmtid': fmtid, 'fmturl': unquote_plus(fmturl) }
252                         fmt_infomap[int(fmtid)] = unquote_plus(fmturl)
253                 print "[YTTrailer] got",sorted(fmt_infomap.iterkeys())
254                 if video_fmt_map and len(video_fmt_map):
255                         print "[YTTrailer] found best available video format:",video_fmt_map[sorted(video_fmt_map.iterkeys())[0]]['fmtid']
256                         video_url = video_fmt_map[sorted(video_fmt_map.iterkeys())[0]]['fmturl']
257                         print "[YTTrailer] found best available video url:",video_url
258
259                 return video_url
260
261 class YTTrailerList(Screen, YTTrailer):
262
263         skin = """
264                 <screen name="YTTrailerList" position="center,center" size="580,436" title="YT Trailer-List" backgroundColor="#ff000000">
265                         <widget name="list" position="0,0" size="580,436" />
266                 </screen>"""
267
268         def __init__(self, session, eventname):
269                 Screen.__init__(self, session)
270                 YTTrailer.__init__(self, session)
271
272                 self["actions"] = ActionMap(["WizardActions"],
273                 {
274                         "ok": self.okPressed,
275                         "back": self.close
276                 }, -1)
277
278                 self.eventName = eventname
279                 self["list"] = TrailerList()
280                 self.onLayoutFinish.append(self.startRun)
281
282
283         def startRun(self):
284                 feeds = self.getYTFeeds(self.eventName, config.plugins.yttrailer.max_results.value)
285                 if feeds is not None:
286                         entryList = []
287                         for entry in feeds.entry:
288                                 entryList.append(((entry),))
289                         self["list"].setList(entryList)
290
291         def okPressed(self):
292                 entry = self["list"].getCurrent()
293                 if entry:
294                         ref = self.setServiceReference(entry)
295                         if ref:
296                                 self.session.open(TrailerPlayer, ref)
297
298 class TrailerList(GUIComponent, object):
299
300         GUI_WIDGET = eListbox
301
302         def __init__(self):
303                 GUIComponent.__init__(self)
304                 self.l = eListboxPythonMultiContent()
305                 self.l.setBuildFunc(self.buildList)
306                 self.l.setFont(0, gFont("Regular", 22))
307                 self.l.setFont(1, gFont("Regular", 18))
308                 self.l.setItemHeight(75)
309
310         def buildList(self, entry):
311                 width = self.l.getItemSize().width()
312                 res = [ None ]
313                 res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 0, width , 24, 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, entry.media.title.text))
314                 res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 28, width , 40, 1, RT_WRAP, entry.media.description.text))
315                 return res
316
317         def getCurrent(self):
318                 cur = self.l.getCurrentSelection()
319                 return cur and cur[0]
320
321         def postWidgetCreate(self, instance):
322                 instance.setContent(self.l)
323                 self.instance.setWrapAround(True)
324
325         def preWidgetRemove(self, instance):
326                 instance.setContent(None)
327
328         def setList(self, list):
329                 self.l.setList(list)
330
331 class TrailerPlayer(InfoBarBase, InfoBarShowHide, InfoBarSeek, InfoBarAudioSelection, InfoBarNotifications, InfoBarServiceNotifications, InfoBarPVRState, InfoBarMoviePlayerSummarySupport, Screen):
332
333         ENABLE_RESUME_SUPPORT = True
334         ALLOW_SUSPEND = True
335                 
336         def __init__(self, session, ref):
337                 Screen.__init__(self, session)
338                 self.session = session
339                 self["actions"] = HelpableActionMap(self, "MoviePlayerActions",
340                         {
341                                 "leavePlayer": (self.leavePlayer, _("leave movie player..."))
342                         })
343                 self.allowPiP = False
344                 for x in InfoBarShowHide, InfoBarBase, InfoBarSeek, \
345                                 InfoBarAudioSelection, InfoBarNotifications, \
346                                 InfoBarServiceNotifications, InfoBarPVRState,  \
347                                 InfoBarMoviePlayerSummarySupport:
348                         x.__init__(self)
349
350                 self.returning = False
351                 self.skinName = "MoviePlayer"
352                 self.lastservice = session.nav.getCurrentlyPlayingServiceReference()
353                 self.session.nav.playService(ref)               
354                 self.onClose.append(self.__onClose)
355
356         def leavePlayer(self):
357                 self.close()
358
359         def doEofInternal(self, playing):
360                 self.close()
361
362         def __onClose(self):
363                 self.session.nav.playService(self.lastservice)
364
365 class YTTrailerSetup(ConfigListScreen, Screen):
366         skin = """
367                 <screen position="center,center" size="560,400" title="YT-Trailer Setup">
368                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
369                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
370                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
371                         <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" />
372                         <widget render="Label" source="key_red" position="0,0" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
373                         <widget render="Label" source="key_green" position="140,0" size="140,40" zPosition="5" valign="center" halign="center" backgroundColor="red" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
374                         <widget name="config" position="20,50" size="520,330" scrollbarMode="showOnDemand" />
375                 </screen>"""
376
377         def __init__(self, session, args = None):
378                 Screen.__init__(self, session)
379                 self["key_red"] = StaticText(_("Cancel"))
380                 self["key_green"] = StaticText(_("Save"))
381
382                 cfglist = [ ]
383                 cfglist.append(getConfigListEntry(_("Extended search filter"), config.plugins.yttrailer.ext_descr))
384                 cfglist.append(getConfigListEntry(_("Best resolution"), config.plugins.yttrailer.best_resolution))
385                 cfglist.append(getConfigListEntry(_("Max. results in list-mode"), config.plugins.yttrailer.max_results))
386                 ConfigListScreen.__init__(self, cfglist, session)
387                 self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
388                 {
389                         "green": self.keySave,
390                         "cancel": self.keyClose
391                 }, -2)
392
393         def keySave(self):
394                 for x in self["config"].list:
395                         x[1].save()
396                 configfile.save()
397                 self.close()
398
399         def keyClose(self):
400                 for x in self["config"].list:
401                         x[1].cancel()
402                 self.close()
403