2005-11-21 19:21:25 +01:00
|
|
|
|
|
|
|
#
|
|
|
|
# gPodder
|
|
|
|
# Copyright (c) 2005 Thomas Perl <thp@perli.net>
|
|
|
|
# Released under the GNU General Public License (GPL)
|
|
|
|
#
|
|
|
|
|
|
|
|
#
|
|
|
|
# libgpodder.py -- gpodder configuration
|
|
|
|
# thomas perl <thp@perli.net> 20051030
|
|
|
|
#
|
|
|
|
#
|
|
|
|
|
|
|
|
import gtk
|
2006-03-19 15:21:48 +01:00
|
|
|
import thread
|
2006-03-24 20:08:59 +01:00
|
|
|
import threading
|
2006-03-31 18:20:18 +02:00
|
|
|
import urllib
|
2005-11-21 19:21:25 +01:00
|
|
|
|
|
|
|
from xml.sax.saxutils import DefaultHandler
|
|
|
|
from xml.sax import make_parser
|
|
|
|
from string import strip
|
|
|
|
from os.path import expanduser
|
|
|
|
from os.path import exists
|
|
|
|
from os.path import dirname
|
|
|
|
from os import mkdir
|
2006-01-09 00:52:40 +01:00
|
|
|
from os import environ
|
2006-02-04 18:29:17 +01:00
|
|
|
from os import system
|
2006-03-24 20:08:59 +01:00
|
|
|
from os import unlink
|
2005-11-21 19:21:25 +01:00
|
|
|
|
2006-03-29 15:24:44 +02:00
|
|
|
# for the desktop symlink stuff:
|
|
|
|
from os import symlink
|
|
|
|
from os import stat
|
|
|
|
from stat import S_ISLNK
|
|
|
|
from stat import ST_MODE
|
|
|
|
|
2005-11-21 19:21:25 +01:00
|
|
|
from librssreader import rssReader
|
2006-03-03 21:04:25 +01:00
|
|
|
from libpodcasts import podcastChannel
|
2005-11-21 19:21:25 +01:00
|
|
|
|
2006-03-31 18:20:18 +02:00
|
|
|
from gtk.gdk import PixbufLoader
|
|
|
|
|
2006-04-05 21:07:05 +02:00
|
|
|
from ConfigParser import ConfigParser
|
|
|
|
|
2005-11-21 22:17:54 +01:00
|
|
|
# global debugging variable, set to False on release
|
2006-02-04 18:29:17 +01:00
|
|
|
# TODO: while developing a new version, set this to "True"
|
2006-03-30 00:07:27 +02:00
|
|
|
debugging = True
|
2005-11-21 22:17:54 +01:00
|
|
|
|
2006-03-24 20:08:59 +01:00
|
|
|
# global recursive lock for thread exclusion
|
|
|
|
globalLock = threading.RLock()
|
2006-03-19 15:21:48 +01:00
|
|
|
|
2006-04-05 21:07:05 +02:00
|
|
|
# my gpodderlib variable
|
|
|
|
g_podder_lib = None
|
|
|
|
|
2005-11-21 22:17:54 +01:00
|
|
|
def isDebugging():
|
|
|
|
return debugging
|
|
|
|
|
2006-03-19 15:21:48 +01:00
|
|
|
def getLock():
|
|
|
|
globalLock.acquire()
|
|
|
|
|
|
|
|
def releaseLock():
|
|
|
|
globalLock.release()
|
|
|
|
|
2006-04-05 21:07:05 +02:00
|
|
|
# some awkward kind of "singleton" ;)
|
|
|
|
def gPodderLib():
|
|
|
|
global g_podder_lib
|
|
|
|
if g_podder_lib == None:
|
|
|
|
g_podder_lib = gPodderLibClass()
|
|
|
|
return g_podder_lib
|
|
|
|
|
|
|
|
class gPodderLibClass( object):
|
2005-11-21 19:21:25 +01:00
|
|
|
gpodderdir = ""
|
|
|
|
downloaddir = ""
|
|
|
|
cachedir = ""
|
2006-01-09 00:52:40 +01:00
|
|
|
http_proxy = ""
|
|
|
|
ftp_proxy = ""
|
2006-04-05 21:07:05 +02:00
|
|
|
proxy_use_environment = False
|
2006-02-04 18:29:17 +01:00
|
|
|
open_app = ""
|
2006-03-31 18:20:18 +02:00
|
|
|
desktop_link = _("gPodder downloads")
|
2006-04-05 21:07:05 +02:00
|
|
|
gpodderconf_section = 'gpodder-conf-1'
|
2005-11-21 19:21:25 +01:00
|
|
|
|
|
|
|
def __init__( self):
|
|
|
|
self.gpodderdir = expanduser( "~/.config/gpodder/")
|
|
|
|
self.createIfNecessary( self.gpodderdir)
|
|
|
|
self.downloaddir = self.gpodderdir + "downloads/"
|
|
|
|
self.createIfNecessary( self.downloaddir)
|
|
|
|
self.cachedir = self.gpodderdir + "cache/"
|
|
|
|
self.createIfNecessary( self.cachedir)
|
2006-01-09 00:52:40 +01:00
|
|
|
try:
|
|
|
|
self.http_proxy = environ['http_proxy']
|
|
|
|
except:
|
|
|
|
self.http_proxy = ''
|
|
|
|
try:
|
|
|
|
self.ftp_proxy = environ['ftp_proxy']
|
|
|
|
except:
|
|
|
|
self.ftp_proxy = ''
|
|
|
|
self.loadConfig()
|
2005-11-21 19:21:25 +01:00
|
|
|
|
|
|
|
def createIfNecessary( self, path):
|
|
|
|
#TODO: recursive mkdir all parent directories
|
|
|
|
|
|
|
|
if path.endswith('/'):
|
|
|
|
path = path[:-1]
|
|
|
|
|
|
|
|
if not exists(dirname(path)):
|
|
|
|
mkdir(dirname(path))
|
|
|
|
|
|
|
|
if not exists( path):
|
|
|
|
mkdir( path)
|
|
|
|
|
|
|
|
def getConfigFilename( self):
|
2006-01-09 00:52:40 +01:00
|
|
|
return self.gpodderdir + "gpodder.conf"
|
2006-04-05 21:07:05 +02:00
|
|
|
|
2005-11-21 19:21:25 +01:00
|
|
|
def getChannelsFilename( self):
|
|
|
|
return self.gpodderdir + "channels.xml"
|
2006-02-04 18:29:17 +01:00
|
|
|
|
2006-01-09 00:52:40 +01:00
|
|
|
def propertiesChanged( self):
|
2006-04-05 21:07:05 +02:00
|
|
|
# set new environment variables for subprocesses to use,
|
|
|
|
# but only if we are not told to passthru the env vars
|
|
|
|
if not self.proxy_use_environment:
|
|
|
|
environ['http_proxy'] = self.http_proxy
|
|
|
|
environ['ftp_proxy'] = self.ftp_proxy
|
2006-01-09 00:52:40 +01:00
|
|
|
# save settings for next startup
|
|
|
|
self.saveConfig()
|
|
|
|
|
|
|
|
def saveConfig( self):
|
2006-04-05 21:07:05 +02:00
|
|
|
parser = ConfigParser()
|
|
|
|
self.write_to_parser( parser, 'http_proxy', self.http_proxy)
|
|
|
|
self.write_to_parser( parser, 'ftp_proxy', self.ftp_proxy)
|
|
|
|
self.write_to_parser( parser, 'player', self.open_app)
|
|
|
|
self.write_to_parser( parser, 'proxy_use_env', self.proxy_use_environment)
|
2006-01-09 00:52:40 +01:00
|
|
|
fn = self.getConfigFilename()
|
|
|
|
fp = open( fn, "w")
|
2006-04-05 21:07:05 +02:00
|
|
|
parser.write( fp)
|
2006-01-09 00:52:40 +01:00
|
|
|
fp.close()
|
2006-04-05 21:07:05 +02:00
|
|
|
|
|
|
|
def get_from_parser( self, parser, option, default = ''):
|
|
|
|
try:
|
|
|
|
result = parser.get( self.gpodderconf_section, option)
|
|
|
|
if isDebugging():
|
|
|
|
print "get_from_parser( %s) = %s" % ( option, result )
|
|
|
|
return result
|
|
|
|
except:
|
|
|
|
return default
|
|
|
|
|
|
|
|
def get_boolean_from_parser( self, parser, option, default = False):
|
|
|
|
try:
|
|
|
|
result = parser.getboolean( self.gpodderconf_section, option)
|
|
|
|
return result
|
|
|
|
except:
|
|
|
|
return default
|
|
|
|
|
|
|
|
def write_to_parser( self, parser, option, value = ''):
|
|
|
|
if not parser.has_section( self.gpodderconf_section):
|
|
|
|
parser.add_section( self.gpodderconf_section)
|
|
|
|
try:
|
|
|
|
parser.set( self.gpodderconf_section, option, str(value))
|
|
|
|
except:
|
|
|
|
if isDebugging():
|
|
|
|
print 'write_to_parser: could not write config (option=%s, value=%s' % (option, value)
|
2006-01-09 00:52:40 +01:00
|
|
|
|
|
|
|
def loadConfig( self):
|
2006-04-05 21:07:05 +02:00
|
|
|
was_oldstyle = False
|
2006-01-09 00:52:40 +01:00
|
|
|
try:
|
|
|
|
fn = self.getConfigFilename()
|
2006-04-05 21:07:05 +02:00
|
|
|
if open(fn,'r').read(1) != '[':
|
|
|
|
if isDebugging():
|
|
|
|
print 'seems like old-style config. trying to read it anyways..'
|
|
|
|
fp = open( fn, 'r')
|
|
|
|
http = fp.readline()
|
|
|
|
ftp = fp.readline()
|
|
|
|
app = fp.readline()
|
|
|
|
fp.close()
|
|
|
|
was_oldstyle = True
|
|
|
|
else:
|
|
|
|
parser = ConfigParser()
|
|
|
|
parser.read( fn)
|
|
|
|
if parser.has_section( self.gpodderconf_section):
|
|
|
|
http = self.get_from_parser( parser, 'http_proxy')
|
|
|
|
ftp = self.get_from_parser( parser, 'ftp_proxy')
|
|
|
|
app = self.get_from_parser( parser, 'player', 'gnome-open')
|
2006-04-05 21:10:35 +02:00
|
|
|
self.proxy_use_environment = self.get_boolean_from_parser( parser, 'proxy_use_env', True)
|
2006-04-05 21:07:05 +02:00
|
|
|
else:
|
|
|
|
if isDebugging():
|
|
|
|
print "config file %s has no section %s" % (fn, gpodderconf_section)
|
|
|
|
if not self.proxy_use_environment:
|
2006-01-09 00:52:40 +01:00
|
|
|
self.http_proxy = strip( http)
|
|
|
|
self.ftp_proxy = strip( ftp)
|
2006-04-05 21:07:05 +02:00
|
|
|
if strip( app) != '':
|
2006-02-04 18:29:17 +01:00
|
|
|
self.open_app = strip( app)
|
|
|
|
else:
|
2006-04-05 21:07:05 +02:00
|
|
|
self.open_app = 'gnome-open'
|
2006-01-09 00:52:40 +01:00
|
|
|
except:
|
2006-02-04 18:29:17 +01:00
|
|
|
# TODO: well, well.. (http + ftp?)
|
2006-04-05 21:07:05 +02:00
|
|
|
self.open_app = 'gnome-open'
|
|
|
|
if was_oldstyle:
|
|
|
|
self.saveConfig()
|
2006-02-04 18:29:17 +01:00
|
|
|
|
|
|
|
def openFilename( self, filename):
|
|
|
|
if isDebugging():
|
|
|
|
print "open " + filename + " with " + self.open_app
|
|
|
|
system( self.open_app + " " + filename + " &")
|
2006-03-03 21:04:25 +01:00
|
|
|
|
2006-03-29 15:24:44 +02:00
|
|
|
def getDesktopSymlink( self):
|
2006-03-31 18:20:18 +02:00
|
|
|
symlink_path = expanduser( "~/Desktop/%s" % self.desktop_link)
|
2006-03-29 15:24:44 +02:00
|
|
|
return exists( symlink_path)
|
|
|
|
|
|
|
|
def createDesktopSymlink( self):
|
|
|
|
if isDebugging():
|
|
|
|
print "createDesktopSymlink requested"
|
|
|
|
if not self.getDesktopSymlink():
|
|
|
|
downloads_path = expanduser( "~/Desktop/")
|
|
|
|
self.createIfNecessary( downloads_path)
|
2006-03-31 18:20:18 +02:00
|
|
|
symlink( self.downloaddir, "%s%s" % (downloads_path, self.desktop_link))
|
2006-03-29 15:24:44 +02:00
|
|
|
|
|
|
|
def removeDesktopSymlink( self):
|
|
|
|
if isDebugging():
|
|
|
|
print "removeDesktopSymlink requested"
|
|
|
|
if self.getDesktopSymlink():
|
2006-03-31 18:20:18 +02:00
|
|
|
unlink( expanduser( "~/Desktop/%s" % self.desktop_link))
|
|
|
|
|
|
|
|
def image_download_thread( self, url, callback_pixbuf = None, callback_status = None, callback_finished = None, cover_file = None):
|
|
|
|
if callback_status != None:
|
|
|
|
callback_status( _('Downloading channel cover...'))
|
|
|
|
pixbuf = PixbufLoader()
|
|
|
|
|
|
|
|
if cover_file == None:
|
|
|
|
if isDebugging():
|
|
|
|
print "directly downloading %s" % url
|
|
|
|
pixbuf.write( urllib.urlopen(url).read())
|
|
|
|
|
|
|
|
if cover_file != None and not exists( cover_file):
|
|
|
|
if isDebugging():
|
|
|
|
print "downloading cover to %s" % cover_file
|
|
|
|
cachefile = open( cover_file, "w")
|
|
|
|
cachefile.write( urllib.urlopen(url).read())
|
|
|
|
cachefile.close()
|
|
|
|
|
|
|
|
if cover_file != None:
|
|
|
|
if isDebugging():
|
|
|
|
print "reading cover from %s" % cover_file
|
|
|
|
pixbuf.write( open( cover_file, "r").read())
|
|
|
|
|
|
|
|
try:
|
|
|
|
pixbuf.close()
|
|
|
|
except:
|
|
|
|
# data error, delete temp file
|
|
|
|
self.deleteFilename( cover_file)
|
|
|
|
|
|
|
|
if callback_pixbuf != None:
|
|
|
|
callback_pixbuf( pixbuf.get_pixbuf())
|
|
|
|
if callback_status != None:
|
|
|
|
callback_status( '')
|
|
|
|
if callback_finished != None:
|
|
|
|
callback_finished()
|
|
|
|
|
|
|
|
def get_image_from_url( self, url, callback_pixbuf = None, callback_status = None, callback_finished = None, cover_file = None):
|
|
|
|
args = ( url, callback_pixbuf, callback_status, callback_finished, cover_file )
|
|
|
|
thread = threading.Thread( target = self.image_download_thread, args = args)
|
|
|
|
thread.start()
|
2006-03-29 15:24:44 +02:00
|
|
|
|
2006-03-24 20:08:59 +01:00
|
|
|
def deleteFilename( self, filename):
|
|
|
|
if isDebugging():
|
|
|
|
print "deleteFilename: " + filename
|
2006-03-29 13:51:25 +02:00
|
|
|
try:
|
|
|
|
unlink( filename)
|
|
|
|
except:
|
|
|
|
# silently ignore
|
|
|
|
pass
|
2006-03-24 20:08:59 +01:00
|
|
|
|
2005-11-21 19:21:25 +01:00
|
|
|
class gPodderChannelWriter( object):
|
|
|
|
def write( self, channels):
|
|
|
|
filename = gPodderLib().getChannelsFilename()
|
|
|
|
fd = open( filename, "w")
|
2006-03-31 18:20:18 +02:00
|
|
|
print >> fd, '<!-- '+_('gPodder channel list')+' -->'
|
2006-03-03 21:04:25 +01:00
|
|
|
print >> fd, '<channels>'
|
2005-11-21 19:21:25 +01:00
|
|
|
for chan in channels:
|
2006-03-31 18:20:18 +02:00
|
|
|
print >> fd, ' <channel name="%s">' % chan.filename
|
|
|
|
print >> fd, ' <url>%s</url>' % chan.url
|
|
|
|
print >> fd, ' <download_dir>%s</download_dir>' % chan.save_dir
|
2006-03-03 21:04:25 +01:00
|
|
|
print >> fd, ' </channel>'
|
|
|
|
print >> fd, '</channels>'
|
2005-11-21 19:21:25 +01:00
|
|
|
fd.close()
|
|
|
|
|
|
|
|
class gPodderChannelReader( DefaultHandler):
|
|
|
|
channels = []
|
|
|
|
current_item = None
|
|
|
|
current_element_data = ""
|
|
|
|
|
|
|
|
def __init__( self):
|
|
|
|
None
|
|
|
|
|
|
|
|
def read( self, force_update = False):
|
|
|
|
self.channels = []
|
|
|
|
parser = make_parser()
|
|
|
|
parser.setContentHandler( self)
|
|
|
|
if exists( gPodderLib().getChannelsFilename()):
|
|
|
|
parser.parse( gPodderLib().getChannelsFilename())
|
|
|
|
else:
|
|
|
|
return []
|
|
|
|
reader = rssReader()
|
|
|
|
input_channels = []
|
|
|
|
|
|
|
|
for channel in self.channels:
|
2006-03-03 21:04:25 +01:00
|
|
|
cachefile = channel.downloadRss(force_update)
|
2006-03-29 13:51:25 +02:00
|
|
|
# check if download was a success
|
|
|
|
if cachefile != None:
|
|
|
|
reader.parseXML(channel.url, cachefile)
|
2005-11-21 19:21:25 +01:00
|
|
|
|
2006-03-29 13:51:25 +02:00
|
|
|
if channel.filename != "" and channel.filename != "__unknown__":
|
|
|
|
reader.channel.shortname = channel.filename
|
2005-11-21 19:21:25 +01:00
|
|
|
|
2006-03-29 13:51:25 +02:00
|
|
|
input_channels.append( reader.channel)
|
2005-11-21 19:21:25 +01:00
|
|
|
|
|
|
|
return input_channels
|
|
|
|
|
|
|
|
def startElement( self, name, attrs):
|
|
|
|
self.current_element_data = ""
|
|
|
|
|
|
|
|
if name == "channel":
|
2006-03-03 21:04:25 +01:00
|
|
|
self.current_item = podcastChannel()
|
2005-11-21 19:21:25 +01:00
|
|
|
self.current_item.filename = attrs.get( "name", "")
|
|
|
|
|
|
|
|
def endElement( self, name):
|
|
|
|
if self.current_item != None:
|
|
|
|
if name == "url":
|
|
|
|
self.current_item.url = self.current_element_data
|
2006-03-03 21:04:25 +01:00
|
|
|
if name == "download_dir":
|
|
|
|
self.current_item.download_dir = self.current_element_data
|
2005-11-21 19:21:25 +01:00
|
|
|
if name == "channel":
|
|
|
|
self.channels.append( self.current_item)
|
|
|
|
self.current_item = None
|
|
|
|
|
|
|
|
def characters( self, ch):
|
|
|
|
self.current_element_data = self.current_element_data + ch
|
|
|
|
|
|
|
|
|