from PyQt4.QtCore import * from PyQt4.QtGui import * from PyQt4.QtNetwork import QHttp from PyKDE4.plasma import Plasma from PyKDE4 import plasmascript from PyKDE4.kdeui import KMessageBox import sys import os import os.path import time import datetime import urllib2 # parsing modules from BeautifulSoup import BeautifulSoup import re class ShowDesc(QThread): def __init__(self, app, showName): QThread.__init__(self) self.app = app self.showName = showName.replace("_", " ") HOST = "http://www.tvrage.com/" self.showUrl = QUrl(HOST+showName.replace(" ","_")) self.imageLocal = self.app.applet.package().path()+"contents/shows/"+self.showName.toLower()+".jpg" self.imageUrl = None self.title = None self.ntimestamp = None self.ptimestamp = None self.busy = True self.finalEpisode = False self.label = None self.ntime = None self.ntimestamp = float(sys.maxint) self.__initGui() self.setEdit(False) def __del__(self): print "removed this showdesc" def __initGui(self): if not self.label == None: print "already guied bitch!" return l = Plasma.Label(self.app.applet) l.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) l.setMinimumSize(48,48) l.setMaximumSize(48,48) self.imgLabel = l l = Plasma.Label(self.app.applet) l.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) l.setMinimumSize(170,48) l.setMaximumSize(170,48) #l.setAutoFillBackground(True) #l.setStyleSheet("QLabel {background: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #eef, stop: 1 #ccf); padding: 1px;border-style: solid;border: 2px solid black;border-radius: 8px;}") #l.setAutoFillBackground(True) self.label = l l = Plasma.Label(self.app.applet) l.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) l.setMinimumSize(80,48) l.setMaximumSize(80,48) #l.setAutoFillBackground(True) #l.setStyleSheet("QLabel {background: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #eef, stop: 1 #ccf); padding: 1px;border-style: solid;border: 2px solid black;border-radius: 8px;}") #l.setAutoFillBackground(True) self.dateLabel = l p = Plasma.PushButton(self.app.applet) icon23 = QIcon() icon23.addPixmap(QPixmap(self.app.applet.package().filePath("ui","remove.png")), QIcon.Normal, QIcon.Off) p.setIcon(icon23) p.setEnabled(False) p.setMinimumSize(48,48) p.setMaximumSize(48,48) p.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) self.removeButton = p self.busyWidget = Plasma.BusyWidget() self.busyWidget.setRunning(True) self.imgLabelContainer = QGraphicsAnchorLayout () self.imgLabelContainer.addAnchor(self.imgLabel,Qt.AnchorVerticalCenter,self.removeButton,Qt.AnchorVerticalCenter) self.imgLabelContainer.addAnchor(self.imgLabel,Qt.AnchorVerticalCenter,self.busyWidget,Qt.AnchorVerticalCenter) self.containerLayout = QGraphicsLinearLayout(Qt.Horizontal) self.containerLayout.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Minimum) self.containerLayout.addItem(self.imgLabelContainer) self.containerLayout.addItem(self.label) self.containerLayout.addItem(self.dateLabel) self.containerLayout.setSpacing(0) self.container = Plasma.Frame() self.container.setLayout(self.containerLayout) self.container.setContentsMargins(0,0,0,0) self.container.setWindowFrameMargins(0,0,0,0) QObject.connect(self.removeButton, SIGNAL("clicked()"), self.destroy) def setEdit(self, t): if not t: self.removeButton.setEnabled(False) self.removeButton.setVisible(False) else: self.removeButton.setEnabled(True) self.removeButton.setVisible(True) def destroy(self): print "destroy" self.label.close() self.imgLabel.close() self.dateLabel.close() self.container.close() # delete show for s in self.app.shows: if s == self: print "removing %s" % self.showName self.app.shows.remove(s) self.app.update() self.app.saveShows() self.quit() def outputElements(self): return self.container def get(self): self.begin() print "Preparing http" print self.showUrl.host() self.http = QHttp(self.showUrl.host()); self.infoBuffer = QBuffer() self.http.get(self.showUrl.path(), self.infoBuffer) QObject.connect(self.http, SIGNAL("done(bool)"), self.fetchedInfo) return True def getImage(self): if self.imageUrl == None: return if (QFile.exists(self.imageLocal)): print "Local copy of image found for '%s'" % self.showName self.imageBuffer = QFile(self.imageLocal) self.http = None self.fetchedImage(False) return print "Preparing http request for image retrieval 'http://%s%s'" % (self.imageUrl.host(),self.imageUrl.path()) self.http = QHttp(self.imageUrl.host()); self.imageBuffer = QFile(self.imageLocal) if not self.imageBuffer.open(QIODevice.WriteOnly): print "Can't open '%s' for writing" % self.imageLocal return self.http.get(self.imageUrl.path(), self.imageBuffer) QObject.connect(self.http, SIGNAL("done(bool)"), self.fetchedImage) def fetchedImage(self,error): filename = self.imageBuffer.fileName() print "fetched image '%s'" % filename self.updateImage(filename) if not self.http == None: self.http.close(); self.imageBuffer.close(); self.end() def begin(self): self.busy = True self.busyWidget.setRunning(True) self.busyWidget.setVisible(True) def end(self,bypassFlag=False): self.busyWidget.setRunning(False) self.busyWidget.setVisible(False) if not bypassFlag: self.busy = False def fetchedInfo(self, error): print "Got a Http response" html = str(self.infoBuffer.data()) # got a http respose, now let's parse it soup = BeautifulSoup(html) numFields = 6 newEpisode = [ None for i in xrange(numFields)] prevEpisode = [ None for i in xrange(numFields)] self.fetchOkay = False # get episode infos h2Tags = soup.findAll('h2') for h in h2Tags: print h # get new episode infos matchObj = re.match(r'.*Next: ([0-9]*)x([0-9]*).--.(.*).\((.*)\/(.*)\/(.*)\) ', str(h), re.M|re.I) if matchObj: newEpisode = matchObj.groups() fetchOkay = True continue # get previous episode infos matchObj = re.match(r'.*Prev: ([0-9]*)x([0-9]*).--.(.*).\((.*)\/(.*)\/(.*)\) ', str(h), re.M|re.I) if matchObj: prevEpisode = matchObj.groups() print "matched prev" self.fetchOkay = True continue # check if last episode aired? matchObj = re.match(r'.*Final: ([0-9]*)x([0-9]*).--.(.*).\((.*)\/(.*)\/(.*)\) ', str(h), re.M|re.I) if matchObj: prevEpisode = matchObj.groups() print "matched prev" self.finalEpisode = True self.fetchOkay = True continue # check if we got valid data or not if not self.fetchOkay and self.app.loaded and self.app.initialized: KMessageBox.messageBox(None, KMessageBox.Information, "Show '%s' can't be found, sorry!" % self.showName) print "fetch wasn't valid" self.destroy() return False # get image info, since we already scanned anyway imgTags = soup.findAll('img', {'src' : re.compile(r'(.*shows.*)(jpe?g)|(png)$')}) for i in imgTags: matchObj = re.match(r'.*src..(.*shows.*).jpg.*>', str(i)) if matchObj: imgSrc = matchObj.group(1) continue self.imageUrl = QUrl(imgSrc+".jpg") # start image retrieval self.getImage() # use the time that it takes to get the image for assignment of the fetched info self.ntime = None self.ptime = None self.title = None self.ntimestamp = float(sys.maxint) self.ptimestamp = None # title self.title = newEpisode[2] monthNum = {'Feb': 2, 'Aug': 8, 'Jan': 1, 'Dec': 12, 'Oct': 10, 'Mar': 3, 'Sep': 9, 'May': 5, 'Jun': 6, 'Jul': 7, 'Apr': 4, 'Nov': 11} # time new episode day = newEpisode[3] month = newEpisode[4] year = newEpisode[5] if not (day==None and month==None and year==None): datestring = "%s %s %s" % (monthNum[day], month, year) self.ntime = time.strptime(datestring,"%m %d %Y") self.ntimestamp = time.mktime(self.ntime) # time prev episode day = prevEpisode[3] month = prevEpisode[4] year = prevEpisode[5] if not (day==None and month==None and year==None): datestring = "%s %s %s" % (monthNum[day], month, year) self.ptime = time.strptime(datestring,"%m %d %Y") self.ptimestamp = time.mktime(self.ptime) print self.ptime self.updateText() def updateImage(self, filename): self.imgLabel.nativeWidget().setPixmap(QPixmap(filename).scaled(48,48)) def updateText(self): if self.title == None: ntitle="TBA" else: ntitle=self.title if self.finalEpisode: ntitle="no more episodes" self.label.setText(" " \ "%(name)s
%(title)s" % dict(name=self.showName.replace("_"," "), title=ntitle)) if self.ntime == None: ntimestr = "TBA" elif time.strftime("%d. %b %y",self.ntime) == time.strftime("%d. %b %y",time.localtime()): ntimestr = "Today" else: ntimestr = time.strftime("%d. %b %y",self.ntime) if self.finalEpisode: ntimestr="finished" self.dateLabel.setText(" " \ "%(ntime)s
%(ptime)s" % dict(name=self.showName, \ ntime=ntimestr, \ ptime=time.strftime("%d. %b %y",self.ptime) if not self.ptime == None else "n/a", \ )) class AirdatePlasmoidApplet(plasmascript.Applet): def __init__(self,parent,args=None): plasmascript.Applet.__init__(self,parent) def init(self): self.__initGui() self.setEnabled(False) self.firstRun = True self.loaded = False self.initialized = False self.needsInitRefetch = True self.timeEngine = self.dataEngine("time") self.timeEngine.connectSource("Local", self, 1000) self.firstRun = False def __initGui(self): self.setAspectRatioMode(Plasma.IgnoreAspectRatio) self.setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Preferred) self.setMinimumSize(370,250) # No configuration interface supported self.setHasConfigurationInterface(False) self.layout = QGraphicsLinearLayout(Qt.Vertical, self.applet) self.setLayout(self.layout) # setup gui self.scrollArea = Plasma.ScrollWidget() self.frame = Plasma.Frame() self.frame.setLayout(QGraphicsLinearLayout(Qt.Vertical)) self.scrollArea.setWidget(self.frame) self.outputLayout = self.frame.layout() self.outputLayout.setSpacing(0) self.layout.addItem(self.scrollArea) self.controlLayout = QGraphicsLinearLayout(Qt.Horizontal) self.controlLayout.setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Preferred) self.layout.addItem(self.controlLayout) self.showUrl = Plasma.LineEdit(self.applet) self.controlLayout.addItem(self.showUrl) self.showUrl.setEnabled(False) self.addButton = Plasma.PushButton(self.applet) icon23 = QIcon() icon23.addPixmap(QPixmap(self.applet.package().filePath("ui","add.png")), QIcon.Normal, QIcon.Off) self.addButton.setIcon(icon23) self.addButton.setMinimumSize(24,24) self.addButton.setMaximumSize(24,24) self.addButton.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) self.addButton.setEnabled(False) self.controlLayout.addItem(self.addButton) QObject.connect(self.addButton, SIGNAL("clicked()"), self.addShow) self.updateButton = Plasma.PushButton(self.applet) self.updateButton.setMinimumSize(24,24) self.updateButton.setMaximumSize(24,24) self.updateButton.setEnabled(False) icon23 = QIcon() icon23.addPixmap(QPixmap(self.applet.package().filePath("ui","reload.png")), QIcon.Normal, QIcon.Off) self.updateButton.setIcon(icon23) self.updateButton.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) self.controlLayout.addItem(self.updateButton) QObject.connect(self.updateButton, SIGNAL("clicked()"), self.refetch) self.editButton = Plasma.PushButton(self.applet) self.editButton.setEnabled(False) self.editButton.setMinimumSize(24,24) self.editButton.setMaximumSize(24,24) self.editButton.setCheckable(True) icon23 = QIcon() icon23.addPixmap(QPixmap(self.applet.package().filePath("ui","edit.png")), QIcon.Normal, QIcon.Off) self.editButton.setIcon(icon23) self.editButton.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) self.controlLayout.addItem(self.editButton) QObject.connect(self.editButton, SIGNAL("clicked()"), self.editShows) def editShows(self): if self.editButton.isChecked(): self.addButton.setEnabled(True) self.showUrl.setEnabled(True) for s in self.shows: s.setEdit(True) else: self.addButton.setEnabled(False) self.showUrl.setEnabled(False) for s in self.shows: s.setEdit(False) def addShow(self): self.setEnabled(False) showName = self.showUrl.text() print "adding show %s" % showName for sd in self.shows: if str(sd.showName).lower() == str(showName).lower(): print "show is already there!!" return newShowDesc = ShowDesc(self,showName) newShowDesc.setEdit(self.editButton.isChecked()) if newShowDesc.get(): self.shows.append(newShowDesc) self.outputLayout.addItem(newShowDesc.outputElements()) self.saveShows() # switch to watch mode self.timeEngine.connectSource("Local",self,1000) def saveShows(self): settings = QSettings("Airdate-Plasmoid","Airdate-Plasmoid") showNames = [ s.showName for s in self.shows ] settings.setValue("shows", showNames) showTitles = [ s.title for s in self.shows ] settings.setValue("titles", showTitles) showNTimestamps = [ s.ntimestamp for s in self.shows ] print showNTimestamps settings.setValue("ntimestamps", showNTimestamps) showPTimestamps = [ s.ptimestamp for s in self.shows ] settings.setValue("ptimestamps", showPTimestamps) def output(self): print "updating output" # sort shows: self.shows = sorted(self.shows, key=lambda ShowDesc: ShowDesc.ntimestamp, reverse=False) for i,s in enumerate(self.shows): self.outputLayout.addItem(s.outputElements()) def update(self): removeList = [] for i in range(self.outputLayout.count()): if not self.outputLayout.itemAt(i).graphicsItem().isVisible(): removeList.append(i) print "not visible!" for i in removeList: self.outputLayout.removeAt(i) def isConnectedToInternet(self): try: response=urllib2.urlopen('http://www.tvrage.com',timeout=2) return True except urllib2.URLError as err: pass return False def refetch(self): self.setEnabled(False) self.updateButton.setEnabled(False) if not self.isConnectedToInternet(): print "not connected to internet" self.needsInitRefetch = True if self.loaded: self.loaded=False print "switching to watch mode" self.timeEngine.connectSource("Local",self,1000) for sd in self.shows: sd.end(bypassFlag=True) return False for i,sd in enumerate(self.shows): sd.get() # switch to watch mode if self.loaded: print "switching to watch mode" self.timeEngine.connectSource("Local",self,1000) return True def isEverythingFetched(self): for s in self.shows: if s.busy == True: return False return True @pyqtSignature("dataUpdated(const QString &, const Plasma::DataEngine::Data &)") def dataUpdated(self, sourceName, data): if self.firstRun: return print "Status: init %s | needinitrefetch %s | loaded %s | internet %s" % (str(self.initialized), str(self.needsInitRefetch), str(self.loaded), self.isConnectedToInternet()) if not self.initialized: print "\tInitialising" # load settings settings = QSettings("Airdate-Plasmoid","Airdate-Plasmoid") nstamps = settings.value("ntimestamps").toList() pstamps = settings.value("ptimestamps").toList() titles = settings.value("titles").toList() shows = settings.value("shows").toList() self.shows = [] for s in shows: sd = ShowDesc(self, s.toString().replace("_", " ")) self.shows.append(sd) self.initialized = True self.needsInitRefetch = True elif self.needsInitRefetch and self.initialized: self.output() self.loaded = self.refetch() self.needsInitRefetch = not self.loaded elif self.loaded and self.isEverythingFetched(): print "\teverything is fetched" ## do sorting when everything is ready self.setEnabled(True) #delete everything, now we can do some sorting! for s in self.shows: self.outputLayout.removeItem(s.outputElements()) #sort this ... self.output() # switch to more conservative mode self.timeEngine.disconnectSource("Local",self) self.editButton.setEnabled(True) self.updateButton.setEnabled(True) self.loaded = True else: print "\t skipped" def CreateApplet(parent): return AirdatePlasmoidApplet(parent)