First cut of download queue support

git-svn-id: svn://svn.berlios.de/gpodder/trunk@316 b0d088ad-0a06-0410-aad2-9ed5178a7e87
This commit is contained in:
Thomas Perl 2007-05-01 20:01:51 +00:00
parent 5197cbccfb
commit b1078b4f9e
8 changed files with 156 additions and 17 deletions

View file

@ -1,3 +1,14 @@
Tue, 1 May 2007 21:58:58 +0200 <thp@perli.net>
* src/gpodder/libwget.py: Implement support for download queue using
a semaphore in the downloadStatusManager class
* src/gpodder/libgpodder.py: Configuration options "max_downloads"
and "max_downloads_enabled" for the download queue
* src/gpodder/gpodder.py: Add support for configuration of the
download queue code (gPodderProperties dialog)
* data/gpodder.glade: Add checkbox + spinbutton for download queue
* TODO: Updated TODO list (download queue done)
* bin/gpodder: Pushed version + release date
Sat, 28 Apr 2007 11:10:18 +0200 <thp@perli.net>
* TODO: Updated TODO list

17
TODO
View file

@ -15,20 +15,9 @@
* Clean up the "Downloads" tab in the preferences dialog - too much cruft
has accumulated over the last few months .. maybe move bittorrent stuff
to a seperate tab or group it different? --thp
* Would it be possible to add some form of bandwidth throttling? I've just
started using gpodder and added all my feeds. It immediately marked all
my new episodes and went to download them all at the same time making my
(relatively slow) connection unusuable.
Would it therefore be possible to add some throttling options, such as,
limit number of concurrent downloads to 'x' and limit maximum bandwidth
to 'y'? The latter could be implemented by passing the relevant parameter
to wget, the former would probably require some form of download queue.
--> partially done (limit rate to x kb/s per episode)
(suggested by Paul Elliott <omahns.home gmail.com>)
* Improvements for download queue:
-> get rid of mandatory restarting for semaphore (x simultaneous downloads)
* I think it would be nice if gPodder had a color indicator (like it
already has for downloaded/deleted episodes) for podcasts that haven't

View file

@ -32,8 +32,8 @@ or played back on the user's desktop.
# PLEASE DO NOT CHANGE FORMAT OF __version__ LINE (setup.py reads this)
__author__ = "Thomas Perl <thp@perli.net>"
__version__ = "0.9.1+svn20070421"
__date__ = "2007-04-21"
__version__ = "0.9.1+svn20070501"
__date__ = "2007-05-01"
__copyright__ = "Copyright (c) 2005-2007 %s. All rights reserved." % __author__
__licence__ = "GPL"

View file

@ -2690,7 +2690,7 @@ you can use the usual format of the environment variables:
<widget class="GtkTable" id="tableDownloadsProperties">
<property name="border_width">10</property>
<property name="visible">True</property>
<property name="n_rows">7</property>
<property name="n_rows">9</property>
<property name="n_columns">3</property>
<property name="homogeneous">False</property>
<property name="row_spacing">5</property>
@ -2996,6 +2996,106 @@ you can use the usual format of the environment variables:
<property name="x_options">fill</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="cbMaxDownloads">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Enable download queue:</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="active">False</property>
<property name="inconsistent">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_cbMaxDownloads_toggled" last_modification_time="Tue, 01 May 2007 19:39:03 GMT"/>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkSpinButton" id="spinMaxDownloads">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="climb_rate">1</property>
<property name="digits">0</property>
<property name="numeric">False</property>
<property name="update_policy">GTK_UPDATE_ALWAYS</property>
<property name="snap_to_ticks">False</property>
<property name="wrap">False</property>
<property name="adjustment">3 1 10 1 10 10</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label67">
<property name="visible">True</property>
<property name="label" translatable="yes">simultaneous downloads</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">7</property>
<property name="bottom_attach">8</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label68">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;small&gt;&lt;b&gt;Please note&lt;/b&gt;: Changes for the download queue settings only take effect after gPodder has been restarted.&lt;/small&gt;</property>
<property name="use_underline">False</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">True</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">3</property>
<property name="top_attach">8</property>
<property name="bottom_attach">9</property>
<property name="y_options"></property>
</packing>
</child>
</widget>
<packing>
<property name="tab_expand">False</property>

View file

@ -1173,6 +1173,8 @@ class Gpodderproperties(SimpleGladeApp):
self.downloadnew.set_active(gl.download_after_update)
self.cbLimitDownloads.set_active(gl.limit_rate)
self.spinLimitDownloads.set_value(gl.limit_rate_value)
self.cbMaxDownloads.set_active(gl.max_downloads_enabled)
self.spinMaxDownloads.set_value(gl.max_downloads)
self.showplayed.set_active(gl.show_played)
self.only_sync_not_played.set_active(gl.only_sync_not_played)
if tagging_supported():
@ -1326,6 +1328,11 @@ class Gpodderproperties(SimpleGladeApp):
self.spinLimitDownloads.set_sensitive( self.cbLimitDownloads.get_active())
#-- Gpodderproperties.on_cbLimitDownloads_toggled }
#-- Gpodderproperties.on_cbMaxDownloads_toggled {
def on_cbMaxDownloads_toggled(self, widget, *args):
self.spinMaxDownloads.set_sensitive( self.cbMaxDownloads.get_active())
#-- Gpodderproperties.on_cbMaxDownloads_toggled }
#-- Gpodderproperties.on_btnOK_clicked {
def on_btnOK_clicked(self, widget, *args):
gl = gPodderLib()
@ -1389,6 +1396,8 @@ class Gpodderproperties(SimpleGladeApp):
gl.download_after_update = self.downloadnew.get_active()
gl.limit_rate = self.cbLimitDownloads.get_active()
gl.limit_rate_value = self.spinLimitDownloads.get_value()
gl.max_downloads_enabled = self.cbMaxDownloads.get_active()
gl.max_downloads = int(self.spinMaxDownloads.get_value())
gl.show_played = self.showplayed.get_active()
gl.update_tags = self.updatetags.get_active()
gl.only_sync_not_played = self.only_sync_not_played.get_active()

View file

@ -276,6 +276,11 @@ class Gpodderproperties(SimpleGladeApp):
print "on_cbLimitDownloads_toggled called with self.%s" % widget.get_name()
#-- Gpodderproperties.on_cbLimitDownloads_toggled }
#-- Gpodderproperties.on_cbMaxDownloads_toggled {
def on_cbMaxDownloads_toggled(self, widget, *args):
print "on_cbMaxDownloads_toggled called with self.%s" % widget.get_name()
#-- Gpodderproperties.on_cbMaxDownloads_toggled }
#-- Gpodderproperties.on_btnOK_clicked {
def on_btnOK_clicked(self, widget, *args):
print "on_btnOK_clicked called with self.%s" % widget.get_name()

View file

@ -137,6 +137,8 @@ class gPodderLibClass( object):
self.main_window_height = 450
self.main_window_x = 0
self.main_window_y = 0
self.max_downloads = 3
self.max_downloads_enabled = False
self.mp3_player_folder = ""
self.only_sync_not_played = False
self.__download_history = DownloadHistory( self.get_download_history_filename())
@ -214,6 +216,8 @@ class gPodderLibClass( object):
self.write_to_parser( parser, 'use_gnome_bittorrent', self.use_gnome_bittorrent)
self.write_to_parser( parser, 'device_type', self.device_type)
self.write_to_parser( parser, 'main_window_width', self.main_window_width)
self.write_to_parser( parser, 'max_downloads', self.max_downloads)
self.write_to_parser( parser, 'max_downloads_enabled', self.max_downloads_enabled)
self.write_to_parser( parser, 'main_window_height', self.main_window_height)
self.write_to_parser( parser, 'main_window_x', self.main_window_x)
self.write_to_parser( parser, 'main_window_y', self.main_window_y)
@ -357,6 +361,8 @@ class gPodderLibClass( object):
self.main_window_height = self.get_int_from_parser( parser, 'main_window_height', 450)
self.main_window_x = self.get_int_from_parser( parser, 'main_window_x', 0)
self.main_window_y = self.get_int_from_parser( parser, 'main_window_y', 0)
self.max_downloads = self.get_int_from_parser( parser, 'max_downloads', 3)
self.max_downloads_enabled = self.get_boolean_from_parser(parser, 'max_downloads_enabled', default=False)
self.mp3_player_folder = self.get_from_parser( parser, 'mp3_player_folder', '/media/usbdisk')
self.only_sync_not_played = self.get_boolean_from_parser(parser, 'only_sync_not_played', default=False)
else:

View file

@ -33,6 +33,7 @@ from os import system
from os import kill
from threading import Thread
from threading import Lock
from threading import Semaphore
from shutil import move
from liblogger import log
@ -84,6 +85,9 @@ class downloadThread( object):
libgpodder.gPodderLib().deleteFilename( self.tempname)
command = 'wget --timeout=120 --continue --tries=inf %s --output-document="%s" "%s"' % ( limit_str, self.tempname, self.url )
log( 'Command: %s', command)
if self.statusmgr:
self.statusmgr.updateInfo( self.statusmgr_id, { 'episode':self.cutename, 'speed':_('Queued'), 'progress':0, 'url':self.url})
self.statusmgr.s_acquire()
process = popen2.Popen3( command, True)
self.pid = process.pid
@ -148,6 +152,9 @@ class downloadThread( object):
if self.ready_event != None:
self.ready_event.set()
if self.statusmgr:
self.statusmgr.s_release()
def cancel( self):
self.is_cancelled = True
@ -165,6 +172,8 @@ class downloadStatusManager( object):
self.next_status_id = 0 # Episode name Speed progress (100) url of download
self.smlock = Lock()
self.semaphore = Semaphore( libgpodder.gPodderLib().max_downloads)
# use smlock around every tree_model usage, as seen here:
self.smlock.acquire()
self.tree_model = gtk.ListStore( gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_STRING)
@ -175,6 +184,16 @@ class downloadStatusManager( object):
self.change_notification = change_notification
self.smlock.release()
def s_acquire( self, blocking = True):
if not libgpodder.gPodderLib().max_downloads_enabled:
# If we disabled the limit, don't block new downloads,
# even if we still have a semaphore created
blocking = False
self.semaphore.acquire( blocking)
def s_release( self):
self.semaphore.release()
def has_items( self):
return bool( len( self.status_list))