Win32-specific changes and bugfixes (bug 247, 442)

This patch merges all the win32-specific changes that
have been done to gPodder so far and also adds some
generic bugfixes that will benefit other versions of
gPodder, too.

Thanks to Stefan Koegl and David Greenbaum for testing.
This commit is contained in:
Thomas Perl 2009-05-09 15:21:04 +02:00
parent 952ea28548
commit de3750e190
7 changed files with 135 additions and 23 deletions

View File

@ -25,6 +25,7 @@ __licence__ = 'GNU General Public License, version 3 or later'
__url__ = 'http://gpodder.org/'
import sys
import platform
import gettext
# Check if real hard dependencies are available
@ -66,3 +67,6 @@ del t
ui_folder = None
icon_file = None
# Set "win32" to True if we are on Windows
win32 = (platform.system() == 'Windows')

View File

@ -42,7 +42,7 @@ _ = gpodder.gettext
if gpodder.interface == gpodder.MAEMO:
default_download_dir = '/media/mmc2/gpodder'
else:
default_download_dir = os.path.expanduser('~/gpodder-downloads')
default_download_dir = os.path.join(os.path.expanduser('~'), 'gpodder-downloads')
gPodderSettings = {
# General settings

View File

@ -30,10 +30,6 @@ import time
import urllib
import urllib2
import datetime
import dbus
import dbus.service
import dbus.mainloop
import dbus.glib
from xml.sax import saxutils
@ -43,6 +39,33 @@ from threading import Semaphore
from string import strip
import gpodder
if gpodder.win32:
# Mock the required D-Bus interfaces with no-ops
class dbus:
class SessionBus:
def __init__(self, *args, **kwargs):
pass
class glib:
class DBusGMainLoop:
pass
class service:
@staticmethod
def method(interface):
return lambda x: x
class BusName:
def __init__(self, *args, **kwargs):
pass
class Object:
def __init__(self, *args, **kwargs):
pass
else:
import dbus
import dbus.service
import dbus.mainloop
import dbus.glib
from gpodder import libtagupdate
from gpodder import util
from gpodder import opml
@ -436,6 +459,10 @@ class gPodder(BuilderWidget, dbus.service.Object):
self.gPodder.connect('key-press-event', self.on_key_press)
self.treeChannels.connect('size-allocate', self.on_tree_channels_resize)
if gpodder.win32:
# FIXME: Implement e-mail sending of list in win32
self.item_email_subscriptions.set_sensitive(False)
if gl.config.show_url_entry_in_podcast_list:
self.hboxAddChannel.show()
@ -2207,6 +2234,9 @@ class gPodder(BuilderWidget, dbus.service.Object):
self.gPodder.hide()
if self.tray_icon is not None:
self.tray_icon.set_visible(False)
# Notify all tasks to to carry out any clean-up actions
self.download_status_manager.tell_all_tasks_to_quit()
@ -4130,7 +4160,7 @@ class gPodderOpmlLister(BuilderWidget):
self.notification(_('There are no YouTube channels that would match this query.'), _('No channels found'))
else:
url = self.entryURL.get_text()
if not url.lower().startswith('http://'):
if not os.path.isfile(url) and not url.lower().startswith('http://'):
log('Using podcast.de search')
url = 'http://api.podcast.de/opml/podcasts/suche/%s' % (urllib.quote(url),)
model = opml.Importer(url).get_model()

66
src/gpodder/launcher.py Normal file
View File

@ -0,0 +1,66 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# gPodder - A media aggregator and podcast client
# Copyright (c) 2005-2009 Thomas Perl and the gPodder Team
#
# 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/>.
#
"""Win32 Launcher script for gPodder
This is only used for the Win32 version of gPodder
and will set up the environment to find all files
and then start up the gPodder GUI.
Thomas Perl <thpinfo.com>; 2009-05-09
"""
import sys
import os
import os.path
import platform
import gettext
if __name__ == '__main__':
prefix = os.path.abspath(os.path.normpath('.'))
data_dir = os.path.join(prefix, 'data')
locale_dir = os.path.join(data_dir, 'locale')
ui_folder = os.path.join(data_dir, 'ui')
icon_file = os.path.join(data_dir, 'gpodder.svg')
# Set up the path to translation files
gettext.bindtextdomain('gpodder', locale_dir)
import gpodder
if not gpodder.win32:
print >>sys.stderr, 'This launcher is only for Win32.'
sys.exit(1)
# Enable i18n for gPodder translations
_ = gpodder.gettext
# Set up paths to folder with GtkBuilder files and gpodder.svg
gpodder.ui_folder = ui_folder
gpodder.icon_file = icon_file
gpodder.interface = gpodder.GUI
from gpodder import gui
gui.main()

View File

@ -66,12 +66,14 @@ _ = gpodder.gettext
if gpodder.interface == gpodder.MAEMO:
ICON_AUDIO_FILE = 'gnome-mime-audio-mp3'
ICON_VIDEO_FILE = 'gnome-mime-video-mp4'
ICON_GENERIC_FILE = 'text-x-generic'
ICON_DOWNLOADING = 'qgn_toolb_messagin_moveto'
ICON_DELETED = 'qgn_toolb_gene_deletebutton'
ICON_NEW = 'qgn_list_gene_favor'
else:
ICON_AUDIO_FILE = 'audio-x-generic'
ICON_VIDEO_FILE = 'video-x-generic'
ICON_GENERIC_FILE = 'text-x-generic'
ICON_DOWNLOADING = gtk.STOCK_GO_DOWN
ICON_DELETED = gtk.STOCK_DELETE
ICON_NEW = gtk.STOCK_ABOUT
@ -431,7 +433,7 @@ class PodcastChannel(PodcastModelObject):
return db.load_episodes(self, factory=self.episode_factory)
def iter_set_downloading_columns(self, model, iter, episode=None, downloading=None):
global ICON_AUDIO_FILE, ICON_VIDEO_FILE
global ICON_AUDIO_FILE, ICON_VIDEO_FILE, ICON_GENERIC_FILE
global ICON_DOWNLOADING, ICON_DELETED, ICON_NEW
if episode is None:
@ -465,7 +467,7 @@ class PodcastChannel(PodcastModelObject):
elif file_type == 'video':
status_icon = util.get_tree_icon(ICON_VIDEO_FILE, not episode.is_played, episode.is_locked, not episode.file_exists(), self.icon_cache, icon_size)
else:
status_icon = util.get_tree_icon('unknown', not episode.is_played, episode.is_locked, not episode.file_exists(), self.icon_cache, icon_size)
status_icon = util.get_tree_icon(ICON_GENERIC_FILE, not episode.is_played, episode.is_locked, not episode.file_exists(), self.icon_cache, icon_size)
elif episode.state == db.STATE_DELETED or episode.state == db.STATE_DOWNLOADED:
status_icon = util.get_tree_icon(ICON_DELETED, not episode.is_played, icon_cache=self.icon_cache, icon_size=icon_size)
else:

View File

@ -48,6 +48,8 @@ import urllib
import urllib2
import os.path
import os
import platform
import shutil
from email.Utils import formatdate
import gpodder
@ -76,15 +78,10 @@ class Importer(object):
"""
self.items = []
try:
if url.startswith('/'):
# assume local filename
if os.path.exists(url):
doc = xml.dom.minidom.parse( url)
else:
log('Empty/non-existing OPML file', sender=self)
return
if os.path.exists(url):
doc = xml.dom.minidom.parse(url)
else:
doc = xml.dom.minidom.parseString( self.read_url( url))
doc = xml.dom.minidom.parseString(self.read_url(url))
for outline in doc.getElementsByTagName('outline'):
if outline.getAttribute('type') in self.VALID_TYPES and outline.getAttribute('xmlUrl') or outline.getAttribute('url'):
@ -206,15 +203,21 @@ class Exporter(object):
# try to save the new file, but keep the old one so we
# don't end up with a clobbed, empty opml file.
FREE_DISK_SPACE_AFTER = 1024*512
if util.get_free_disk_space(os.path.dirname(self.filename)) < 2*len(data)+FREE_DISK_SPACE_AFTER:
available = util.get_free_disk_space(os.path.dirname(self.filename))
if available < 2*len(data)+FREE_DISK_SPACE_AFTER and not gpodder.win32:
# FIXME: get_free_disk_space still unimplemented for win32
log('Not enough free disk space to save channel list to %s', self.filename, sender = self)
return False
fp = open(self.filename+'.tmp', 'w')
fp.write(data)
fp.close()
os.rename(self.filename+'.tmp', self.filename)
if gpodder.win32:
# Win32 does not support atomic rename with os.rename
shutil.move(self.filename+'.tmp', self.filename)
else:
os.rename(self.filename+'.tmp', self.filename)
except:
log( 'Could not open file for writing: %s', self.filename, sender = self)
log('Could not open file for writing: %s', self.filename, sender=self, traceback=True)
return False
return True

View File

@ -37,6 +37,7 @@ import gobject
import os
import os.path
import platform
import glob
import stat
@ -321,6 +322,10 @@ def get_free_disk_space(path):
function returns zero.
"""
if gpodder.win32:
# FIXME: Implement for win32
return 0
if not os.path.exists(path):
return 0
@ -1018,12 +1023,14 @@ def get_episode_info_from_url(url, proxy=None):
def gui_open(filename):
"""
Open a file or folder with the default application set
by the Desktop environment. This uses "xdg-open".
by the Desktop environment. This uses "xdg-open" on all
systems except Win32, which uses os.startfile().
"""
try:
subprocess.Popen(['xdg-open', filename])
# FIXME: Win32-specific "open" code needed here
# as fallback when xdg-open not available
if gpodder.win32:
os.startfile(filename)
else:
subprocess.Popen(['xdg-open', filename])
return True
except:
log('Cannot open file/folder: "%s"', filename, traceback=True)