153 lines
5.4 KiB
Python
153 lines
5.4 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
#
|
||
|
# gPodder - A media aggregator and podcast client
|
||
|
# Copyright (C) 2005-2007 Thomas Perl <thp at perli.net>
|
||
|
#
|
||
|
# gPodder is free software; you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License as published by
|
||
|
# the Free Software Foundation; either version 3 of the License, or
|
||
|
# (at your option) any later version.
|
||
|
#
|
||
|
# gPodder is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
#
|
||
|
|
||
|
|
||
|
#
|
||
|
# download.py -- Download client using DownloadStatusManager
|
||
|
# Thomas Perl <thp@perli.net> 2007-09-15
|
||
|
#
|
||
|
# Based on libwget.py (2005-10-29)
|
||
|
#
|
||
|
|
||
|
from gpodder.liblogger import log
|
||
|
from gpodder import libgpodder
|
||
|
from gpodder import util
|
||
|
from gpodder import services
|
||
|
import gpodder
|
||
|
|
||
|
import threading
|
||
|
import urllib
|
||
|
import shutil
|
||
|
import os.path
|
||
|
import time
|
||
|
|
||
|
class DownloadCancelledException(Exception): pass
|
||
|
|
||
|
|
||
|
class DownloadURLOpener(urllib.FancyURLopener):
|
||
|
version = gpodder.user_agent
|
||
|
|
||
|
def __init__( self, channel):
|
||
|
gl = libgpodder.gPodderLib()
|
||
|
if gl.proxy_use_environment:
|
||
|
proxies = None
|
||
|
else:
|
||
|
proxies = {}
|
||
|
if gl.http_proxy:
|
||
|
proxies['http'] = gl.http_proxy
|
||
|
if gl.ftp_proxy:
|
||
|
proxies['ftp'] = gl.ftp_proxy
|
||
|
|
||
|
self.channel = channel
|
||
|
urllib.FancyURLopener.__init__( self, proxies)
|
||
|
self.addheader( 'Referer', self.channel.url)
|
||
|
|
||
|
def prompt_user_passwd( self, host, realm):
|
||
|
if self.channel.username or self.channel.password:
|
||
|
log( 'Authenticating as "%s" to "%s" for realm "%s".', self.channel.username, host, realm, sender = self)
|
||
|
return ( self.channel.username, self.channel.password )
|
||
|
|
||
|
return ( None, None )
|
||
|
|
||
|
|
||
|
class DownloadThread(threading.Thread):
|
||
|
def __init__( self, channel, episode):
|
||
|
threading.Thread.__init__( self)
|
||
|
self.setDaemon( True)
|
||
|
|
||
|
self.channel = channel
|
||
|
self.episode = episode
|
||
|
|
||
|
self.url = self.episode.url
|
||
|
self.filename = self.episode.local_filename()
|
||
|
self.tempname = os.path.join( os.path.dirname( self.filename), '.tmp-' + os.path.basename( self.filename))
|
||
|
|
||
|
gl = libgpodder.gPodderLib()
|
||
|
self.limit_rate = gl.limit_rate
|
||
|
self.limit_rate_value = gl.limit_rate_value
|
||
|
|
||
|
self.cancelled = False
|
||
|
self.start_time = 0.0
|
||
|
self.speed = _('Queued')
|
||
|
self.progress = 0.0
|
||
|
self.downloader = DownloadURLOpener( self.channel)
|
||
|
|
||
|
def cancel( self):
|
||
|
self.cancelled = True
|
||
|
|
||
|
def status_updated( self, count, blockSize, totalSize):
|
||
|
if totalSize:
|
||
|
self.progress = 100.0*float(count*blockSize)/float(totalSize)
|
||
|
else:
|
||
|
self.progress = 100.0
|
||
|
|
||
|
self.calculate_speed( count, blockSize)
|
||
|
services.download_status_manager.update_status( self.download_id, speed = self.speed, progress = self.progress)
|
||
|
|
||
|
if self.cancelled:
|
||
|
util.delete_file( self.tempname)
|
||
|
raise DownloadCancelledException()
|
||
|
|
||
|
def calculate_speed( self, count, blockSize):
|
||
|
if count % 5 == 0:
|
||
|
now = time.time()
|
||
|
if self.start_time > 0:
|
||
|
passed = now - self.start_time
|
||
|
speed = (count*blockSize)/passed
|
||
|
else:
|
||
|
self.start_time = now
|
||
|
passed = now - self.start_time
|
||
|
speed = count*blockSize
|
||
|
self.speed = '%s/s' % util.format_filesize( speed)
|
||
|
|
||
|
if self.limit_rate and speed > self.limit_rate_value:
|
||
|
# calculate the time that should have passed to reach
|
||
|
# the desired download rate and wait if necessary
|
||
|
should_have_passed = float(count*blockSize)/(self.limit_rate_value*1024.0)
|
||
|
if should_have_passed > passed:
|
||
|
# sleep a maximum of 10 seconds to not cause time-outs
|
||
|
delay = min( 10.0, float(should_have_passed-passed))
|
||
|
time.sleep( delay)
|
||
|
|
||
|
def run( self):
|
||
|
self.download_id = services.download_status_manager.reserve_download_id()
|
||
|
services.download_status_manager.register_download_id( self.download_id, self)
|
||
|
|
||
|
# Initial status update
|
||
|
services.download_status_manager.update_status( self.download_id, episode = self.episode.title, url = self.episode.url, speed = self.speed, progress = self.progress)
|
||
|
|
||
|
acquired = services.download_status_manager.s_acquire()
|
||
|
try:
|
||
|
try:
|
||
|
if self.cancelled:
|
||
|
return
|
||
|
|
||
|
util.delete_file( self.tempname)
|
||
|
self.downloader.retrieve( self.episode.url, self.tempname, reporthook = self.status_updated)
|
||
|
shutil.move( self.tempname, self.filename)
|
||
|
self.channel.addDownloadedItem( self.episode)
|
||
|
finally:
|
||
|
services.download_status_manager.remove_download_id( self.download_id)
|
||
|
services.download_status_manager.s_release( acquired)
|
||
|
except DownloadCancelledException:
|
||
|
log( 'Download has been cancelled: %s', self.episode.title, sender = self)
|
||
|
except:
|
||
|
log( 'Error while downloading "%s".', self.episode.title, sender = self, traceback = True)
|
||
|
|