update some missing plugin icons (and some minor cleanups)
[enigma2-plugins.git] / mytube / src / plugin.py
1 from Components.AVSwitch import AVSwitch
2 from Components.ActionMap import ActionMap
3 from Components.Button import Button
4 from Components.ConfigList import ConfigListScreen
5 from Components.Label import Label
6 from Components.Pixmap import Pixmap
7 from Components.ProgressBar import ProgressBar
8 from Components.ScrollLabel import ScrollLabel
9 from Components.Sources.List import List
10 from Components.Task import Task, Job, job_manager
11 from Components.config import config, ConfigSelection, ConfigSubsection, ConfigText, ConfigYesNo, getConfigListEntry
12 #, ConfigIP, ConfigNumber, ConfigLocations
13 from Screens.ChoiceBox import ChoiceBox
14 from Screens.MessageBox import MessageBox
15 from Screens.Screen import Screen
16 from Screens.VirtualKeyBoard import VirtualKeyBoard
17 from Tools.BoundFunction import boundFunction
18 from Tools.Directories import resolveFilename, SCOPE_HDD, SCOPE_CURRENT_PLUGIN
19 from Tools.Downloader import downloadWithProgress
20 from Tools.Log import Log
21
22 from Plugins.Plugin import PluginDescriptor
23
24 from MyTubePlayer import MyTubePlayer
25 from MyTubeSearch import ConfigTextWithGoogleSuggestions, MyTubeTasksScreen, MyTubeHistoryScreen
26 from MyTubeService import validate_cert, get_rnd, myTubeService
27 from MyTubeSettings import MyTubeSettingsScreen
28
29 from Plugins.SystemPlugins.TubeLib.youtube.Search import Search
30
31 from __init__ import decrypt_block
32
33 from enigma import eTPM, eTimer, ePoint, ePicLoad, eServiceReference
34 from os import path as os_path, remove as os_remove
35 from twisted.web import client
36
37 etpm = eTPM()
38 rootkey = ['\x9f', '|', '\xe4', 'G', '\xc9', '\xb4', '\xf4', '#', '&', '\xce', '\xb3', '\xfe', '\xda', '\xc9', 'U', '`', '\xd8', '\x8c', 's', 'o', '\x90', '\x9b', '\\', 'b', '\xc0', '\x89', '\xd1', '\x8c', '\x9e', 'J', 'T', '\xc5', 'X', '\xa1', '\xb8', '\x13', '5', 'E', '\x02', '\xc9', '\xb2', '\xe6', 't', '\x89', '\xde', '\xcd', '\x9d', '\x11', '\xdd', '\xc7', '\xf4', '\xe4', '\xe4', '\xbc', '\xdb', '\x9c', '\xea', '}', '\xad', '\xda', 't', 'r', '\x9b', '\xdc', '\xbc', '\x18', '3', '\xe7', '\xaf', '|', '\xae', '\x0c', '\xe3', '\xb5', '\x84', '\x8d', '\r', '\x8d', '\x9d', '2', '\xd0', '\xce', '\xd5', 'q', '\t', '\x84', 'c', '\xa8', ')', '\x99', '\xdc', '<', '"', 'x', '\xe8', '\x87', '\x8f', '\x02', ';', 'S', 'm', '\xd5', '\xf0', '\xa3', '_', '\xb7', 'T', '\t', '\xde', '\xa7', '\xf1', '\xc9', '\xae', '\x8a', '\xd7', '\xd2', '\xcf', '\xb2', '.', '\x13', '\xfb', '\xac', 'j', '\xdf', '\xb1', '\x1d', ':', '?']
39
40 config.plugins.mytube = ConfigSubsection()
41 config.plugins.mytube.search = ConfigSubsection()
42
43
44 config.plugins.mytube.search.searchTerm = ConfigTextWithGoogleSuggestions("", False)
45 config.plugins.mytube.search.orderBy = ConfigSelection(
46                                 [
47                                  ("relevance", _("Relevance")),
48                                  ("viewCount", _("View Count")),
49                                  ("date", _("Published")),
50                                  ("rating", _("Rating"))
51                                 ], "relevance")
52 config.plugins.mytube.search.time = ConfigSelection(
53                                 [
54                                  ("all_time", _("All Time")),
55                                  ("this_month", _("This Month")),
56                                  ("this_week", _("This Week")),
57                                  ("today", _("Today"))
58                                 ], "all_time")
59 config.plugins.mytube.search.safeSearch = ConfigSelection(
60                                 [
61                                  (Search.SAFE_SEARCH_NONE, _("No")),
62                                  (Search.SAFE_SEARCH_MODERATE, _("Moderately")),
63                                  (Search.SAFE_SEARCH_STRICT, _("Strictly")),
64                                 ], Search.SAFE_SEARCH_NONE)
65 config.plugins.mytube.search.categories = ConfigSelection(
66                                 [
67                                  (None, _("All")),
68                                  ("Film", _("Film & Animation")),
69                                  ("Autos", _("Autos & Vehicles")),
70                                  ("Music", _("Music")),
71                                  ("Animals", _("Pets & Animals")),
72                                  ("Sports", _("Sports")),
73                                  ("Travel", _("Travel & Events")),
74                                  ("Shortmov", _("Short Movies")),
75                                  ("Games", _("Gaming")),
76                                  ("Comedy", _("Comedy")),
77                                  ("People", _("People & Blogs")),
78                                  ("News", _("News & Politics")),
79                                  ("Entertainment", _("Entertainment")),
80                                  ("Education", _("Education")),
81                                  ("Howto", _("Howto & Style")),
82                                  ("Nonprofit", _("Nonprofits & Activism")),
83                                  ("Tech", _("Science & Technology"))
84                                 ], None)
85 config.plugins.mytube.search.lr = ConfigSelection(
86                                 [
87                                  ("au", _("Australia")),
88                                  ("br", _("Brazil")),
89                                  ("ca", _("Canada")),
90                                  ("cz", _("Czech Republic")),
91                                  ("fr", _("France")),
92                                  ("de", _("Germany")),
93                                  ("gb", _("Great Britain")),
94                                  ("au", _("Australia")),
95                                  ("nl", _("Holland")),
96                                  ("hk", _("Hong Kong")),
97                                  ("in", _("India")),
98                                  ("ie", _("Ireland")),
99                                  ("il", _("Israel")),
100                                  ("it", _("Italy")),
101                                  ("jp", _("Japan")),
102                                  ("mx", _("Mexico")),
103                                  ("nz", _("New Zealand")),
104                                  ("pl", _("Poland")),
105                                  ("ru", _("Russia")),
106                                  ("kr", _("South Korea")),
107                                  ("es", _("Spain")),
108                                  ("se", _("Sweden")),
109                                  ("tw", _("Taiwan")),
110                                  ("us", _("United States"))
111                                 ], "de")
112
113 config.plugins.mytube.general = ConfigSubsection()
114 config.plugins.mytube.general.showHelpOnOpen = ConfigYesNo(default = True)
115 config.plugins.mytube.general.loadFeedOnOpen = ConfigYesNo(default = True)
116 config.plugins.mytube.general.startFeed = ConfigSelection(
117                                 [
118                                  ("top_rated", _("Top rated")),
119                                 ], "top_rated")
120 config.plugins.mytube.general.on_movie_stop = ConfigSelection(default = "ask", choices = [
121         ("ask", _("Ask user")), ("quit", _("Return to movie list")), ("playnext", _("Play next video")), ("playagain", _("Play video again")) ])
122
123 config.plugins.mytube.general.on_exit = ConfigSelection(default = "ask", choices = [
124         ("ask", _("Ask user")), ("quit", _("Return to movie list"))])
125
126 default = resolveFilename(SCOPE_HDD)
127 tmp = config.movielist.videodirs.value
128 if default not in tmp:
129         tmp.append(default)
130 config.plugins.mytube.general.videodir = ConfigSelection(default = default, choices = tmp)
131 config.plugins.mytube.general.history = ConfigText(default="")
132 config.plugins.mytube.general.clearHistoryOnClose = ConfigYesNo(default = False)
133 config.plugins.mytube.general.AutoLoadFeeds = ConfigYesNo(default = True)
134 config.plugins.mytube.general.resetPlayService = ConfigYesNo(default = False)
135 config.plugins.mytube.general.authenticate = ConfigYesNo(default=False)
136
137 class downloadJob(Job):
138         def __init__(self, url, file, title):
139                 Job.__init__(self, title)
140                 downloadTask(self, url, file)
141
142 class downloadTask(Task):
143         def __init__(self, job, url, file):
144                 Task.__init__(self, job, ("download task"))
145                 self.end = 100
146                 self.url = url
147                 self.local = file
148
149         def prepare(self):
150                 self.error = None
151
152         def run(self, callback):
153                 self.callback = callback
154                 self.download = downloadWithProgress(self.url,self.local)
155                 self.download.addProgress(self.http_progress)
156                 self.download.start().addCallback(self.http_finished).addErrback(self.http_failed)
157
158         def http_progress(self, recvbytes, totalbytes):
159                 #print "[http_progress] recvbytes=%d, totalbytes=%d" % (recvbytes, totalbytes)
160                 self.progress = int(self.end*recvbytes/float(totalbytes))
161
162         def http_finished(self, string=""):
163                 print "[http_finished]" + str(string)
164                 Task.processFinished(self, 0)
165
166         def http_failed(self, failure_instance=None, error_message=""):
167                 if error_message == "" and failure_instance is not None:
168                         error_message = failure_instance.getErrorMessage()
169                         print "[http_failed] " + error_message
170                         Task.processFinished(self, 1)
171
172 class MyTubePlayerMainScreen(Screen, ConfigListScreen):
173         Details = {}
174         #(entry, Title, Description, TubeID, thumbnail, PublishedDate,Views,duration,ratings )
175         skin = """
176                 <screen name="MyTubePlayerMainScreen" position="center,95"  size="920,570" title="MyTube - Browser">
177                 <ePixmap pixmap="skin_default/buttons/red.png" position="10,5" size="200,40" alphatest="on"/>
178                 <ePixmap pixmap="skin_default/buttons/green.png" position="210,5" size="200,40" alphatest="on"/>
179                 <ePixmap pixmap="skin_default/buttons/yellow.png" position="410,5" size="200,40" alphatest="on"/>
180                 <widget name="key_red" position="10,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" shadowColor="black" shadowOffset="-2,-2"/>
181                 <widget name="key_green" position="210,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" shadowColor="black" shadowOffset="-2,-2"/>
182                 <widget name="key_yellow" position="410,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" shadowColor="black" shadowOffset="-2,-2"/>
183                 <ePixmap pixmap="skin_default/icons/info.png" position="670,10" size="70,30" alphatest="on"/>
184                 <ePixmap pixmap="skin_default/icons/menu.png" position="755,10" size="70,30" alphatest="on"/>
185                 <widget name="VKeyIcon" pixmap="skin_default/icons/text.png" position="840,10" size="70,30" alphatest="on"/>
186                 <eLabel position="10,50" size="900,1" backgroundColor="grey"/>
187                 <widget name="config" position="10,60" size="900,30" scrollbarMode="showNever" transparent="1"/>
188                 <eLabel position="10,95" size="900,1" backgroundColor="grey"/>
189                 <widget source="feedlist" render="Listbox"      position="10,110" size="900,450" enableWrapAround="1" scrollbarMode="showOnDemand" transparent="1">
190                         <convert type="TemplatedMultiContent">
191                         {"templates":
192                                 {"default": (90,[
193                                                 MultiContentEntryPixmapAlphaTest(pos=(2,3),size=(130,94),png=4),# index 4 is the thumbnail
194                                                 MultiContentEntryText(pos=(140,2),size=(750,26),font=0,flags=RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP,text=1),# index 1 is the Title
195                                                 MultiContentEntryText(pos=(150,35),size=(450,20),font=1,flags=RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP,text=5),# index 5 is the Published Date
196                                                 MultiContentEntryText(pos=(150,62),size=(450,20),font=1,flags=RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP,text=6),# index 6 is the Views Count
197                                                 MultiContentEntryText(pos=(600,35),size=(280,20),font=1,flags=RT_HALIGN_RIGHT | RT_VALIGN_TOP| RT_WRAP,text=7,color=0xa0a0a0),# index 7 is the duration
198                                                 MultiContentEntryText(pos=(600,62),size=(280,20),font=1,flags=RT_HALIGN_RIGHT | RT_VALIGN_TOP| RT_WRAP,text=8,color=0xa0a0a0),# index 8 is the ratingcount
199                                         ]),
200                                 "state": (90,[
201                                                 MultiContentEntryText(pos=(10,5),size=(880,28),font=2,flags=RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP,text=0),# index 0 is the name
202                                                 MultiContentEntryText(pos=(10,37),size=(880,46),font=3,flags=RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP,text=1),# index 2 is the description
203                                         ])
204                                 },
205                                 "fonts": [gFont("Regular",23),gFont("Regular",18),gFont("Regular",26),gFont("Regular",20)],
206                                 "itemHeight": 90
207                         }
208                         </convert>
209                 </widget>
210                 <widget name="HelpWindow" position="360,530" zPosition="1" size="1,1"/>
211                 <widget name="thumbnail" position="0,0" size="130,94" alphatest="on"/> # fake entry for dynamic thumbnail resizing,currently there is no other way doing this.
212                 <widget name="ButtonBlue" position="0,0" size="0,0"/>
213         </screen>"""
214
215         def __init__(self, session, l2key=None):
216                 Screen.__init__(self, session)
217                 self.session = session
218                 self._userCodeMbx = None
219                 self.l2key = l2key
220                 self.l3key = None
221                 self.skin_path = plugin_path
222                 self.FeedURL = None
223                 self.ytfeed = None
224                 self.currentFeedName = None
225                 self.videolist = []
226                 self.queryThread = None
227                 self.queryRunning = False
228
229                 self.video_playlist = []
230                 self.statuslist = []
231                 self.mytubeentries = None
232
233                 self.thumbnails = []
234                 self.index = 0
235                 self.maxentries = 0
236
237                 self.screenshotList = []
238                 self.pixmaps_to_load = []
239                 self.picloads = {}
240
241                 self.oldfeedentrycount = 0
242                 self.appendEntries = False
243                 self.lastservice = session.nav.getCurrentlyPlayingServiceReference()
244                 self.propagateUpDownNormally = True
245                 self.FirstRun = True
246                 self.HistoryWindow = None
247                 self.History = None
248                 self.searchtext = _("Welcome to the MyTube Youtube Player.\n\nWhile entering your search term(s) you will get suggestions displayed matching your search term.\n\nTo select a suggestion press DOWN on your remote, select the desired result and press OK on your remote to start the search.\n\nPress exit to get back to the input field.")
249                 self.feedtext = _("Welcome to the MyTube Youtube Player.\n\nUse the Bouqet+ button to navigate to the search field and the Bouqet- to navigate to the video entries.\n\nTo play a movie just press OK on your remote control.\n\nPress info to see the movie description.\n\nPress the Menu button for additional options.\n\nThe Help button shows this help again.")
250                 self.currList = "configlist"
251                 self.oldlist = None
252
253                 self["feedlist"] = List(self.videolist)
254                 self["thumbnail"] = Pixmap()
255                 self["thumbnail"].hide()
256                 self["HelpWindow"] = Pixmap()
257                 self["HelpWindow"].hide()
258                 self["key_red"] = Button(_("Close"))
259                 self["key_green"] = Button(_("Std. Feeds"))
260                 self["key_yellow"] = Button(_("History"))
261                 self["ButtonBlue"] = Pixmap()
262                 self["VKeyIcon"] = Pixmap()
263                 self["ButtonBlue"].hide()
264                 self["VKeyIcon"].hide()
265                 self["result"] = Label("")
266
267
268                 self["searchactions"] = ActionMap(["ShortcutActions", "WizardActions", "HelpActions", "MediaPlayerActions"],
269                 {
270                         "ok": self.keyOK,
271                         "back": self.leavePlayer,
272                         "red": self.leavePlayer,
273                         "blue": self.openKeyboard,
274                         "yellow": self.handleHistory,
275                         "up": self.keyUp,
276                         "down": self.handleSuggestions,
277                         "left": self.keyLeft,
278                         "right": self.keyRight,
279                         "prevBouquet": self.switchToFeedList,
280                         "nextBouquet": self.switchToConfigList,
281                         "displayHelp": self.handleHelpWindow,
282                         "menu" : self.handleMenu,
283                 }, -2)
284
285                 self["suggestionactions"] = ActionMap(["ShortcutActions", "WizardActions", "MediaPlayerActions", "HelpActions", "NumberActions"],
286                 {
287                         "ok": self.keyOK,
288                         "back": self.switchToConfigList,
289                         "red": self.switchToConfigList,
290                         "nextBouquet": self.switchToConfigList,
291                         "prevBouquet": self.switchToFeedList,
292                         "up": self.keyUp,
293                         "down": self.keyDown,
294                         "left": self.keyLeft,
295                         "right": self.keyRight,
296                         "0": self.toggleScreenVisibility
297                 }, -2)
298
299
300                 self["videoactions"] = ActionMap(["ShortcutActions", "WizardActions", "MediaPlayerActions", "MovieSelectionActions", "HelpActions"],
301                 {
302                         "ok": self.keyOK,
303                         "back": self.leavePlayer,
304                         "red": self.leavePlayer,
305                         "yellow": self.handleHistory,
306                         "up": self.keyUp,
307                         "down": self.keyDown,
308                         "nextBouquet": self.switchToConfigList,
309                         "green": self.keyStdFeed,
310                         "showEventInfo": self.showVideoInfo,
311                         "displayHelp": self.handleHelpWindow,
312                         "menu" : self.handleMenu,
313                 }, -2)
314
315                 self["statusactions"] = ActionMap(["ShortcutActions", "WizardActions", "HelpActions", "MediaPlayerActions"],
316                 {
317                         "back": self.leavePlayer,
318                         "red": self.leavePlayer,
319                         "nextBouquet": self.switchToConfigList,
320                         "green": self.keyStdFeed,
321                         "yellow": self.handleHistory,
322                         "menu": self.handleMenu
323                 }, -2)
324
325                 self["historyactions"] = ActionMap(["ShortcutActions", "WizardActions", "MediaPlayerActions", "MovieSelectionActions", "HelpActions"],
326                 {
327                         "ok": self.keyOK,
328                         "back": self.closeHistory,
329                         "red": self.closeHistory,
330                         "yellow": self.handleHistory,
331                         "up": self.keyUp,
332                         "down": self.keyDown,
333                         "left": self.keyLeft,
334                         "right": self.keyRight,
335                 }, -2)
336
337                 self["videoactions"].setEnabled(False)
338                 self["statusactions"].setEnabled(False)
339                 self["historyactions"].setEnabled(False)
340
341                 self.timer_startDownload = eTimer()
342                 self.timer_startDownload_conn = self.timer_startDownload.timeout.connect(self.downloadThumbnails)
343                 self.timer_thumbnails = eTimer()
344                 self.timer_thumbnails_conn = self.timer_thumbnails.timeout.connect(self.updateFeedThumbnails)
345
346                 self.SearchConfigEntry = None
347                 self.searchContextEntries = []
348                 config.plugins.mytube.search.searchTerm.value = ""
349                 ConfigListScreen.__init__(self, self.searchContextEntries, session)
350                 self.createSetup()
351                 self.onLayoutFinish.append(self.layoutFinished)
352                 self.onShown.append(self.setWindowTitle)
353                 self.onClose.append(self.__onClose)
354
355         def __onClose(self):
356                 del self.timer_startDownload
357                 del self.timer_thumbnails
358                 self.Details = {}
359                 self.session.nav.playService(self.lastservice)
360                 myTubeService.cancelAuthFlow()
361
362         def layoutFinished(self):
363                 self.currList = "status"
364                 current = self["config"].getCurrent()
365                 if current[1].help_window.instance is not None:
366                         current[1].help_window.instance.hide()
367
368                 l3cert = etpm.getData(eTPM.DT_LEVEL3_CERT)
369                 if l3cert is None or l3cert is "":
370                         self["videoactions"].setEnabled(False)
371                         self["searchactions"].setEnabled(False)
372                         self["config_actions"].setEnabled(False)
373                         self["historyactions"].setEnabled(False)
374                         self["statusactions"].setEnabled(True)
375                         self.hideSuggestions()
376                         self.statuslist = []
377                         self.statuslist.append(( _("Genuine Dreambox validation failed!"), _("Verify your Dreambox authenticity by running the genuine dreambox plugin!" ) ))
378                         self["feedlist"].style = "state"
379                         self['feedlist'].setList(self.statuslist)
380                         return
381
382                 self.l3key = validate_cert(l3cert, self.l2key)
383                 if self.l3key is None:
384                         print "l3cert invalid"
385                         return
386                 rnd = get_rnd()
387                 if rnd is None:
388                         print "random error"
389                         return
390
391                 val = etpm.computeSignature(rnd)
392                 result = decrypt_block(val, self.l3key)
393
394                 self.statuslist = []
395                 if result[80:88] != rnd:
396                         self.statuslist.append(( _("Genuine Dreambox validation failed!"), _("Verify your Dreambox authenticity by running the genuine dreambox plugin!" ) ))
397                         self["feedlist"].style = "state"
398                         self['feedlist'].setList(self.statuslist)
399                         return
400
401                 # we need to login here; startService() is fired too often for external curl
402 #               self.tryUserLogin()
403
404                 self.statuslist.append(( _("Fetching feed entries"), _("Trying to download the Youtube feed entries. Please wait..." ) ))
405                 self["feedlist"].style = "state"
406                 self['feedlist'].setList(self.statuslist)
407                 myTubeService.addReadyCallback(self._onServiceReady)
408                 if config.plugins.mytube.general.authenticate.value:
409                         myTubeService.startAuthenticatedService(self._onUserCodeReady)
410                 else:
411                         myTubeService.startService()
412
413         def _onServiceReady(self, ready):
414                 Log.w("%s" %(ready,))
415                 if self._userCodeMbx:
416                         self._userCodeMbx.close()
417                         self._userCodeMbx = None
418
419                 if ready:
420                         if config.plugins.mytube.general.loadFeedOnOpen.value:
421                                 self.getFeed()
422                                 self.setState('getFeed')
423                         else:
424                                 self.setState('byPass')
425
426         def setWindowTitle(self):
427                 self.setTitle(_("MyTubePlayer"))
428
429         def createSetup(self):
430                 self.searchContextEntries = []
431                 self.SearchConfigEntry = getConfigListEntry(_("Search Term(s)"), config.plugins.mytube.search.searchTerm)
432                 self.searchContextEntries.append(self.SearchConfigEntry)
433                 self["config"].list = self.searchContextEntries
434                 self["config"].l.setList(self.searchContextEntries)
435
436         def setState(self,status = None):
437                 if status:
438                         self.currList = "status"
439                         self["videoactions"].setEnabled(False)
440                         self["searchactions"].setEnabled(False)
441                         self["config_actions"].setEnabled(False)
442                         self["historyactions"].setEnabled(False)
443                         self["statusactions"].setEnabled(True)
444                         self["ButtonBlue"].hide()
445                         self["VKeyIcon"].hide()
446                         self.statuslist = []
447                         self.hideSuggestions()
448                         result = None
449                         if self.l3key is not None:
450                                 rnd = get_rnd()
451                                 if rnd is None:
452                                         return
453                                 val = etpm.computeSignature(rnd)
454                                 result = decrypt_block(val, self.l3key)
455                         if not result or result[80:88] != rnd:
456                                 self["key_green"].show()
457                                 self.statuslist.append(( _("Genuine Dreambox validation failed!"), _("Verify your Dreambox authenticity by running the genuine dreambox plugin!" ) ))
458                                 self["feedlist"].style = "state"
459                                 self['feedlist'].setList(self.statuslist)
460                                 return
461
462                         print "Genuine Dreambox validation passed"
463
464                         if self.HistoryWindow is not None:
465                                 self.HistoryWindow.deactivate()
466                                 self.HistoryWindow.instance.hide()
467                         if status == 'getFeed':
468                                 self.statuslist.append(( _("Fetching feed entries"), _("Trying to download the Youtube feed entries. Please wait..." ) ))
469                         elif status == 'getSearchFeed':
470                                 self.statuslist.append(( _("Fetching search entries"), _("Trying to download the Youtube search results. Please wait..." ) ))
471                         elif status == 'Error':
472                                 self.statuslist.append(( _("An error occured."), _("There was an error getting the feed entries. Please try again." ) ))
473                         elif status == 'noVideos':
474                                 self["key_green"].show()
475                                 self.statuslist.append(( _("No videos to display"), _("Please select a standard feed or try searching for videos." ) ))
476                         elif status == 'byPass':
477                                 self.statuslist.append(( _("Not fetching feed entries"), _("Please enter your search term." ) ))
478                                 self["feedlist"].style = "state"
479                                 self['feedlist'].setList(self.statuslist)
480                                 self.switchToConfigList()
481                         self["feedlist"].style = "state"
482                         self['feedlist'].setList(self.statuslist)
483
484
485         def _onUserCodeReady(self, userCode):
486                 self._userCodeMbx = self.session.open(MessageBox, str(_("Please visit: %s\nAnd enter: %s") % (userCode.verification_url, userCode.user_code)), type=MessageBox.TYPE_INFO, title=_("Authentication awaiting"))
487                 Log.w(userCode)
488
489         def handleHelpWindow(self):
490                 print "[handleHelpWindow]"
491                 if self.currList == "configlist":
492                         self.hideSuggestions()
493                         self.session.openWithCallback(self.ScreenClosed, MyTubeVideoHelpScreen, self.skin_path, wantedinfo = self.searchtext, wantedtitle = _("MyTubePlayer Help") )
494                 elif self.currList == "feedlist":
495                         self.session.openWithCallback(self.ScreenClosed, MyTubeVideoHelpScreen, self.skin_path, wantedinfo = self.feedtext, wantedtitle = _("MyTubePlayer Help") )
496
497         def handleFirstHelpWindow(self):
498                 print "[handleFirstHelpWindow]"
499                 if config.plugins.mytube.general.showHelpOnOpen.value is True:
500                         if self.currList == "configlist":
501                                 self.hideSuggestions()
502                                 self.session.openWithCallback(self.firstRunHelpClosed, MyTubeVideoHelpScreen, self.skin_path,wantedinfo = self.feedtext, wantedtitle = _("MyTubePlayer Help") )
503                 else:
504                         self.FirstRun = False
505
506         def firstRunHelpClosed(self):
507                 if self.FirstRun == True:
508                         self.FirstRun = False
509                         self.switchToConfigList()
510
511         def handleMenu(self):
512                 if self.currList == "configlist" or self.currList == "status":
513                         menulist = (
514                                         (_("MyTube Settings"), "settings"),
515                                 )
516                         self.hideSuggestions()
517                         self.session.openWithCallback(self.openMenu, ChoiceBox, title=_("Select your choice."), list = menulist)
518
519                 elif self.currList == "feedlist":
520                         menulist = [(_("MyTube Settings"), "settings")]
521                         menulist.extend((
522                                         (_("Related videos"), "related"),
523                                         (_("Channel videos"), "channel_videos"),
524                                 ))
525                         
526                         if myTubeService.is_auth() is True:
527                                 menulist.extend((
528                                                 (_("Subscribe to channel"), "subscribe"),
529                                                 (_("Add to favorites"), "favorite"),
530                                         ))                              
531                         
532                         if config.usage.setup_level.index >= 2: # expert+
533                                 menulist.extend((
534                                         (_("Download Video"), "download"),
535                                         (_("View active downloads"), "downview")
536                                 ))
537
538                         self.hideSuggestions()
539                         self.session.openWithCallback(self.openMenu, ChoiceBox, title=_("Select your choice."), list = menulist)
540
541         def openMenu(self, answer):
542                 answer = answer and answer[1]
543                 if answer == "settings":
544                         print "settings selected"
545                         self.session.openWithCallback(self.ScreenClosed,MyTubeSettingsScreen, self.skin_path )
546                 elif answer == "related":
547                         current = self["feedlist"].getCurrent()[0]
548                         self.setState('getFeed')
549                         self.getRelatedVideos(current)
550                 elif answer == "channel_videos":
551                         current = self["feedlist"].getCurrent()[0]
552                         self.setState('getFeed')
553                         self.getChannelVideos(current)
554                 elif answer == "subscribe":
555                         current = self["feedlist"].getCurrent()[0]
556                         self.session.open(MessageBox, current.subscribeToUser(), MessageBox.TYPE_INFO)
557                 elif answer == "favorite":
558                         current = self["feedlist"].getCurrent()[0]
559                         self.session.open(MessageBox, current.addToFavorites(), MessageBox.TYPE_INFO)
560                                         
561                 elif answer == "response":
562                         current = self["feedlist"].getCurrent()[0]
563                         self.setState('getFeed')
564                         self.getResponseVideos(current)
565                 elif answer == "download":
566                         if self.currList == "feedlist":
567                                 current = self[self.currList].getCurrent()
568                                 if current:
569                                         video = current[0]
570                                         if video:
571                                                 myurl = video.url
572                                                 filename = str(config.plugins.mytube.general.videodir.value)+ str(video.title) + '.mp4'
573                                                 job_manager.AddJob(downloadJob(myurl,filename, str(video.title)))
574                 elif answer == "downview":
575                         self.tasklist = []
576                         for job in job_manager.getPendingJobs():
577                                 self.tasklist.append((job,job.name,job.getStatustext(),int(100*job.progress/float(job.end)) ,str(100*job.progress/float(job.end)) + "%" ))
578                         self.session.open(MyTubeTasksScreen, self.skin_path , self.tasklist)
579                 elif answer == None:
580                         self.ScreenClosed()
581
582         def openKeyboard(self):
583                 self.hideSuggestions()
584                 self.session.openWithCallback(self.SearchEntryCallback, VirtualKeyBoard, title = (_("Enter your search term(s)")), text = config.plugins.mytube.search.searchTerm.value)
585
586         def ScreenClosed(self):
587                 print "ScreenCLosed, restoring old window state"
588                 if self.currList == "historylist":
589                         if self.HistoryWindow.status() is False:
590                                 self.HistoryWindow.activate()
591                                 self.HistoryWindow.instance.show()
592                 elif self.currList == "configlist":
593                         self.switchToConfigList()
594                         ConfigListScreen.keyOK(self)
595                 elif self.currList == "feedlist":
596                         self.switchToFeedList()
597
598         def SearchEntryCallback(self, callback = None):
599                 if callback is not None and len(callback):
600                         config.plugins.mytube.search.searchTerm.value = callback
601                         ConfigListScreen.keyOK(self)
602                         self["config"].getCurrent()[1].getSuggestions()
603                 current = self["config"].getCurrent()
604                 if current[1].help_window.instance is not None:
605                         current[1].help_window.instance.show()
606                 if current[1].suggestionsWindow.instance is not None:
607                         current[1].suggestionsWindow.instance.show()
608                 self.propagateUpDownNormally = True
609
610         def openStandardFeedClosed(self, answer):
611                 answer = answer and answer[1]
612                 if answer is not None:
613                         self.setState('getFeed')
614                         self.appendEntries = False
615                         self.getFeed(videoCategoryId=answer)
616
617         def handleLeave(self, how):
618                 self.is_closing = True
619                 if how == "ask":
620                         if self.currList == "configlist":
621                                 list = (
622                                         (_("Yes"), "quit"),
623                                         (_("No"), "continue"),
624                                         (_("No, but switch to video entries."), "switch2feed")
625                                 )
626                         else:
627                                 list = (
628                                         (_("Yes"), "quit"),
629                                         (_("No"), "continue"),
630                                         (_("No, but switch to video search."), "switch2search")
631                                 )
632                         self.session.openWithCallback(self.leavePlayerConfirmed, ChoiceBox, title=_("Really quit MyTube Player?"), list = list)
633                 else:
634                         self.leavePlayerConfirmed([True, how])
635
636         def leavePlayer(self):
637                 print "leavePlayer"
638                 if self.HistoryWindow is not None:
639                         self.HistoryWindow.deactivate()
640                         self.HistoryWindow.instance.hide()
641                 if self.currList == "configlist":
642                         current = self["config"].getCurrent()
643                         if current[1].suggestionsWindow.activeState is True:
644                                 self.propagateUpDownNormally = True
645                                 current[1].deactivateSuggestionList()
646                                 self["config"].invalidateCurrent()
647                         else:
648                                 self.hideSuggestions()
649                                 self.handleLeave(config.plugins.mytube.general.on_exit.value)
650                 else:
651                         self.hideSuggestions()
652                         self.handleLeave(config.plugins.mytube.general.on_exit.value)
653
654         def leavePlayerConfirmed(self, answer):
655                 answer = answer and answer[1]
656                 if answer == "quit":
657                         self.doQuit()
658                 elif answer == "continue":
659                         if self.currList == "historylist":
660                                 if self.HistoryWindow.status() is False:
661                                         self.HistoryWindow.activate()
662                                         self.HistoryWindow.instance.show()
663                         elif self.currList == "configlist":
664                                 self.switchToConfigList()
665                         elif self.currList == "feedlist":
666                                 self.switchToFeedList()
667                 elif answer == "switch2feed":
668                         self.switchToFeedList()
669                 elif answer == "switch2search":
670                         self.switchToConfigList()
671                 elif answer == None:
672                         if self.currList == "historylist":
673                                 if self.HistoryWindow.status() is False:
674                                         self.HistoryWindow.activate()
675                                         self.HistoryWindow.instance.show()
676                         elif self.currList == "configlist":
677                                 self.switchToConfigList()
678                         elif self.currList == "feedlist":
679                                 self.switchToFeedList()
680
681         def doQuit(self):
682                 myTubeService.onReady.remove(self._onServiceReady)
683
684                 if self["config"].getCurrent()[1].suggestionsWindow is not None:
685                         self.session.deleteDialog(self["config"].getCurrent()[1].suggestionsWindow)
686                 if self.HistoryWindow is not None:
687                         self.session.deleteDialog(self.HistoryWindow)
688                 if config.plugins.mytube.general.showHelpOnOpen.value is True:
689                         config.plugins.mytube.general.showHelpOnOpen.value = False
690                         config.plugins.mytube.general.showHelpOnOpen.save()
691                 if not config.plugins.mytube.general.clearHistoryOnClose.value:
692                         if self.History and len(self.History):
693                                 config.plugins.mytube.general.history.value = ",".join(self.History)
694                 else:
695                         config.plugins.mytube.general.history.value = ""
696                 config.plugins.mytube.general.history.save()
697                 config.plugins.mytube.general.save()
698                 config.plugins.mytube.save()
699                 self.cancelThread()
700                 self.close()
701
702         def keyOK(self):
703                 print "self.currList im KeyOK",self.currList
704                 if self.currList == "configlist" or self.currList == "suggestionslist":
705                         self["config"].invalidateCurrent()
706                         if config.plugins.mytube.search.searchTerm.value != "":
707                                 self.add2History()
708                                 searchTerm = config.plugins.mytube.search.searchTerm.value
709                                 print "Search searchcontext",searchTerm
710                                 if isinstance(self["config"].getCurrent()[1], ConfigTextWithGoogleSuggestions) and not self.propagateUpDownNormally:
711                                         self.propagateUpDownNormally = True
712                                         self["config"].getCurrent()[1].deactivateSuggestionList()
713                                 self.setState('getSearchFeed')
714                                 self.runSearch(searchTerm)
715                 elif self.currList == "feedlist":
716                         current = self[self.currList].getCurrent()
717                         if current:
718                                 Log.d(current)
719                                 video = current[0]
720                                 if video is not None:
721                                         hasUriResolver = False
722                                         try:
723                                                 from enigma import eUriResolver
724                                                 hasUriResolver = True
725                                         except:
726                                                 pass
727                                         if hasUriResolver:
728                                                 uri = "yt://%s" %(video.id,)
729                                                 myreference = eServiceReference(eServiceReference.idURI,0,uri)
730                                                 myreference.setName(video.title)
731                                                 self.session.openWithCallback(self.onPlayerClosed, MyTubePlayer, myreference, self.lastservice, infoCallback = self.showVideoInfo, nextCallback = self.getNextEntry, prevCallback = self.getPrevEntry )
732                                         else:
733                                                 myurl = video.url
734                                                 print "Playing URL",myurl
735                                                 if myurl is not None:
736                                                         myreference = eServiceReference(4097,0,myurl)
737                                                         myreference.setName(video.title)
738                                                         self.session.openWithCallback(self.onPlayerClosed, MyTubePlayer, myreference, self.lastservice, infoCallback = self.showVideoInfo, nextCallback = self.getNextEntry, prevCallback = self.getPrevEntry )
739                                                 else:
740                                                         self.session.open(MessageBox, _("Sorry, video is not available!"), MessageBox.TYPE_INFO)
741                 elif self.currList == "historylist":
742                         if self.HistoryWindow is not None:
743                                 config.plugins.mytube.search.searchTerm.value = self.HistoryWindow.getSelection()
744                         self["config"].invalidateCurrent()
745                         if config.plugins.mytube.search.searchTerm.value != "":
746                                 searchTerm = config.plugins.mytube.search.searchTerm.value
747                                 print "Search searchcontext",searchTerm
748                                 self.setState('getSearchFeed')
749                                 self.runSearch(searchTerm)
750
751         def onPlayerClosed(self):
752                 if config.plugins.mytube.general.resetPlayService.value:
753                         self.session.nav.playService(self.lastservice)
754
755         def toggleScreenVisibility(self):
756                 if self.shown is True:
757                         self.hide()
758                 else:
759                         self.show()
760
761         def keyUp(self):
762                 print "self.currList im KeyUp",self.currList
763                 if self.currList == "suggestionslist":
764                         if config.plugins.mytube.search.searchTerm.value != "":
765                                 if not self.propagateUpDownNormally:
766                                         self["config"].getCurrent()[1].suggestionListUp()
767                                         self["config"].invalidateCurrent()
768                 elif self.currList == "feedlist":
769                         self[self.currList].selectPrevious()
770                 elif self.currList == "historylist":
771                         if self.HistoryWindow and self.HistoryWindow.shown:
772                                 self.HistoryWindow.up()
773
774         def keyDown(self):
775                 print "self.currList im KeyDown",self.currList
776                 if self.currList == "suggestionslist":
777                         if config.plugins.mytube.search.searchTerm.value != "":
778                                 if not self.propagateUpDownNormally:
779                                         self["config"].getCurrent()[1].suggestionListDown()
780                                         self["config"].invalidateCurrent()
781                 elif self.currList == "feedlist":
782                         print self[self.currList].count()
783                         print self[self.currList].index
784                         if self[self.currList].index == self[self.currList].count()-1 and myTubeService.hasNextPage():
785                                 # load new feeds on last selected item
786                                 if config.plugins.mytube.general.AutoLoadFeeds.value is False:
787                                         self.session.openWithCallback(self.getNextEntries, MessageBox, _("Do you want to see more entries?"))
788                                 else:
789                                         self.getNextEntries(True)
790                         else:
791                                 self[self.currList].selectNext()
792                 elif self.currList == "historylist":
793                         if self.HistoryWindow is not None and self.HistoryWindow.shown:
794                                 self.HistoryWindow.down()
795         def keyRight(self):
796                 print "self.currList im KeyRight",self.currList
797                 if self.propagateUpDownNormally:
798                         ConfigListScreen.keyRight(self)
799                 else:
800                         if self.currList == "suggestionslist":
801                                 if config.plugins.mytube.search.searchTerm.value != "":
802                                         self["config"].getCurrent()[1].suggestionListPageDown()
803                                         self["config"].invalidateCurrent()
804                         elif self.currList == "historylist":
805                                 if self.HistoryWindow is not None and self.HistoryWindow.shown:
806                                         self.HistoryWindow.pageDown()
807
808         def keyLeft(self):
809                 print "self.currList im kEyLeft",self.currList
810                 if self.propagateUpDownNormally:
811                         ConfigListScreen.keyLeft(self)
812                 else:
813                         if self.currList == "suggestionslist":
814                                 if config.plugins.mytube.search.searchTerm.value != "":
815                                         self["config"].getCurrent()[1].suggestionListPageUp()
816                                         self["config"].invalidateCurrent()
817                         elif self.currList == "historylist":
818                                 if self.HistoryWindow is not None and self.HistoryWindow.shown:
819                                         self.HistoryWindow.pageDown()
820         def keyStdFeed(self):
821                 self.hideSuggestions()
822                 menulist = []
823                 for category in myTubeService.getCategories():
824                         menulist.append((category.title, category.id))
825 #               if myTubeService.is_auth():
826 #                       menulist.extend((
827 #                               (_("My Subscriptions"), "my_subscriptions"),
828 #                               (_("My Favorites"), "my_favorites"),
829 #                               (_("My History"), "my_history"),
830 #                               (_("My Watch Later"), "my_watch_later"),
831 #                               (_("My Recommendations"), "my_recommendations"),
832 #                               (_("My Uploads"), "my_uploads"),
833 #                       ))
834
835                 self.session.openWithCallback(self.openStandardFeedClosed, ChoiceBox, title=_("Select new feed to view."), list = menulist)
836
837         def handleSuggestions(self):
838                 print "handleSuggestions"
839                 print "self.currList",self.currList
840                 if self.currList == "configlist":
841                         self.switchToSuggestionsList()
842                 elif self.currList == "historylist":
843                         if self.HistoryWindow is not None and self.HistoryWindow.shown:
844                                 self.HistoryWindow.down()
845
846         def switchToSuggestionsList(self):
847                 print "switchToSuggestionsList"
848                 self.currList = "suggestionslist"
849                 self["ButtonBlue"].hide()
850                 self["VKeyIcon"].hide()
851                 self["statusactions"].setEnabled(False)
852                 self["config_actions"].setEnabled(False)
853                 self["videoactions"].setEnabled(False)
854                 self["searchactions"].setEnabled(False)
855                 self["suggestionactions"].setEnabled(True)
856                 self["historyactions"].setEnabled(False)
857                 self["key_green"].hide()
858                 self.propagateUpDownNormally = False
859                 self["config"].invalidateCurrent()
860                 if self.HistoryWindow is not None and self.HistoryWindow.shown:
861                         self.HistoryWindow.deactivate()
862                         self.HistoryWindow.instance.hide()
863
864         def switchToConfigList(self):
865                 print "switchToConfigList"
866                 self.currList = "configlist"
867                 self["config_actions"].setEnabled(True)
868                 self["historyactions"].setEnabled(False)
869                 self["statusactions"].setEnabled(False)
870                 self["videoactions"].setEnabled(False)
871                 self["suggestionactions"].setEnabled(False)
872                 self["searchactions"].setEnabled(True)
873                 self["key_green"].hide()
874                 self["ButtonBlue"].show()
875                 self["VKeyIcon"].show()
876                 self["config"].invalidateCurrent()
877                 helpwindowpos = self["HelpWindow"].getPosition()
878                 current = self["config"].getCurrent()
879                 if current[1].help_window.instance is not None:
880                         current[1].help_window.instance.move(ePoint(helpwindowpos[0],helpwindowpos[1]))
881                         current[1].help_window.instance.show()
882                 if current[1].suggestionsWindow.instance is not None:
883                         current[1].suggestionsWindow.instance.show()
884                         self["config"].getCurrent()[1].getSuggestions()
885                 self.propagateUpDownNormally = True
886                 if self.HistoryWindow is not None and self.HistoryWindow.shown:
887                         self.HistoryWindow.deactivate()
888                         self.HistoryWindow.instance.hide()
889                 if self.FirstRun == True:
890                         self.handleFirstHelpWindow()
891
892         def switchToFeedList(self, append = False):
893                 print "switchToFeedList"
894                 print "switching to feedlist from:",self.currList
895                 print "len(self.videolist):",len(self.videolist)
896                 if self.HistoryWindow is not None and self.HistoryWindow.shown:
897                         self.HistoryWindow.deactivate()
898                         self.HistoryWindow.instance.hide()
899                 self.hideSuggestions()
900                 if len(self.videolist):
901                         self.currList = "feedlist"
902                         self["ButtonBlue"].hide()
903                         self["VKeyIcon"].hide()
904                         self["videoactions"].setEnabled(True)
905                         self["suggestionactions"].setEnabled(False)
906                         self["searchactions"].setEnabled(False)
907                         self["statusactions"].setEnabled(False)
908                         self["historyactions"].setEnabled(False)
909                         self["key_green"].show()
910                         self["config_actions"].setEnabled(False)
911                         if not append:
912                                 self[self.currList].setIndex(0)
913                         self["feedlist"].updateList(self.videolist)
914                 else:
915                         self.setState('noVideos')
916
917
918         def switchToHistory(self):
919                 print "switchToHistory"
920                 self.oldlist = self.currList
921                 self.currList = "historylist"
922                 print "switchToHistory currentlist",self.currList
923                 print "switchToHistory oldlist",self.oldlist
924                 self.hideSuggestions()
925                 self["ButtonBlue"].hide()
926                 self["VKeyIcon"].hide()
927                 self["key_green"].hide()
928                 self["videoactions"].setEnabled(False)
929                 self["suggestionactions"].setEnabled(False)
930                 self["searchactions"].setEnabled(False)
931                 self["statusactions"].setEnabled(False)
932                 self["config_actions"].setEnabled(False)
933                 self["historyactions"].setEnabled(True)
934                 self.HistoryWindow.activate()
935                 self.HistoryWindow.instance.show()
936
937         def handleHistory(self):
938                 if self.HistoryWindow is None:
939                         self.HistoryWindow = self.session.instantiateDialog(MyTubeHistoryScreen, zPosition=1000)
940                 if self.currList in ("configlist","feedlist"):
941                         if self.HistoryWindow.status() is False:
942                                 print "status is FALSE,switchToHistory"
943                                 self.switchToHistory()
944                 elif self.currList == "historylist":
945                         self.closeHistory()
946
947         def closeHistory(self):
948                 print "closeHistory currentlist",self.currList
949                 print "closeHistory oldlist",self.oldlist
950                 if self.currList == "historylist":
951                         if self.HistoryWindow.status() is True:
952                                 print "status is TRUE, closing historyscreen"
953                                 self.HistoryWindow.deactivate()
954                                 self.HistoryWindow.instance.hide()
955                                 if self.oldlist == "configlist":
956                                         self.switchToConfigList()
957                                 elif self.oldlist == "feedlist":
958                                         self.switchToFeedList()
959
960         def add2History(self):
961                 if self.History is None:
962                         self.History = config.plugins.mytube.general.history.value.split(',')
963                 if self.History[0] == '':
964                         del self.History[0]
965                 print "self.History im add",self.History
966                 if config.plugins.mytube.search.searchTerm.value in self.History:
967                         self.History.remove((config.plugins.mytube.search.searchTerm.value))
968                 self.History.insert(0,(config.plugins.mytube.search.searchTerm.value))
969                 if len(self.History) == 30:
970                         self.History.pop()
971                 config.plugins.mytube.general.history.value = ",".join(self.History)
972                 config.plugins.mytube.general.history.save()
973                 print "configvalue",config.plugins.mytube.general.history.value
974
975         def hideSuggestions(self):
976                 current = self["config"].getCurrent()
977                 if current[1].help_window.instance is not None:
978                         current[1].help_window.instance.hide()
979                 if current[1].suggestionsWindow.instance is not None:
980                         current[1].suggestionsWindow.instance.hide()
981                 self.propagateUpDownNormally = True
982
983         def getFeed(self, chart=None, videoCategoryId=None, ids=[]):
984                 self.queryStarted()
985                 self.queryThread = myTubeService.getFeed(callback=self.gotFeed, chart=chart, videoCategoryId=videoCategoryId, ids=ids)
986
987         def getNextEntries(self, result):
988                 if not result:
989                         return
990                 if myTubeService.hasNextPage():
991                         self.appendEntries = True
992                         myTubeService.getNextPage()
993
994         def getRelatedVideos(self, video):
995                 if video:
996                         self.search(relatedToVideoId=video.id)
997
998         def getChannelVideos(self, video):
999                 if video:
1000                         self.search(channelId=video.channelId)
1001
1002         def runSearch(self, searchTerm = None):
1003                 Log.d(searchTerm)
1004                 if searchTerm:
1005                         self.search(searchTerm=searchTerm)
1006
1007         def search(self, searchTerm=None, relatedToVideoId=None, channelId=None):
1008                 Log.d("searchTerm=%s, relatedToVideoId=%s, channelId=%s" % (searchTerm, relatedToVideoId, channelId))
1009                 self.queryStarted()
1010                 self.appendEntries = False
1011
1012 #               categories = [ config.plugins.mytube.search.categories.value ],
1013
1014                 self.queryThread = myTubeService.search(
1015                                         searchTerm=searchTerm,
1016                                         orderby=config.plugins.mytube.search.orderBy.value,
1017                                         time=config.plugins.mytube.search.time.value,
1018                                         lr=config.plugins.mytube.search.lr.value,
1019                                         relatedToVideoId=relatedToVideoId,
1020                                         channelId=channelId,
1021                                         safeSearch=config.plugins.mytube.search.safeSearch.value,
1022                                         callback=self.gotSearchFeed)
1023
1024         def queryStarted(self):
1025                 if self.queryRunning:
1026                         self.cancelThread()
1027                 self.queryRunning = True
1028
1029         def queryFinished(self):
1030                 self.queryRunning = False
1031
1032         def cancelThread(self):
1033                 print "[MyTubePlayer] cancelThread"
1034                 if self.queryThread is not None:
1035                         self.queryThread.cancel()
1036                 self.queryFinished()
1037
1038         def gotFeed(self, success, items, data):
1039                 print "[MyTubePlayer] gotFeed"
1040                 if self.FirstRun:
1041                         self.FirstRun = False
1042                 self.queryFinished()
1043                 if not success:
1044                         self.gotFeedError(items)
1045                 if success and items is not None:
1046                         self.ytfeed = items
1047                 myTubeService.feed = data
1048                 self.buildEntryList()
1049                 text = _("Results: %s - Page: %s " % (str(myTubeService.getTotalResults()), str(myTubeService.getCurrentPage())))
1050                 #text = "TODO" #TODO text
1051                 #auth_username = myTubeService.getAuthedUsername()
1052                 #if auth_username:
1053                 #                       text = auth_username + ' - ' + text
1054                 self["result"].setText(text)
1055
1056         def gotFeedError(self, error):
1057                 print "[MyTubePlayer] gotFeedError"
1058                 self.queryFinished()
1059                 self.setState('Error')
1060
1061         def gotSearchFeed(self, success, feed, data):
1062                 if self.FirstRun:
1063                         self.FirstRun = False
1064                 self.gotFeed(success, feed, data)
1065
1066         def buildEntryList(self):
1067                 self.mytubeentries = None
1068                 self.screenshotList = []
1069                 self.maxentries = 0
1070                 self.mytubeentries = self.ytfeed
1071                 self.maxentries = len(self.mytubeentries)-1
1072                 if self.mytubeentries and len(self.mytubeentries):
1073                         if self.appendEntries == False:
1074                                 self.videolist = []
1075                                 for video in self.mytubeentries:
1076                                         video_id = video.id
1077                                         thumbnailUrl = None
1078                                         thumbnailUrl = video.thumbnailUrl
1079                                         if thumbnailUrl is not None:
1080                                                 self.screenshotList.append((video_id,thumbnailUrl))
1081                                         if not self.Details.has_key(video_id):
1082                                                 self.Details[video_id] = { 'thumbnail': None}
1083                                         self.videolist.append(self.buildEntryComponent(video, video_id))
1084                                 if len(self.videolist):
1085                                         self["feedlist"].style = "default"
1086                                         self["feedlist"].disable_callbacks = True
1087                                         self["feedlist"].list = self.videolist
1088                                         self["feedlist"].disable_callbacks = False
1089                                         self["feedlist"].setIndex(0)
1090                                         self["feedlist"].setList(self.videolist)
1091                                         self["feedlist"].updateList(self.videolist)
1092                                         if self.FirstRun and not config.plugins.mytube.general.loadFeedOnOpen.value:
1093                                                 self.switchToConfigList()
1094                                         else:
1095                                                 self.switchToFeedList()
1096                         else:
1097                                 self.oldfeedentrycount = 0 #TODO self["feedlist"].count()
1098                                 for video in self.mytubeentries:
1099                                         video_id = video.id
1100                                         thumbnailUrl = None
1101                                         thumbnailUrl = video.thumbnailUrl
1102                                         if thumbnailUrl is not None:
1103                                                 self.screenshotList.append((video_id,thumbnailUrl))
1104                                         if not self.Details.has_key(video_id):
1105                                                 self.Details[video_id] = { 'thumbnail': None}
1106                                         self.videolist.append(self.buildEntryComponent(video, video_id))
1107                                 if len(self.videolist):
1108                                         self["feedlist"].style = "default"
1109                                         old_index = self["feedlist"].index
1110                                         self["feedlist"].disable_callbacks = True
1111                                         self["feedlist"].list = self.videolist
1112                                         self["feedlist"].disable_callbacks = False
1113                                         self["feedlist"].setList(self.videolist)
1114                                         self["feedlist"].setIndex(old_index)
1115                                         self["feedlist"].updateList(self.videolist)
1116                                         self["feedlist"].selectNext()
1117                                         self.switchToFeedList(True)
1118                         if not self.timer_startDownload.isActive():
1119                                 print "STARRTDOWNLOADTIMER IM BUILDENTRYLIST"
1120                                 self.timer_startDownload.start(5)
1121                 else:
1122                         self.setState('Error')
1123                         pass
1124
1125         def buildEntryComponent(self, entry,TubeID):
1126                 title = entry.title
1127                 description = entry.description
1128                 myTubeID = TubeID
1129                 publishedDate = entry.publishedDate
1130                 if publishedDate is not "unknown":
1131                         published = publishedDate.split("T")[0]
1132                 else:
1133                         published = "unknown"
1134                 Views = entry.views
1135                 if Views is not "not available":
1136                         views = Views
1137                 else:
1138                         views = "not available"
1139                 duration = entry.duration
1140                 if duration is not 0:
1141                         durationInSecs = int(duration)
1142                         mins = int(durationInSecs / 60)
1143                         secs = durationInSecs - mins * 60
1144                         duration = "%d:%02d" % (mins, secs)
1145                 else:
1146                         duration = "not available"
1147                 likes = entry.likes
1148                 if likes is not "":
1149                         likes = likes
1150                 else:
1151                         likes = ""
1152                 thumbnail = None
1153                 if self.Details[myTubeID]["thumbnail"]:
1154                         thumbnail = self.Details[myTubeID]["thumbnail"]
1155                 return((entry, title, description, myTubeID, thumbnail, _("Added: ") + str(published), _("Views: ") + str(views), _("Duration: ") + str(duration), _("Likes: ") + str(likes) ))
1156
1157         def getNextEntry(self):
1158                 i = self["feedlist"].getIndex() + 1
1159                 if i < len(self.videolist):
1160                         self["feedlist"].selectNext()
1161                         current = self["feedlist"].getCurrent()
1162                         if current:
1163                                 video = current[0]
1164                                 if video:
1165                                         myurl = video.url
1166                                         if myurl is not None:
1167                                                 print "Got a URL to stream"
1168                                                 myreference = eServiceReference(4097,0,myurl)
1169                                                 myreference.setName(video.title)
1170                                                 return myreference,False
1171                                         else:
1172                                                 print "NoURL im getNextEntry"
1173                                                 return None,True
1174
1175                 print "no more entries to play"
1176                 return None,False
1177
1178         def getPrevEntry(self):
1179                 i = self["feedlist"].getIndex() - 1
1180                 if i >= 0:
1181                         self["feedlist"].selectPrevious()
1182                         current = self["feedlist"].getCurrent()
1183                         if current:
1184                                 video = current[0]
1185                                 if video:
1186                                         myurl = video.url
1187                                         if myurl is not None:
1188                                                 print "Got a URL to stream"
1189                                                 myreference = eServiceReference(4097,0,myurl)
1190                                                 myreference.setName(video.title)
1191                                                 return myreference,False
1192                                         else:
1193                                                 return None,True
1194                 return None,False
1195
1196         def showVideoInfo(self):
1197                 if self.currList == "feedlist":
1198                         self.openInfoScreen()
1199
1200         def openInfoScreen(self):
1201                 if self.currList == "feedlist":
1202                         current = self[self.currList].getCurrent()
1203                         if current:
1204                                 video = current[0]
1205                                 if video:
1206                                         print "Title im showVideoInfo",video.title
1207                                         self.session.open(MyTubeVideoInfoScreen, self.skin_path, video = video )
1208
1209         def downloadThumbnails(self):
1210                 self.timer_startDownload.stop()
1211                 for entry in self.screenshotList:
1212                         thumbnailUrl = entry[1]
1213                         tubeid = entry[0]
1214                         thumbnailFile = "/tmp/"+str(tubeid)+".jpg"
1215                         if self.Details.has_key(tubeid):
1216                                 if self.Details[tubeid]["thumbnail"] is None:
1217                                         if thumbnailUrl is not None:
1218                                                 if tubeid not in self.pixmaps_to_load:
1219                                                         self.pixmaps_to_load.append(tubeid)
1220                                                         if (os_path.exists(thumbnailFile) == True):
1221                                                                 self.fetchFinished(False,tubeid)
1222                                                         else:
1223                                                                 client.downloadPage(thumbnailUrl,thumbnailFile).addCallback(self.fetchFinished,str(tubeid)).addErrback(self.fetchFailed,str(tubeid))
1224                                         else:
1225                                                 if tubeid not in self.pixmaps_to_load:
1226                                                         self.pixmaps_to_load.append(tubeid)
1227                                                         self.fetchFinished(False,tubeid, failed = True)
1228
1229         def fetchFailed(self,string,tubeid):
1230                 print "thumbnail-fetchFailed for: ",tubeid,string.getErrorMessage()
1231                 self.fetchFinished(False,tubeid, failed = True)
1232
1233         def fetchFinished(self,x,tubeid, failed = False):
1234                 print "thumbnail-fetchFinished for:",tubeid
1235                 self.pixmaps_to_load.remove(tubeid)
1236                 if failed:
1237                         thumbnailFile = resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MyTube/plugin.png")
1238                 else:
1239                         thumbnailFile = "/tmp/"+str(tubeid)+".jpg"
1240                 sc = AVSwitch().getFramebufferScale()
1241                 if (os_path.exists(thumbnailFile) == True):
1242                         self.picloads[tubeid] = ePicLoad()
1243                         self.picloads[tubeid].conn = self.picloads[tubeid].PictureData.connect(boundFunction(self.finish_decode, tubeid))
1244                         self.picloads[tubeid].setPara((self["thumbnail"].instance.size().width(), self["thumbnail"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
1245                         self.picloads[tubeid].startDecode(thumbnailFile)
1246                 else:
1247                         self.pixmaps_to_load.append(tubeid)
1248                         self.fetchFinished(False,tubeid, failed = True)
1249
1250         def finish_decode(self,tubeid,info):
1251                 print "thumbnail finish_decode:", tubeid,info
1252                 ptr = self.picloads[tubeid].getData()
1253                 thumbnailFile = "/tmp/"+str(tubeid)+".jpg"
1254                 if ptr != None:
1255                         if self.Details.has_key(tubeid):
1256                                 self.Details[tubeid]["thumbnail"] = ptr
1257                         if (os_path.exists(thumbnailFile) == True):
1258                                 os_remove(thumbnailFile)
1259                         del self.picloads[tubeid]
1260                 else:
1261                         del self.picloads[tubeid]
1262                         if self.Details.has_key(tubeid):
1263                                 self.Details[tubeid]["thumbnail"] = None
1264                 self.timer_thumbnails.start(1)
1265
1266         def updateFeedThumbnails(self):
1267                 self.timer_thumbnails.stop()
1268                 if len(self.picloads) != 0:
1269                         self.timer_thumbnails.start(1)
1270                 else:
1271                         idx = 0
1272                         for entry in self.videolist:
1273                                 tubeid = entry[3]
1274                                 if self.Details.has_key(tubeid):
1275                                         if self.Details[tubeid]["thumbnail"] is not None:
1276                                                 thumbnail = entry[4]
1277                                                 if thumbnail == None:
1278                                                         video = entry[0]
1279                                                         self.videolist[idx] = self.buildEntryComponent(video, tubeid )
1280                                 idx += 1
1281                         if self.currList == "feedlist":
1282                                 self["feedlist"].updateList(self.videolist)
1283
1284
1285 class MyTubeVideoInfoScreen(Screen):
1286         skin = """
1287                 <screen name="MyTubeVideoInfoScreen" position="center,120" size="920,520" title="MyTube - Video Info">
1288                 <ePixmap pixmap="skin_default/buttons/red.png" position="10,5" size="200,40" alphatest="on"/>
1289                 <widget name="key_red" position="10,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" shadowColor="black" shadowOffset="-2,-2"/>
1290                 <widget name="starsbg" position="810,60" size="100,20" zPosition="5" pixmap="~/starsbar_empty.png" alphatest="on"/>
1291                 <widget name="stars" position="810,60" size="100,20" zPosition="6" pixmap="~/starsbar_filled.png" transparent="1"/>
1292                 <eLabel position="0,49" size="920,1" backgroundColor="grey"/>
1293                 <widget source="infolist" render="Listbox" position="33,56" size="240,180" scrollbarMode="showNever" selectionDisabled="1" transparent="1">
1294                         <convert type="TemplatedMultiContent">
1295                                 {"templates":
1296                                         {"default": (180,[
1297                                                 MultiContentEntryPixmapAlphaTest(pos=(0,0),size=(240,180),png=0),# index 0 is the thumbnail
1298                                                 ]),
1299                                         "state": (180,[
1300                                                 MultiContentEntryText(pos=(0,0),size=(240,180),font=0,flags=RT_HALIGN_CENTER | RT_VALIGN_CENTER| RT_WRAP,text=0,color=0xffffff,color_sel=0xffffff,backcolor=0x000000,backcolor_sel=0x000000),# index 0 is the name
1301                                                 ])
1302                                         },
1303                                         "fonts": [gFont("Regular",20)],
1304                                         "itemHeight": 180
1305                                 }
1306                         </convert>
1307                 </widget>
1308                 <widget name="author" position="300,60" size="580,25" font="Regular;22"/>
1309                 <widget name="duration" position="300,90" size="580,25" font="Regular;22"/>
1310                 <widget name="published" position="300,120" size="590,25" font="Regular;22"/>
1311                 <widget name="views" position="300,150" size="590,25" font="Regular;22"/>
1312                 <widget name="tags" position="300,180" size="590,50" font="Regular;22"/>
1313                 <eLabel position="10,245" size="900,1" backgroundColor="grey"/>
1314                 <widget name="detailtext" position="20,255" size="890,270" font="Regular;20"/>
1315                 <widget name="thumbnail" position="0,0" size="240,180" alphatest="on"/> # fake entry for dynamic thumbnail resizing,currently there is no other way doing this.
1316                 <widget name="title" position="0,0" size="0,0"/>
1317         </screen>"""
1318
1319         def __init__(self, session, plugin_path, video = None):
1320                 Screen.__init__(self, session)
1321                 self.session = session
1322                 self.skin_path = plugin_path
1323                 self.video = video
1324                 self.infolist = []
1325                 self.thumbnails = []
1326                 self.picloads = {}
1327                 self["title"] = Label()
1328                 self["key_red"] = Button(_("Close"))
1329                 self["thumbnail"] = Pixmap()
1330                 self["thumbnail"].hide()
1331                 self["detailtext"] = ScrollLabel()
1332                 self["starsbg"] = Pixmap()
1333                 self["stars"] = ProgressBar()
1334                 self["duration"] = Label()
1335                 self["channelTitle"] = Label()
1336                 self["author"] = self["channelTitle"] #skincompat
1337                 self["published"] = Label()
1338                 self["views"] = Label()
1339                 self["tags"] = Label()
1340                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions", "MovieSelectionActions"],
1341                 {
1342                         "back": self.close,
1343                         "red": self.close,
1344                         "up": self.pageUp,
1345                         "down": self.pageDown,
1346                         "left": self.pageUp,
1347                         "right": self.pageDown,
1348                 }, -2)
1349
1350                 self["infolist"] = List(self.infolist)
1351                 self.timer = eTimer()
1352                 self.timer_conn = self.timer.timeout.connect(self.picloadTimeout)
1353                 self.onLayoutFinish.append(self.layoutFinished)
1354                 self.onShown.append(self.setWindowTitle)
1355
1356         def layoutFinished(self):
1357                 self.statuslist = []
1358                 self.statuslist.append(( _("Downloading screenshots. Please wait..." ),_("Downloading screenshots. Please wait..." ) ))
1359                 self["infolist"].style = "state"
1360                 self['infolist'].setList(self.statuslist)
1361                 self.loadPreviewpics()
1362                 if self.video.title is not None:
1363                         self["title"].setText(self.video.title)
1364                 description = None
1365                 if self.video.description is not None:
1366                         self["detailtext"].setText(self.video.description.strip())
1367
1368                 #TODO implement Likes
1369                 self["stars"].hide()
1370                 self["starsbg"].hide()
1371
1372                 if self.video.duration is not 0:
1373                         durationInSecs = int(self.video.duration)
1374                         mins = int(durationInSecs / 60)
1375                         secs = durationInSecs - mins * 60
1376                         duration = "%d:%02d" % (mins, secs)
1377                         self["duration"].setText(_("Duration: ") + str(duration))
1378
1379                 if self.video.channelTitle:
1380                         self["channelTitle"].setText(_("Channel: ") + self.video.channelTitle)
1381
1382                 if self.video.publishedDate is not "unknown":
1383                         self["published"].setText(_("Added: ") + self.video.publishedDate.split("T")[0])
1384
1385                 if self.video.views is not "not available":
1386                         self["views"].setText(_("Views: ") + str(self.video.views))
1387
1388         def setWindowTitle(self):
1389                 self.setTitle(_("MyTubeVideoInfoScreen"))
1390
1391         def pageUp(self):
1392                 self["detailtext"].pageUp()
1393
1394         def pageDown(self):
1395                 self["detailtext"].pageDown()
1396
1397         def loadPreviewpics(self):
1398                 self.thumbnails = []
1399                 self.mythumbubeentries = None
1400                 self.index = 0
1401                 self.maxentries = 0
1402                 self.picloads = {}
1403                 self.mythumbubeentries = [self.video.thumbnailUrl]
1404                 self.maxentries = len(self.mythumbubeentries)-1
1405                 if self.mythumbubeentries:
1406                         currindex = 0
1407                         for entry in self.mythumbubeentries:
1408                                 thumbID = self.video.id + str(currindex)
1409                                 thumbnailFile = "/tmp/" + thumbID + ".jpg"
1410                                 currPic = [currindex,thumbID,thumbnailFile,None]
1411                                 self.thumbnails.append(currPic)
1412                                 thumbnailUrl = None
1413                                 thumbnailUrl = entry
1414                                 if thumbnailUrl is not None:
1415                                         client.downloadPage(thumbnailUrl,thumbnailFile).addCallback(self.fetchFinished,currindex,thumbID).addErrback(self.fetchFailed,currindex,thumbID)
1416                                 currindex +=1
1417                 else:
1418                         pass
1419
1420         def fetchFailed(self, string, index, id):
1421                 print "[fetchFailed] for index:" + str(index) + "for ThumbID:" + id + string.getErrorMessage()
1422
1423         def fetchFinished(self, string, index, id):
1424                 print "[fetchFinished] for index:" + str(index) + " for ThumbID:" + id
1425                 self.decodePic(index)
1426
1427         def decodePic(self, index):
1428                 sc = AVSwitch().getFramebufferScale()
1429                 self.picloads[index] = ePicLoad()
1430                 self.picloads[index].conn = self.picloads[index].PictureData.connect(boundFunction(self.finish_decode, index))
1431                 for entry in self.thumbnails:
1432                         if entry[0] == index:
1433                                 self.index = index
1434                                 thumbnailFile = entry[2]
1435                                 if (os_path.exists(thumbnailFile) == True):
1436                                         print "[decodePic] DECODING THUMBNAIL for INDEX:"+  str(self.index) + "and file: " + thumbnailFile
1437                                         self.picloads[index].setPara((self["thumbnail"].instance.size().width(), self["thumbnail"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
1438                                         self.picloads[index].startDecode(thumbnailFile)
1439                                 else:
1440                                         print "[decodePic] Thumbnail file NOT FOUND !!!-->:",thumbnailFile
1441
1442         def finish_decode(self, picindex = None, picInfo=None):
1443                 print "finish_decode - of INDEX", picindex
1444                 ptr = self.picloads[picindex].getData()
1445                 if ptr != None:
1446                         self.thumbnails[picindex][3] = ptr
1447                         if (os_path.exists(self.thumbnails[picindex][2]) == True):
1448                                 print "removing", self.thumbnails[picindex][2]
1449                                 os_remove(self.thumbnails[picindex][2])
1450                                 del self.picloads[picindex]
1451                                 if len(self.picloads) == 0:
1452                                         self.timer.startLongTimer(3)
1453
1454         def picloadTimeout(self):
1455                 self.timer.stop()
1456                 if len(self.picloads) == 0:
1457                                 self.buildInfoList()
1458                 else:
1459                         self.timer.startLongTimer(2)
1460
1461         def buildInfoList(self):
1462                 self.infolist = []
1463                 thumb0 = None
1464                 thumb1 = None
1465                 thumb2 = None
1466                 thumb3 = None
1467                 count = len(self.thumbnails)
1468                 if count > 0:
1469                         thumb0 = self.thumbnails[0][3]
1470                 if count > 1:
1471                         thumb1 = self.thumbnails[1][3]
1472                 if count > 2:
1473                         thumb2 = self.thumbnails[2][3]
1474                 if count > 3:
1475                         thumb3 = self.thumbnails[3][3]
1476                 self.infolist.append(( thumb0, thumb1, thumb2, thumb3))
1477                 if len(self.infolist):
1478                         self["infolist"].style = "default"
1479                         self["infolist"].disable_callbacks = True
1480                         self["infolist"].list = self.infolist
1481                         self["infolist"].disable_callbacks = False
1482                         self["infolist"].setIndex(0)
1483                         self["infolist"].setList(self.infolist)
1484                         self["infolist"].updateList(self.infolist)
1485
1486
1487 class MyTubeVideoHelpScreen(Screen):
1488         skin = """
1489                 <screen name="MyTubeVideoHelpScreen" position="center,120" size="820,520" title="MyTube - Help">
1490                 <ePixmap pixmap="skin_default/buttons/red.png" position="10,5" size="200,40" alphatest="on"/>
1491                 <widget name="key_red" position="10,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" shadowColor="black" shadowOffset="-2,-2"/>
1492                 <eLabel position="10,50" size="800,1" backgroundColor="grey"/>
1493                 <widget name="detailtext" position="10,60" size="800,450" font="Regular;22" halign="left" valign="top"/>
1494                 <widget name="title" position="0,0" size="0,0"/>
1495         </screen>"""
1496
1497         def __init__(self, session, plugin_path, wantedinfo = None, wantedtitle = None):
1498                 Screen.__init__(self, session)
1499                 self.session = session
1500                 self.skin_path = plugin_path
1501                 self.wantedinfo = wantedinfo
1502                 self.wantedtitle = wantedtitle
1503                 self["title"] = Label()
1504                 self["key_red"] = Button(_("Close"))
1505                 self["detailtext"] = ScrollLabel()
1506
1507                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"],
1508                 {
1509                         "back": self.close,
1510                         "red": self.close,
1511                         "up": self.pageUp,
1512                         "down": self.pageDown,
1513                         "left": self.pageUp,
1514                         "right": self.pageDown,
1515                 }, -2)
1516
1517                 self.onLayoutFinish.append(self.layoutFinished)
1518                 self.onShown.append(self.setWindowTitle)
1519
1520         def layoutFinished(self):
1521                 if self.wantedtitle is None:
1522                         self["title"].setText(_("Help"))
1523                 else:
1524                         self["title"].setText(self.wantedtitle)
1525                 if self.wantedinfo is None:
1526                         self["detailtext"].setText(_("This is the help screen. Feed me with something to display."))
1527                 else:
1528                         self["detailtext"].setText(self.wantedinfo)
1529
1530         def setWindowTitle(self):
1531                 self.setTitle(_("MyTubeVideohelpScreen"))
1532
1533         def pageUp(self):
1534                 self["detailtext"].pageUp()
1535
1536         def pageDown(self):
1537                 self["detailtext"].pageDown()
1538
1539
1540 def MyTubeMain(session, **kwargs):
1541         l2 = False
1542         l2cert = etpm.getData(eTPM.DT_LEVEL2_CERT)
1543         if l2cert is None:
1544                 print "l2cert not found"
1545                 return
1546
1547         l2key = validate_cert(l2cert, rootkey)
1548         if l2key is None:
1549                 print "l2cert invalid"
1550                 return
1551         l2 = True
1552         if l2:
1553                 session.open(MyTubePlayerMainScreen, l2key)
1554
1555 def Plugins(path, **kwargs):
1556         global plugin_path
1557         plugin_path = path
1558         return PluginDescriptor(
1559                 name=_("My TubePlayer"),
1560                 description=_("Play YouTube movies"),
1561                 where = [ PluginDescriptor.WHERE_EXTENSIONSMENU, PluginDescriptor.WHERE_PLUGINMENU ],
1562                 icon = "plugin.png", fnc = MyTubeMain)