--- /dev/null
+from twisted.internet import reactor, defer
+from twisted.internet.protocol import Protocol, ClientCreator
+from twisted.protocols.ftp import FTPClient, FTPFileListProtocol
+
+from os import SEEK_END
+
+# XXX: did I ever actually test supportPartial?
+class FTPDownloader(Protocol):
+ """Download to a file from FTP."""
+
+ def __init__(self, host, port, path, fileOrName, username = 'anonymous', \
+ password = 'my@email.com', passive = True, supportPartial = False, \
+ *args, **kwargs):
+
+ timeout = 30
+
+ # We need this later
+ self.path = path
+ self.resume = supportPartial
+
+ # Output
+ if isinstance(fileOrName, str):
+ self.filename = fileOrName
+ self.file = None
+ else:
+ self.file = fileOrName
+
+ creator = ClientCreator(reactor, FTPClient, username, password, passive = passive)
+
+ creator.connectTCP(host, port, timeout).addCallback(self.controlConnectionMade).addErrback(self.connectionFailed)
+
+ self.deferred = defer.Deferred()
+
+ def controlConnectionMade(self, ftpclient):
+ # We need the client locally
+ self.ftpclient = ftpclient
+
+ # Try to fetch filesize
+ self.ftpFetchSize()
+
+ # Handle recieved msg
+ def sizeRcvd(self, msgs):
+ # Split up return
+ code, msg = msgs[0].split()
+ if code == '213':
+ self.totallength = int(msg)
+ # We know the size, so start fetching
+ self.ftpFetchFile()
+ else:
+ # Error while reading size, try to list it
+ self.ftpFetchList()
+
+ def ftpFetchSize(self):
+ d = self.ftpclient.queueStringCommand('SIZE ' + self.path)
+ d.addCallback(self.sizeRcvd).addErrback(self.ftpFetchList)
+
+ # Handle recieved msg
+ def listRcvd(self, *args):
+ # Quit if file not found
+ if not len(self.filelist.files):
+ self.connectionFailed()
+ return
+
+ self.totallength = self.filelist.files[0]['size']
+
+ # Invalidate list
+ self.filelist = None
+
+ # We know the size, so start fetching
+ self.ftpFetchFile()
+
+ def ftpFetchList(self, *args, **kwargs):
+ self.filelist = FTPFileListProtocol()
+ d = self.ftpclient.list(self.path, self.filelist)
+ d.addCallback(self.listRcvd).addErrback(self.connectionFailed)
+
+ def openFile(self):
+ if self.resume:
+ file = open(self.filename, 'ab')
+ else:
+ file = open(self.filename, 'wb')
+
+ return (file, file.tell())
+
+ def ftpFetchFile(self):
+ offset = 0
+
+ # Finally open file
+ if self.file is None:
+ try:
+ self.file, offset = self.openFile()
+ except IOError, ie:
+ self.connectionFailed()
+ return
+
+ offset = self.resume and offset or 0
+
+ d = self.ftpclient.retrieveFile(self.path, self, offset = offset)
+ d.addCallback(self.ftpFinish).addErrback(self.connectionFailed)
+
+ def dataReceived(self, data):
+ if not self.file:
+ return
+
+ try:
+ self.file.seek(0, SEEK_END)
+
+ self.file.write(data)
+ except IOError, ie:
+ self.connectionFailed()
+
+ def ftpFinish(self, code = 0, message = None):
+ self.ftpclient.quit()
+ if self.file is not None:
+ self.file.close()
+ self.deferred.callback(code)
+
+ def connectionFailed(self, reason = None):
+ if self.file is not None:
+ self.file.close()
+ self.deferred.errback(reason)
from Components.config import config
from Components.Pixmap import Pixmap
from Components.ActionMap import ActionMap
-from twisted.web.client import downloadPage
+
+from FTPDownloader import FTPDownloader
+from twisted.web.client import HTTPDownloader
+from twisted.internet import reactor
+from urlparse import urlparse, urlunparse
+
+def _parse(url, defaultPort = None):
+ url = url.strip()
+ parsed = urlparse(url)
+ scheme = parsed[0]
+ path = urlunparse(('','')+parsed[2:])
+
+ if defaultPort is None:
+ if scheme == 'https':
+ defaultPort = 443
+ elif scheme == 'ftp':
+ defaultPort = 21
+ else:
+ defaultPort = 80
+
+ host, port = parsed[1], defaultPort
+
+ if '@' in host:
+ username, host = host.split('@')
+ if ':' in username:
+ username, password = username.split(':')
+ else:
+ password = ""
+ else:
+ username = ""
+ password = ""
+
+ if ':' in host:
+ host, port = host.split(':')
+ port = int(port)
+
+ if path == "":
+ path = "/"
+
+ return scheme, host, port, path, username, password
+
+def download(url, file, contextFactory = None, *args, **kwargs):
+
+ """Download a remote file from http(s) or ftp.
+
+ @param file: path to file on filesystem, or file-like object.
+
+ See HTTPDownloader to see what extra args can be passed if remote file
+ is accessible via http or https. Both Backends should offer supportPartial.
+ """
+ scheme, host, port, path, username, password = _parse(url)
+
+ if scheme == 'ftp':
+ if not (username and password):
+ username = 'anonymous'
+ password = 'my@email.com'
+
+ client = FTPDownloader(
+ host,
+ port,
+ path,
+ file,
+ username,
+ password,
+ *args,
+ **kwargs
+ )
+ return client.deferred
+
+ # We force username and password here as we lack a satisfying input method
+ if username and password:
+ from base64 import encodestring
+
+ # twisted will crash if we don't rewrite this ;-)
+ url = scheme + '://' + host + ':' + str(port) + path
+
+ basicAuth = encodestring("%s:%s" % (username, password))
+ authHeader = "Basic " + basicAuth.strip()
+ AuthHeaders = {"Authorization": authHeader}
+
+ if kwargs.has_key("headers"):
+ kwargs["headers"].update(AuthHeaders)
+ else:
+ kwargs["headers"] = AuthHeaders
+
+ factory = HTTPDownloader(url, file, *args, **kwargs)
+ if scheme == 'https':
+ from twisted.internet import ssl
+ if contextFactory is None:
+ contextFactory = ssl.ClientContextFactory()
+ reactor.connectSSL(host, port, factory, contextFactory)
+ else:
+ reactor.connectTCP(host, port, factory)
+
+ return factory.deferred
class PictureScreen(Screen):
- skin = ""
- prozessing =False # if fetching or converting is active
- autoreload =False
- def __init__(self, session,title,filename, slideshowcallback = None,args=0):
- self.session=session
- self.slideshowcallback=slideshowcallback
- self.screentitle = title
- ##
- size_w = getDesktop(0).size().width()
- size_h = getDesktop(0).size().height()
- self.skin = """
- <screen position="0,0" size="%i,%i" title="%s" flags=\"wfNoBorder\">
- <widget name="pixmap" position="0,0" size="%i,%i" backgroundColor=\"black\"/>
- </screen>""" % (size_w,size_h,filename,size_w,size_h)
- Screen.__init__(self, session)
- self.filename = filename
- self["pixmap"] = Pixmap()
-
- self["actions"] = ActionMap(["WizardActions", "DirectionActions","ChannelSelectBaseActions","ShortcutActions"],
- {
- "ok": self.do,
- "back": self.exit,
- "green":self.AutoReloaderSwitch,
- }, -1)
-
- self.onLayoutFinish.append(self.do)
-
- def AutoReloaderSwitch(self):
- if self.filename.startswith("http") or self.filename.startswith("ftp"):
- if self.autoreload is False:
- self.autoreload = True
- self.do()
- else:
- self.autoreload = False
-
- def do(self):
- if self.prozessing:
- pass
- elif self.filename.startswith("http") or self.filename.startswith("ftp"):
- self.fetchFile(self.filename)
- else:
- self.sourcefile = self.filename
- self.setPicture(self.filename)
-
- def exit(self):
- self.cleanUP()
- self.close()
-
- def cleanUP(self):
- try:
- if os.path.exists("/tmp/loadedfile"):
- os.remove("/tmp/loadedfile")
- except:## OSerror??
- pass
-
- def fetchFile(self,url):
- self.prozessing =True
- self.setTitle("loading File")
- print "fetching URL ",url
- self.sourcefile = "/tmp/loadedfile"
- downloadPage(url,self.sourcefile).addCallback(self.fetchFinished).addErrback(self.fetchFailed)
-
-
- def fetchFailed(self,string):
- print "fetch failed",string
- self.setTitle( "fetch failed: "+string)
-
- def fetchFinished(self,string):
- print "fetching finished "
- self.setPicture(self.sourcefile)
-
- def setPicture(self,string):
- self.setTitle(self.filename.split("/")[-1])
- pixmap = loadPic(string,getDesktop(0).size().width(),getDesktop(0).size().height(), AVSwitch().getAspectRatioSetting()/2,1, 0,1)
- if pixmap is not None:
- self["pixmap"].instance.setPixmap(pixmap)
- self.prozessing =False
-
- if self.autoreload is True:
- self.cleanUP()
- self.do()
- elif self.slideshowcallback is not None:
- self.closetimer = eTimer()
- self.closetimer.timeout.get().append(self.slideshowcallback)
- print "waiting ",config.plugins.pictureviewer.slideshowtime.value," seconds for next picture"
- self.closetimer.start(int(config.plugins.pictureviewer.slideshowtime.value))
-
+ skin = ""
+ prozessing =False # if fetching or converting is active
+ autoreload =False
+ def __init__(self, session,title,filename, slideshowcallback = None,args=0):
+ self.slideshowcallback=slideshowcallback
+ self.screentitle = title
+ ##
+ size_w = getDesktop(0).size().width()
+ size_h = getDesktop(0).size().height()
+ self.skin = """
+ <screen position="0,0" size="%i,%i" title="%s" flags=\"wfNoBorder\">
+ <widget name="pixmap" position="0,0" size="%i,%i" backgroundColor=\"black\"/>
+ </screen>""" % (size_w,size_h,filename,size_w,size_h)
+ Screen.__init__(self, session)
+ self.filename = filename
+ self["pixmap"] = Pixmap()
+
+ self["actions"] = ActionMap(["WizardActions", "DirectionActions","ChannelSelectBaseActions","ShortcutActions"],
+ {
+ "ok": self.do,
+ "back": self.exit,
+ "green":self.AutoReloaderSwitch,
+ }, -1)
+
+ self.onLayoutFinish.append(self.do)
+
+ def AutoReloaderSwitch(self):
+ if self.filename.startswith("http") or self.filename.startswith("ftp"):
+ if self.autoreload is False:
+ self.autoreload = True
+ self.do()
+ else:
+ self.autoreload = False
+
+ def do(self):
+ if self.prozessing:
+ pass
+ elif self.filename.startswith("http") or self.filename.startswith("ftp"):
+ self.fetchFile(self.filename)
+ else:
+ self.sourcefile = self.filename
+ self.setPicture(self.filename)
+
+ def exit(self):
+ self.cleanUP()
+ self.close()
+
+ def cleanUP(self):
+ try:
+ if os.path.exists("/tmp/loadedfile"):
+ os.remove("/tmp/loadedfile")
+ except:## OSerror??
+ pass
+
+ def fetchFile(self,url):
+ self.prozessing =True
+ self.setTitle("loading File")
+ print "fetching URL ",url
+ self.sourcefile = "/tmp/loadedfile"
+ download(url,self.sourcefile).addCallback(self.fetchFinished).addErrback(self.fetchFailed)
+
+ def fetchFailed(self,string):
+ print "fetch failed",string
+ self.setTitle( "fetch failed: "+string)
+
+ def fetchFinished(self,string):
+ print "fetching finished "
+ self.setPicture(self.sourcefile)
+
+ def setPicture(self,string):
+ self.setTitle(self.filename.split("/")[-1])
+ pixmap = loadPic(string,getDesktop(0).size().width(),getDesktop(0).size().height(), AVSwitch().getAspectRatioSetting()/2,1, 0,1)
+ if pixmap is not None:
+ self["pixmap"].instance.setPixmap(pixmap)
+ self.prozessing =False
+
+ if self.autoreload is True:
+ self.cleanUP()
+ self.do()
+ elif self.slideshowcallback is not None:
+ self.closetimer = eTimer()
+ self.closetimer.timeout.get().append(self.slideshowcallback)
+ print "waiting ",config.plugins.pictureviewer.slideshowtime.value," seconds for next picture"
+ self.closetimer.start(int(config.plugins.pictureviewer.slideshowtime.value))
+