support for single file transfer to device, based on a patch by Preben Randhol

git-svn-id: svn://svn.berlios.de/gpodder/trunk@280 b0d088ad-0a06-0410-aad2-9ed5178a7e87
This commit is contained in:
Thomas Perl 2007-03-18 18:28:17 +00:00
parent 19e6a670c8
commit 9cfc89d1e4
6 changed files with 242 additions and 108 deletions

View file

@ -1,3 +1,17 @@
Sun, 18 Mar 2007 19:23:46 +0100 <thp@perli.net>
* src/gpodder/libipodsync.py: Add support for selected episodes to be
transferred to the device; get configuration data directly
from gPodderLib()
* src/gpodder/gpodder.py.orig: Updated by tepache
* src/gpodder/gpodder.py: Add support for "transfer" to device of
selected episodes instead of all episodes based on a patch by
Preben Randhol <randhol+gpodder pvv.org>; simplified and cleaned-up
code for device sync and device cleanup to allow for easier addition
of aforementioned patch
* data/gpodder.glade: Support for "transfer" to device button and menu
item based on a patch by Preben Randhol <randhol+gpodder pvv.org>
* bin/gpodder: pushed version + release date
Sun, 18 Mar 2007 17:18:17 +0100 <thp@perli.net>
* Makefile: Bugfix - remove manpage "gpodder.1" in "make uninstall"

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.0+svn20070317"
__date__ = "2007-03-17"
__version__ = "0.9.0+svn20070318"
__date__ = "2007-03-18"
__copyright__ = "Copyright (c) 2005-2007 %s. All rights reserved." % __author__
__licence__ = "GPL"

View file

@ -505,19 +505,19 @@
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator8">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator8">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="itemPlaySelected">
<property name="visible">True</property>
<property name="label" translatable="yes">Play selected episode</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_btnDownloadedExecute_clicked" last_modification_time="Fri, 08 Dec 2006 23:50:54 GMT"/>
<accelerator key="Return" modifiers="GDK_SHIFT_MASK" signal="activate"/>
<signal name="activate" handler="on_btnDownloadedExecute_clicked" last_modification_time="Fri, 08 Dec 2006 23:50:54 GMT"/>
<accelerator key="Return" modifiers="GDK_SHIFT_MASK" signal="activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image2325">
@ -532,6 +532,33 @@
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator9">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="itemTransferSelected">
<property name="visible">True</property>
<property name="label" translatable="yes">Transfer selected episodes</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_btnTransfer_clicked" last_modification_time="Sat, 17 Mar 2007 12:59:55 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image2407">
<property name="visible">True</property>
<property name="stock">gtk-network</property>
<property name="icon_size">1</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
</widget>
@ -846,6 +873,85 @@
</packing>
</child>
<child>
<widget class="GtkButton" id="btnTransfer">
<property name="tooltip" translatable="yes">Transfer selected file(s) to player</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_btnTransfer_clicked" last_modification_time="Sat, 17 Mar 2007 12:19:24 GMT"/>
<child>
<widget class="GtkAlignment" id="alignment15">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox21">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image2333">
<property name="visible">True</property>
<property name="stock">gtk-network</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label64">
<property name="visible">True</property>
<property name="label" translatable="yes">_Transfer</property>
<property name="use_underline">True</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.5</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="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnPlay">
<property name="can_focus">True</property>

View file

@ -206,9 +206,12 @@ class Gpodder(SimpleGladeApp):
if is_download_button:
self.btnPlay.hide_all()
self.btnDownload.show_all()
self.btnTransfer.hide_all()
else:
self.btnPlay.show_all()
self.btnDownload.hide_all()
if gl.device_type != 'none':
self.btnTransfer.show_all()
def updateComboBox( self):
try:
@ -231,6 +234,7 @@ class Gpodder(SimpleGladeApp):
while gtk.events_pending():
gtk.main_iteration( False)
self.treeAvailable.scroll_to_point( rect.x, rect.y)
self.play_or_download()
else:
if self.treeAvailable.get_model():
self.treeAvailable.get_model().clear()
@ -332,50 +336,27 @@ class Gpodder(SimpleGladeApp):
message = _('gPodder currently only supports URLs starting with <b>http://</b>, <b>feed://</b> or <b>ftp://</b>.')
self.show_message( message, title)
def sync_to_ipod_proc( self, sync_win):
gpl = gPodderLib()
gpl.loadConfig()
sync = gPodder_iPodSync( ipod_mount = gpl.ipod_mount, callback_status = sync_win.set_status, callback_progress = sync_win.set_progress, callback_done = sync_win.close)
def sync_to_ipod_proc( self, sync, episodes = None):
if not sync.open():
title = _('Cannot access iPod')
message = _('Make sure your iPod is connected to your computer and mounted. Please also make sure you have set the correct path to your iPod in the preferences dialog.')
title = _('Cannot access device')
message = _('Make sure your device is connected to your computer and mounted. Please also make sure you have set the correct path to your device in the preferences dialog.')
gobject.idle_add( self.show_message, message, title)
sync.close()
return False
for channel in self.ldb.channel_list:
channel.set_metadata_from_localdb()
sync.sync_channel( channel)
if episodes == None:
for channel in self.ldb.channel_list:
channel.set_metadata_from_localdb()
sync.sync_channel( channel)
else:
sync.sync_channel( self.active_channel, episodes)
sync.close()
def sync_to_fs_proc( self, sync_win):
gpl = gPodderLib()
gpl.loadConfig()
if not gpl.can_write_directory( gpl.mp3_player_folder):
title = _('Cannot write to MP3 player')
message = _('Make sure your MP3 player is connected to your computer and mounted and that you have the correct permissions.')
gobject.idle_add( self.show_message, message, title)
if sync_win.close:
sync_win.close()
return False
sync = gPodder_FSSync( destination = gpl.mp3_player_folder, callback_status = sync_win.set_status, callback_progress = sync_win.set_progress, callback_done = sync_win.close)
for channel in self.ldb.channel_list:
channel.set_metadata_from_localdb()
sync.sync_channel( channel)
sync.close()
def ipod_cleanup_proc( self, sync_win):
gpl = gPodderLib()
gpl.loadConfig()
sync = gPodder_iPodSync( ipod_mount = gpl.ipod_mount, callback_status = sync_win.set_status, callback_progress = sync_win.set_progress, callback_done = sync_win.close)
def ipod_cleanup_proc( self, sync):
if not sync.open():
title = _('Cannot access iPod')
message = _('Make sure your iPod is connected to your computer and mounted. Please also make sure you have set the correct path to your iPod in the preferences dialog.')
title = _('Cannot access device')
message = _('Make sure your device is connected to your computer and mounted. Please also make sure you have set the correct path to your device in the preferences dialog.')
gobject.idle_add( self.show_message, message, title)
sync.close()
return False
@ -383,23 +364,6 @@ class Gpodder(SimpleGladeApp):
sync.clean_playlist()
sync.close()
def fs_cleanup_proc( self, sync_win):
gpl = gPodderLib()
gpl.loadConfig()
if not gpl.can_write_directory( gpl.mp3_player_folder):
title = _('Cannot write to MP3 player')
message = _('Make sure your MP3 player is connected to your computer and mounted and that you have the correct permissions.')
gobject.idle_add( self.show_message, message, title)
if sync_win.close:
sync_win.close()
return False
sync = gPodder_FSSync( destination = gpl.mp3_player_folder, callback_status = sync_win.set_status, callback_progress = sync_win.set_progress, callback_done = sync_win.close)
sync.clean_playlist()
sync.close()
def update_feed_cache_callback( self, label, progressbar, position, count):
title = _('Please wait...')
@ -603,53 +567,69 @@ class Gpodder(SimpleGladeApp):
title = _('No device configured')
message = _('To use the synchronization feature, please configure your device in the preferences dialog first.')
self.show_message( message, title)
elif gl.device_type == 'ipod':
if not ipod_supported():
title = _('Libraries needed: gpod, pymad')
message = _('To use the iPod synchronization feature, you need to install the <b>python-gpod</b> and <b>python-pymad</b> libraries from your distribution vendor. More information about the needed libraries can be found on the gPodder website.')
self.show_message( message, title)
return
if gl.device_type == 'ipod' and not ipod_supported():
title = _('Libraries needed: gpod, pymad')
message = _('To use the iPod synchronization feature, you need to install the <b>python-gpod</b> and <b>python-pymad</b> libraries from your distribution vendor. More information about the needed libraries can be found on the gPodder website.')
self.show_message( message, title)
return
if gl.device_type in [ 'ipod', 'filesystem' ]:
sync_class = None
if gl.device_type == 'filesystem':
sync_class = gPodder_FSSync
elif gl.device_type == 'ipod':
sync_class = gPodder_iPodSync
if not sync_class:
return
sync_win = Gpoddersync( gpodderwindow = self.gPodder)
while gtk.events_pending():
gtk.main_iteration( False)
args = ( sync_win, )
thread = Thread( target = self.sync_to_ipod_proc, args = args)
thread.start()
elif gl.device_type == 'filesystem':
sync_win = Gpoddersync( gpodderwindow = self.gPodder)
while gtk.events_pending():
gtk.main_iteration( False)
args = ( sync_win, )
thread = Thread( target = self.sync_to_fs_proc, args = args)
sync = sync_class( callback_status = sync_win.set_status, callback_progress = sync_win.set_progress, callback_done = sync_win.close)
thread_args = [ sync ]
if widget == None:
thread_args.append( args[0])
thread = Thread( target = self.sync_to_ipod_proc, args = thread_args)
thread.start()
#-- Gpodder.on_sync_to_ipod_activate }
#-- Gpodder.on_cleanup_ipod_activate {
def on_cleanup_ipod_activate(self, widget, *args):
gl = gPodderLib()
target_function = None
if gl.device_type == 'none':
title = _('No device configured')
message = _('To use the synchronization feature, please configure your device in the preferences dialog first.')
self.show_message( message, title)
elif gl.device_type == 'ipod':
title = _('Delete podcasts on iPod?')
message = _('Do you really want to completely remove all episodes in the <b>Podcasts</b> playlist on your iPod?')
if self.show_confirmation( message, title):
target_function = self.ipod_cleanup_proc
elif gl.device_type == 'filesystem':
title = _('Delete podcasts from MP3 player?')
message = _('Do you really want to completely remove all episodes from your MP3 player?')
if self.show_confirmation( message, title):
target_function = self.fs_cleanup_proc
return
if gl.device_type == 'ipod' and not ipod_supported():
title = _('Libraries needed: gpod, pymad')
message = _('To use the iPod synchronization feature, you need to install the <b>python-gpod</b> and <b>python-pymad</b> libraries from your distribution vendor. More information about the needed libraries can be found on the gPodder website.')
self.show_message( message, title)
return
if gl.device_type in [ 'ipod', 'filesystem' ]:
sync_class = None
if gl.device_type == 'filesystem':
title = _('Delete podcasts from MP3 player?')
message = _('Do you really want to completely remove all episodes from your MP3 player?')
if self.show_confirmation( message, title):
sync_class = gPodder_FSSync
elif gl.device_type == 'ipod':
title = _('Delete podcasts on iPod?')
message = _('Do you really want to completely remove all episodes in the <b>Podcasts</b> playlist on your iPod?')
if self.show_confirmation( message, title):
sync_class = gPodder_iPodSync
if not sync_class:
return
if target_function:
sync_win = Gpoddersync( gpodderwindow = self.gPodder)
while gtk.events_pending():
gtk.main_iteration( False)
args = ( sync_win, )
thread = Thread( target = target_function, args = args)
sync = sync_class( callback_status = sync_win.set_status, callback_progress = sync_win.set_progress, callback_done = sync_win.close)
thread = Thread( target = self.ipod_cleanup_proc, args = ( sync, ))
thread.start()
#-- Gpodder.on_cleanup_ipod_activate }
@ -730,6 +710,11 @@ class Gpodder(SimpleGladeApp):
Gpodderopmllister( gpodderwindow = self.gPodder).get_channels_from_url( gPodderLib().opml_url, self.add_new_channel)
#-- Gpodder.on_itemImportChannels_activate }
#-- Gpodder.on_btnTransfer_clicked {
def on_btnTransfer_clicked(self, widget, *args):
self.on_treeAvailable_row_activated( widget, args)
#-- Gpodder.on_btnTransfer_clicked }
#-- Gpodder.on_homepage_activate {
def on_homepage_activate(self, widget, *args):
os.system( 'gnome-open ' + app_website)
@ -801,6 +786,8 @@ class Gpodder(SimpleGladeApp):
try:
selection = self.treeAvailable.get_selection()
selection_tuple = selection.get_selected_rows()
transfer_files = False
episodes = []
if selection.count_selected_rows() > 1:
widget_to_send = None
@ -809,10 +796,20 @@ class Gpodder(SimpleGladeApp):
widget_to_send = widget
show_message_dialog = True
if widget.get_name() == 'itemTransferSelected' or widget.get_name() == 'btnTransfer':
transfer_files = True
for apath in selection_tuple[1]:
selection_iter = self.treeAvailable.get_model().get_iter( apath)
url = self.treeAvailable.get_model().get_value( selection_iter, 0)
self.download_podcast_by_url( url, show_message_dialog, widget_to_send)
if transfer_files:
episodes.append( self.active_channel.find_episode( url))
else:
self.download_podcast_by_url( url, show_message_dialog, widget_to_send)
if transfer_files and len(episodes):
self.on_sync_to_ipod_activate( None, episodes)
except:
title = _('Nothing selected')
message = _('Please select an episode that you want to download and then click on the download button to start downloading the selected episode.')

View file

@ -125,6 +125,11 @@ class Gpodder(SimpleGladeApp):
print "on_btnDownloadedExecute_clicked called with self.%s" % widget.get_name()
#-- Gpodder.on_btnDownloadedExecute_clicked }
#-- Gpodder.on_btnTransfer_clicked {
def on_btnTransfer_clicked(self, widget, *args):
print "on_btnTransfer_clicked called with self.%s" % widget.get_name()
#-- Gpodder.on_btnTransfer_clicked }
#-- Gpodder.on_homepage_activate {
def on_homepage_activate(self, widget, *args):
print "on_homepage_activate called with self.%s" % widget.get_name()

View file

@ -91,6 +91,7 @@ import email.Utils
import liblocaldb
import libpodcasts
import libgpodder
import gobject
@ -115,6 +116,9 @@ class gPodderSyncMethod:
self.callback_progress = callback_progress
self.callback_status = callback_status
self.callback_done = callback_done
def open( self):
return False
def set_progress( self, pos, max):
if self.callback_progress:
@ -128,14 +132,17 @@ class gPodderSyncMethod:
if self.callback_done:
gobject.idle_add( self.callback_done)
def sync_channel( self, channel):
if not channel.sync_to_devices:
def sync_channel( self, channel, episodes = None):
if not channel.sync_to_devices and episodes == None:
return False
max = len( channel)
if episodes == None:
episodes = channel
max = len( episodes)
pos = 1
for episode in channel:
for episode in episodes:
self.set_progress( pos, max)
if channel.is_downloaded( episode):
self.add_episode_from_channel( channel, episode)
@ -165,10 +172,11 @@ class gPodder_iPodSync( gPodderSyncMethod):
pl_master = None
pl_podcasts = None
def __init__( self, ipod_mount = '/media/ipod/', callback_progress = None, callback_status = None, callback_done = None):
def __init__( self, callback_progress = None, callback_status = None, callback_done = None):
if not ipod_supported():
log( '(ipodsync) iPod functions not supported. (libgpod + eyed3 needed)')
self.ipod_mount = ipod_mount
gl = libgpodder.gPodderLib()
self.ipod_mount = gl.ipod_mount
gPodderSyncMethod.__init__( self, callback_progress, callback_status, callback_done)
def open( self):
@ -187,14 +195,13 @@ class gPodder_iPodSync( gPodderSyncMethod):
return False
return True
def close( self, write_update = True):
def close( self):
if not ipod_supported():
return False
if write_update:
if self.itdb:
self.set_progress( 100, 100)
self.set_status( '...', '...', _('Saving iPod database...'))
if self.itdb:
gpod.itdb_write( self.itdb, None)
gpod.itdb_write( self.itdb, None)
self.itdb = None
time.sleep( 1)
gPodderSyncMethod.close( self)
@ -401,9 +408,14 @@ class gPodder_iPodSync( gPodderSyncMethod):
class gPodder_FSSync( gPodderSyncMethod):
def __init__( self, destination = '/tmp/', callback_progress = None, callback_status = None, callback_done = None):
self.destination = destination
def __init__( self, callback_progress = None, callback_status = None, callback_done = None):
gl = libgpodder.gPodderLib()
self.destination = gl.mp3_player_folder
gPodderSyncMethod.__init__( self, callback_progress, callback_status, callback_done)
def open( self):
gpl = libgpodder.gPodderLib()
return gpl.can_write_directory( self.destination)
def add_episode_from_channel( self, channel, episode):
replace_chars = ( '/', '?', ':', '!', '<', '>', '&', '*', '|')
@ -440,7 +452,7 @@ class gPodder_FSSync( gPodderSyncMethod):
folders = glob.glob( os.path.join( self.destination, '*'))
for folder in range( len( folders)):
self.set_progress( folder+1, len( folders))
self.set_status( channel = os.path.basename( folders[folder]), progressbar_text = _('%d of %d') % ( folder+1, len(folders), ))
self.set_status( episode = _('All files'), channel = os.path.basename( folders[folder]), progressbar_text = _('%d of %d') % ( folder+1, len(folders), ))
log( 'deleting: %s', folders[folder])
shutil.rmtree( folders[folder])
try: