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: <a.*>([0-9]*)x([0-9]*).--.(.*)</a>.\((.*)\/(.*)\/(.*)\) ', str(h), re.M|re.I)
if matchObj:
newEpisode = matchObj.groups()
fetchOkay = True
continue
# get previous episode infos
matchObj = re.match(r'.*Prev: <a.*>([0-9]*)x([0-9]*).--.(.*)</a>.\((.*)\/(.*)\/(.*)\) ', 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: <a.*>([0-9]*)x([0-9]*).--.(.*)</a>.\((.*)\/(.*)\/(.*)\) ', 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(" " \
"<b>%(name)s</b><br>%(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(" " \
"<span style=\"color:#1245aa; font-weight:bold;\">%(ntime)s</span><br><span style=\"font-size:7pt;color:#1245aa;\">%(ptime)s</span>" % 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)