adding support for webcams from http://webcam.travel
[enigma2-plugins.git] / webcamviewer / src / WebcamTravel.py
1 from Screens.Screen import Screen
2 from Components.Sources.List import List
3 from Components.Button import Button
4 from Components.Label import Label
5 from Components.ActionMap import ActionMap
6 from Screens.InputBox import InputBox
7 from Components.Input import Input
8 from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest
9 from Components.Pixmap import Pixmap
10 from Components.AVSwitch import AVSwitch
11 from Tools.BoundFunction import boundFunction
12
13 from enigma import eListboxPythonMultiContent, RT_HALIGN_LEFT, RT_HALIGN_RIGHT,ePicLoad,eTimer
14
15 from PictureScreen import PictureScreen
16
17 from twisted.web.client import getPage,downloadPage
18 #from twisted.internet import reactor
19
20 from xml.etree.cElementTree import fromstring as cElementTree_fromstring
21
22 from os import remove as os_remove
23 from os.path import exists as os_path_exists
24 from datetime import datetime
25
26 from urllib import quote as urllib_quote
27 #########################################
28
29 class TravelWebcamviewer(Screen):
30         skin = ""
31         def __init__(self, session, args = 0):
32                 skin =  """<screen position="93,70" size="550,450" title="Webcams provided by webcams.travel">
33
34                         <widget source="list" render="Listbox" position="0,0" size="550,350" zPosition="1" scrollbarMode="showOnDemand" transparent="1"  >
35                                 <convert type="TemplatedMultiContent">
36                                 {"templates":
37                                         {"default": (77,[
38                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 0), size = (100, 75), png = 4), # index 4 is the thumbnail
39                                                         MultiContentEntryText(pos = (100, 1), size = (500, 22), font=0, flags = RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP, text = 1), # index 1 is the Title
40                                                         MultiContentEntryText(pos = (100, 24), size = (300, 18), font=1, flags = RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP, text = 5), # index 5 is the Published Date
41                                                         MultiContentEntryText(pos = (100, 43), size = (300, 18), font=1, flags = RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP, text = 6), # index 6 is the Views Count
42                                                         MultiContentEntryText(pos = (400, 24), size = (200, 18), font=1, flags = RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP, text = 7), # index 7 is the duration
43                                                         MultiContentEntryText(pos = (400, 43), size = (200, 18), font=1, flags = RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP, text = 8), # index 8 is the ratingcount
44                                                 ]),
45                                         "status": (77,[
46                                                         MultiContentEntryText(pos = (10, 1), size = (500, 28), font=2, flags = RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP, text = 0), # index 0 is the name
47                                                         MultiContentEntryText(pos = (10, 22), size = (500, 46), font=3, flags = RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP, text = 1), # index 2 is the description
48                                                 ])
49                                         },
50                                         "fonts": [gFont("Regular", 22),gFont("Regular", 18),gFont("Regular", 26),gFont("Regular", 20)],
51                                         "itemHeight": 77
52                                 }
53                                 </convert>
54                         </widget>
55                         <widget name="thumbnail" position="0,0" size="100,75" alphatest="on"/> # fake entry for dynamic thumbnail resizing, currently there is no other way doing this.
56
57                         <widget name="count" position="5,360" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
58                         <widget name="page" position="150,360" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
59                         <widget name="currentnumbers" position="295,360" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
60
61                         <ePixmap position="5,410" zPosition="0" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
62                         <ePixmap position="150,410" zPosition="1" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
63                         <ePixmap position="295,410" zPosition="2" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
64                         <!-- #not used now# ePixmap position="445,410" zPosition="3" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" //-->
65                         <widget name="key_red" position="5,410" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
66                         <widget name="key_green" position="150,410" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
67                         <widget name="key_yellow" position="295,410" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
68                         <widget name="key_blue" position="445,410" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
69
70                 </screen>"""
71                 self.skin = skin
72                 Screen.__init__(self, session)
73                 self.picloads = {}
74                 self.thumbnails = {}
75
76                 self["list"] = List([])
77                 self["thumbnail"] = Pixmap()
78                 self["thumbnail"].hide()
79
80                 self["count"] = Label(_("Cams: "))
81                 self["page"] = Label(_("Page: "))
82                 self["currentnumbers"] = Label(_("current: "))
83
84
85                 self["key_red"] = Button(_("prev"))
86                 self["key_red"].hide()
87                 self["key_green"] = Button(_("next"))
88                 self["key_green"].hide()
89                 self["key_yellow"] = Button(_("search"))
90                 self["key_blue"] = Button(_("hdkfjhg"))
91
92                 self["key_blue"].hide() #not used at the moment
93
94                 self["actions"] = ActionMap(["WizardActions", "MenuActions", "DirectionActions", "ShortcutActions"],
95                         {
96                          "ok": self.onOK,
97                          "red": self.onRed,
98                          "green": self.onGreen,
99                          "yellow": self.onYellow,
100                          "back": self.close
101                          }, -1)
102
103                 self.timer_default = eTimer()
104                 self.timer_default.timeout.callback.append(self.buildCamList)
105
106                 self.timer_status = eTimer()
107                 self.timer_status.timeout.callback.append(self.buildStatusList)
108
109                 self.timer_labels = eTimer()
110                 self.timer_labels.timeout.callback.append(self.refreshLabels)
111
112                 self.onLayoutFinish.append(self.loadData)
113
114         def onOK(self):
115                 selection = self["list"].getCurrent()
116                 if selection:
117                         print selection
118                         self.session.open(PictureScreen,selection[0].title,selection[0].pic_url)
119
120         def onRed(self):
121                 if self.hasPrevPage():
122                         self.timer_status.start(1)
123                         WebcamTravelerAPI().list_popular(self.onDataLoaded,_page=self.page-1)
124
125         def onGreen(self):
126                 if self.hasNextPage():
127                         self.timer_status.start(1)
128                         WebcamTravelerAPI().list_popular(self.onDataLoaded,_page=self.page+1)
129
130         def onYellow(self):
131                 self.session.openWithCallback(self.onSearchkeyEntered,InputBox, title=_("Please enter a searchkey:"), text="Search Webcams", maxSize=False, type=Input.TEXT)
132
133         def onSearchkeyEntered(self,value):
134                 if value is not None:
135                         self.timer_status.start(1)
136                         WebcamTravelerAPI().search(self.onDataLoaded,value)
137
138         def loadData(self):
139                 self.timer_status.start(1)
140                 WebcamTravelerAPI().list_popular(self.onDataLoaded)
141
142         def onDataLoaded(self,list,count=0,page=0,per_page=0):
143                 print "onDataLoaded",list,count,page,per_page
144                 self.count =count
145                 self.page = page
146                 self.per_page = per_page
147
148                 self.pixmaps_to_load = []
149                 self.picloads = {}
150                 self.thumbnails = {}
151                 self.list = list
152                 self.downloadThumbnails()
153
154         def downloadThumbnails(self):
155                 for cam in self.list:
156                         self.pixmaps_to_load.append(cam.webcamid)
157                         downloadPage(cam.thumbnail_url,"/tmp/"+str(cam.webcamid)+"_thumb.jpg").addCallback(self.fetchFinished,cam.webcamid).addErrback(self.fetchFailed,cam.webcamid)
158
159         def fetchFailed(self,string,webcamid):
160                 print "fetchFailed",webcamid,string.getErrorMessage()
161                 self.buildEntryStatus(string.getErrorMessage())
162                 self.pixmaps_to_load.remove(webcamid)
163
164         def fetchFinished(self,x,webcamid):
165                 print "fetchFinished",x,webcamid
166                 self.pixmaps_to_load.remove(webcamid)
167
168                 sc = AVSwitch().getFramebufferScale()
169                 if (os_path_exists("/tmp/"+str(webcamid)+"_thumb.jpg") == True):
170                         self.picloads[webcamid] = ePicLoad()
171                         self.picloads[webcamid].PictureData.get().append(boundFunction(self.finish_decode, webcamid))
172                         self.picloads[webcamid].setPara((self["thumbnail"].instance.size().width(), self["thumbnail"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
173                         self.picloads[webcamid].startDecode("/tmp/"+str(webcamid)+"_thumb.jpg")
174                 else:
175                         print "[decodePic] Thumbnail file NOT FOUND !!!-->:",thumbnailFile
176
177         def finish_decode(self,webcamid,info):
178                 print "finish_decode - of webcamid", webcamid,info
179                 ptr = self.picloads[webcamid].getData()
180                 if ptr != None:
181                         self.thumbnails[webcamid] = ptr
182                         print "removing file"
183                         os_remove("/tmp/"+str(webcamid)+"_thumb.jpg")
184                         del self.picloads[webcamid]
185                         self.timer_default.start(1)
186
187
188
189         def buildStatusList(self):
190                 self.timer_status.stop()
191                 print "buildStatusList"
192                 statuslist = []
193                 statuslist.append(self.buildEntryStatus("loading data"))
194
195                 self["list"].style = "status"
196                 self["list"].disable_callbacks = True
197                 self["list"].list = statuslist
198                 self["list"].disable_callbacks = False
199                 self["list"].setIndex(0)
200                 self["list"].setList(statuslist)
201                 self["list"].updateList(statuslist)
202
203
204         def buildCamList(self):
205                 if len(self.picloads) != 0:
206                         return
207                 self.timer_default.stop()
208                 print "buildCamList"
209                 statuslist = []
210                 for cam in self.list:
211                         x= self.buildEntryCam(cam)
212                         statuslist.append(x)
213
214                 self["list"].style = "default"
215                 self["list"].disable_callbacks = True
216                 self["list"].list = statuslist
217                 self["list"].disable_callbacks = False
218                 self["list"].setIndex(0)
219                 self["list"].setList(statuslist)
220                 self["list"].updateList(statuslist)
221                 self.timer_labels.start(1)
222
223         def refreshLabels(self):
224                 self.timer_labels.stop()
225                 if self.hasNextPage():
226                         self["key_green"].show()
227                 else:
228                         self["key_green"].hide()
229
230                 if self.hasPrevPage():
231                         self["key_red"].show()
232                 else:
233                         self["key_red"].hide()
234                 self["count"].setText(_("Cams: ")+str(self.count))
235                 self["page"].setText(_("Page: ")+str(self.page)+"/"+str(self.count/self.per_page))
236                 self["currentnumbers"].setText(_("current: ")+str(((self.page-1)*self.per_page)+1)+"-"+str(((self.page-1)*self.per_page)+len(self.list)))
237
238         def buildEntryCam(self, cam):
239                 return ((cam, cam.title, cam.webcamid,"last update",self.thumbnails[cam.webcamid], _("Last updated: ")+cam.last_update, _("Views: ")+cam.view_count, _("User: ")+cam.user, _("Ratings: ")+cam.rating_avg ))
240
241         def buildEntryStatus(self, text):
242                 return (("loading ...", "please wait just a moment","cccccccccccc","last update","1111111111111", _("Last updated: "), _("Views: "), _("Duration: ") , _("Ratings: ") ))
243
244         def hasNextPage(self):
245                 if (self.per_page*(self.page+1)>self.count):
246                         return False
247                 else:
248                         return True
249
250         def hasPrevPage(self):
251                 if (self.page>1):
252                         return True
253                 else:
254                         return False
255
256
257 #########################################
258
259
260
261 #########################################
262 # API ###################################
263 #########################################
264 #########################################
265 #########################################
266
267 class WebcamTravelerAPI:
268         APIKEY="e1019c6811f593a7cca1cf4f536da4c7"
269         URL_HOST = "api.webcams.travel"
270         URL_FORMAT = "rest"
271
272         def get(self,method,callback,errorback,**kwargs):
273                 url = "http://"+self.URL_HOST+"/"+self.URL_FORMAT+"?method="+method+"&devid="+self.APIKEY
274                 for key in kwargs:
275                         print key,kwargs[key]
276                         url +="&"+str(key)+"="+str(kwargs[key])
277                 print url
278                 cb = getPage(url).addCallback(callback)
279                 if errorback!=None:
280                         cb.addErrback(errorback)
281                 else:
282                         cb.addErrback(self.loadingFailed)
283
284         def loadingFailed(self,reason):
285                 print "loadingFailed",reason
286
287         def list_popular(self,callback,_page=1,_per_page=30):
288                 """     wct.webcams.list_popular
289                         Get the popular webcams.
290
291                         devid (required)
292                                 Your developer ID. If you do not have one, please signup for a developer ID.
293                         per_page (optional)
294                                 Number of webcams to return per page. If this argument is omitted, it defaults to 10. The maximum allowed value is 50.
295                         page (optional)
296                                 The page of results to return. If this argument is omitted, it defaults to 1.
297                 """
298                 cb = lambda raw: self.list_popularCB(raw,callback)
299                 self.get("wct.webcams.list_popular",cb,None,page=_page,per_page=_per_page)
300
301         def list_popularCB(self,raw,callback):
302                 dom = cElementTree_fromstring(raw)
303                 list, _count, _page, _per_page = self.parseWebcam(dom)
304                 callback(list,count=_count,page=_page,per_page=_per_page)
305
306         def parseWebcam(self,dom):
307                 cams= dom.findall("webcams")
308                 _count = int(cams[0].findtext("count", 0))
309                 _page = int(cams[0].findtext("page", 0))
310                 _per_page = int(cams[0].findtext("per_page", 0))
311                 list = []
312                 for cam in cams[0].findall("webcam"):
313                         ca = Cam(cam)
314                         list.append(ca)
315                 return list,_count, _page, _per_page
316
317         def search(self,callback,searchkey,_page=1,_per_page=30):
318                 """wct.search.webcams
319
320                         Search the webcams by the given query.
321
322
323                         Arguments
324
325                         devid (required)
326                         Your developer ID. If you do not have one, please signup for a developer ID.
327                         query (required)
328                         The query to search for.
329                         per_page (optional)
330                         Number of comments to return per page. If this argument is omitted, it defaults to 10. The maximum allowed value is 50.
331                         page (optional)
332                         The page of results to return. If this argument is omitted, it defaults to 1.
333                 """
334                 cb = lambda raw: self.searchCB(raw,callback)
335                 self.get("wct.search.webcams",cb,None,query=urllib_quote(searchkey),page=_page,per_page=_per_page)
336
337         def searchCB(self,raw,callback):
338                 dom = cElementTree_fromstring(raw)
339                 list, _count, _page, _per_page = self.parseWebcam(dom)
340                 callback(list,count=_count,page=_page,per_page=_per_page)
341
342
343 class Cam:
344         def __init__(self,element):
345                 self.title = element.findtext("title", 0).encode('utf-8',"ignore")
346                 self.webcamid = int(element.findtext("webcamid", 0))
347                 self.pic_url = "http://images.webcams.travel/webcam/"+str(self.webcamid)+".jpg"
348                 #self.icon_url = element.findtext("icon_url", 0)
349                 self.thumbnail_url = element.findtext("thumbnail_url", 0)
350                 self.view_count = element.findtext("view_count", 0)
351                 self.user = element.findtext("user", 0)
352                 self.userid = element.findtext("userid", 0)
353                 self.rating_avg = element.findtext("rating_avg", 0)
354                 self.rating_count = element.findtext("rating_count", 0)
355                 self.city = element.findtext("city", 0)
356                 self.country = element.findtext("country", 0)
357                 self.continent = element.findtext("continent", 0)
358                 self.latitude = element.findtext("latitude", 0)
359                 self.longitude = element.findtext("longitude", 0)
360
361                 datex = datetime.fromtimestamp(int(element.findtext("last_update", 0)))
362                 self.last_update = datex.strftime("%d.%m.%Y %H:%M:%S")
363
364
365
366