#!/usr/bin/env python
-VERSION = "2.0"
+VERSION = "2.1"
-import asyncore, asynchat, socket
-import os, os.path, random, sys, time
+import asyncore
+import asynchat
+import socket
+import os
+import random
+import sys
+import time
from optparse import OptionParser
import logging
-from logging import debug, info, warning, error, critical
-logging.basicConfig(format="%(levelname)s: %(message)s")
+try:
+ logging.basicConfig(format="%(levelname)s: %(message)s")
+except TypeError:
+# Python 2.3's logging.basicConfig does not support parameters
+ logging.basicConfig()
+
try:
import cPickle as pickle
except ImportError:
import asyncsched
import wallchanger
except ImportError, e:
- critical("Missing libraries! Exiting...")
+ logging.critical("Missing libraries! Exiting...", exc_info=1)
sys.exit(1)
class BaseFileList(object):
"""Base file list implementation"""
- def scan_paths(self):
- raise NotImplementedError()
+ def __init__(self):
+ self.paths = []
+ self.favourites = []
def add_path(self, path):
- raise NotImplementedError()
+ self.paths.append(path)
- def store_cache(self, path):
- pass
+ def store_cache(self, filename):
+ try:
+ logging.debug("Attempting to store cache")
+ fd = open(filename, 'wb')
+ pickle.dump(self, fd, 2)
+ logging.debug("Cache successfully stored")
+ except Exception, e:
+ warning("Storing cache: %s" % e)
- def load_cache(self, filename, rescanPaths = False):
- pass
+ def load_cache(self, filename):
+ try:
+ logging.debug("Attempting to load cache from: %s" % filename)
+ self.paths.sort()
+
+ fd = open(filename, 'rb')
+ tmp = pickle.load(fd)
+
+ if tmp.__class__ != self.__class__:
+ raise ValueError("Using different file list type")
+
+ tmp.paths.sort()
+ if self.paths != tmp.paths:
+ raise ValueError, "Path list changed"
+
+ # Overwrite this object with the other
+ for attr, value in tmp.__dict__.items():
+ setattr(self, attr, value)
+
+ return True
+
+ except Exception, e:
+ logging.warning("Loading cache: %s" % e)
+ return False
+
+ def add_to_favourites(self):
+ '''Adds the current image to the list of favourites'''
+ self.favourites.append(self.get_current_image())
+
+ def scan_paths(self):
+ raise NotImplementedError()
def get_next_image(self):
raise NotImplementedError()
class RandomFileList(BaseFileList):
def __init__(self):
+ super(RandomFileList, self).__init__()
self.list = []
- self.paths = []
self.last_image = None
def scan_paths(self):
def add_path(self, path):
self.paths.append(path)
- debug('Added path "%s" to the list' % path)
-
+ logging.debug('Added path "%s" to the list' % path)
+
def get_next_image(self):
n = random.randint(0, len(self.list)-1)
self.last_image = self.list[n]
- debug("Picked file '%s' from list" % self.last_image)
+ logging.debug("Picked file '%s' from list" % self.last_image)
return self.last_image
def get_current_image(self):
class AllRandomFileList(BaseFileList):
def __init__(self):
+ super(AllRandomFileList, self).__init__()
self.list = None
- self.paths = []
self.imagePointer = 0
# Scan the input directory, and then randomize the file list
def scan_paths(self):
- debug("Scanning paths")
+ logging.debug("Scanning paths")
self.list = []
for path in self.paths:
- debug('Scanning "%s"' % path)
+ logging.debug('Scanning "%s"' % path)
for dirpath, dirsnames, filenames in os.walk(path):
for filename in filter_images(filenames):
- debug('Adding file "%s"' % filename)
+ logging.debug('Adding file "%s"' % filename)
self.list.append(os.path.join(dirpath, filename))
random.shuffle(self.list)
def add_path(self, path):
self.paths.append(path)
- debug('Added path "%s" to the list' % path)
+ logging.debug('Added path "%s" to the list' % path)
def store_cache(self, filename):
try:
fd = open(filename, 'wb')
- pickle.dump(obj = self, file = fd, protocol = 2)
- debug("Cache successfully stored")
+ pickle.dump(self, fd, 2)
+ logging.debug("Cache successfully stored")
except Exception, e:
- warning("Storing cache: %s" % e)
-
- def load_cache(self, filename, rescanPaths = False):
- debug('Attempting to load cache from "%s"' % filename)
- self.paths.sort()
- try:
- fd = open(filename, 'rb')
- tmp = pickle.load(fd)
- if self.paths == tmp.paths:
- debug("Path lists match, copying properties")
- # Overwrite this object with the other
- for attr in ('list', 'imagePointer'):
- setattr(self, attr, getattr(tmp, attr))
- else:
- debug("Ignoring cache, path lists do not match")
- except Exception, e:
- warning("Loading cache: %s" % e)
+ logging.warning("Storing cache", exc_info=1)
def get_current_image(self):
return self.list[self.imagePointer]
def get_next_image(self):
self.imagePointer = self.__inc_in_range(self.imagePointer)
imageName = self.list[self.imagePointer]
- debug("Picked file '%s' (pointer=%d) from list" % (imageName, self.imagePointer))
+ logging.debug("Picked file '%s' (pointer=%d) from list" % (imageName, self.imagePointer))
return imageName
def get_prev_image(self):
self.imagePointer = self.__inc_in_range(self.imagePointer, amount=-1)
imageName = self.list[self.imagePointer]
- debug("Picked file '%s' (pointer=%d) from list" % (imageName, self.imagePointer))
+ logging.debug("Picked file '%s' (pointer=%d) from list" % (imageName, self.imagePointer))
return imageName
def is_empty(self):
return len(self.list) == 0
+
class FolderRandomFileList(BaseFileList):
"""A file list that will pick a file randomly within a directory. Each
directory has the same chance of being chosen."""
def __init__(self):
+ super(FolderRandomFileList, self).__init__()
self.directories = {}
self.last_image = None
pass
def add_path(self, path):
- debug('Added path "%s" to the list' % path)
+ logging.debug('Added path "%s" to the list' % path)
for dirpath, dirs, filenames in os.walk(path):
- debug('Scanning "%s" for images' % dirpath)
+ logging.debug('Scanning "%s" for images' % dirpath)
if self.directories.has_key(dirpath):
continue
filenames = list(filter_images(filenames))
if len(filenames):
self.directories[dirpath] = filenames
- debug('Adding "%s" to "%s"' % (filenames, dirpath))
+ logging.debug('Adding "%s" to "%s"' % (filenames, dirpath))
else:
- debug("No images found in '%s'" % dirpath)
+ logging.debug("No images found in '%s'" % dirpath)
def get_next_image(self):
directory = random.choice(self.directories.keys())
- debug('directory: "%s"' % directory)
+ logging.debug('directory: "%s"' % directory)
filename = random.choice(self.directories[directory])
- debug('filename: "%s"' % filename)
+ logging.debug('filename: "%s"' % filename)
return os.path.join(directory, filename)
def get_current_image(self):
class Cycler(object):
- def init(self, options, paths):
+ def init(self, options, paths, oneshot=False):
self.cycle_time = options.cycle_time
- self.history_filename = options.history_filename
+ self.cache_filename = options.cache_filename
- debug("Initialising wallchanger")
- wallchanger.init(options.background_colour, options.permanent)
+ logging.debug("Initialising wallchanger")
+ wallchanger.init(options.background_colour, options.permanent, options.convert)
- debug("Initialising file list")
+ logging.debug("Initialising file list")
if options.all_random:
self.filelist = AllRandomFileList()
elif options.folder_random:
for path in paths:
self.filelist.add_path(path)
- if self.filelist.load_cache(self.history_filename):
- debug("Loaded cache successfully")
+ if self.filelist.load_cache(self.cache_filename):
+ logging.debug("Loaded cache successfully")
else:
- debug("Could not load cache")
+ logging.debug("Could not load cache")
self.filelist.scan_paths()
if self.filelist.is_empty():
- error("No images were found. Exiting...")
+ logging.error("No images were found. Exiting...")
sys.exit(1)
self.task = None
- self.cmd_reload()
+ if oneshot:
+ self.cmd_next()
+ else:
+ self.cmd_reload()
def finish(self):
- self.filelist.store_cache(self.history_filename)
+ self.filelist.store_cache(self.cache_filename)
def find_files(self, options, paths):
return filelist
if self.task is not None:
self.task.cancel()
self.task = asyncsched.schedule(self.cycle_time, next)
- debug("Reset timer for %s seconds" % self.cycle_time)
+ logging.debug("Reset timer for %s seconds" % self.cycle_time)
+ self.filelist.store_cache(self.cache_filename)
def cmd_reload(self):
image = self.filelist.get_current_image()
def cmd_rescan(self):
self.filelist.scan_paths()
- self.cmd_next()
def cmd_pause(self):
if self.task is not None:
def cmd_exit(self):
asyncsched.exit()
+ def cmd_favourite(self):
+ self.filelist.add_to_favourites()
+
class Server(asynchat.async_chat):
- def __init__(self, cycler, conn, addr):
- asynchat.async_chat.__init__(self, conn=conn)
+ def __init__(self, cycler, sock):
+ asynchat.async_chat.__init__(self, sock)
self.cycler = cycler
self.ibuffer = []
self.set_terminator("\n")
self.ibuffer = []
prefix, cmd = line.split(None, 1)
if prefix != "cmd":
- debug('Bad line received "%s"' % line)
+ logging.debug('Bad line received "%s"' % line)
return
if hasattr(self.cycler, "cmd_" + cmd):
- debug('Executing command "%s"' % cmd)
+ logging.debug('Executing command "%s"' % cmd)
getattr(self.cycler, "cmd_" + cmd)()
else:
- debug('Unknown command received "%s"' % cmd)
+ logging.debug('Unknown command received "%s"' % cmd)
+class SockHackWrap(object):
+ def __init__(self, sock, addr):
+ self.__sock = sock
+ self.__addr = addr
+ def getpeername(self):
+ return self.__addr
+ def __getattr__(self, key):
+ return getattr(self.__sock, key)
class Listener(asyncore.dispatcher):
def __init__(self, socket_filename, cycler):
asyncore.dispatcher.__init__(self)
self.cycler = cycler
self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ try:
+ os.unlink(socket_filename)
+ except OSError:
+ pass
self.bind(socket_filename)
self.listen(2) # Backlog = 2
def handle_accept(self):
- conn, addr = self.accept()
- Server(self.cycler, conn, addr)
+ sock, addr = self.accept()
+ Server(self.cycler, SockHackWrap(sock, addr))
def writable(self):
return False
sock = sock.makefile()
for i, cmd in enumerate(args):
sock.write("cmd %s\n" % cmd)
- if i+1 != len(args):
+ if i < len(args) - 1:
time.sleep(options.cycle_time)
sock.close()
def do_oneshot(options, paths):
cycler = Cycler()
- cycler.init(options, paths)
+ cycler.init(options, paths, oneshot=True)
def build_parser():
parser = OptionParser(version="%prog " + VERSION,
parser.add_option("--socket",
action="store", type="string", dest="socket_filename", default=os.path.expanduser('~/.randombg_socket'),
help="Location of the command/control socket.")
- parser.add_option("--history-file",
- action="store", type="string", dest="history_filename", default=os.path.expanduser('~/.randombg_historyfile'),
+ parser.add_option("--cache-file",
+ action="store", type="string", dest="cache_filename", default=os.path.expanduser('~/.randombg_cache'),
help="Stores the location of the last image to be loaded.")
+ parser.add_option("--server",
+ action="store_true", dest="server", default=False,
+ help="Run in server mode to listen for clients.")
return parser
def main():
elif options.verbose >= 2:
logging.getLogger().setLevel(logging.DEBUG)
+ if options.server:
+ do_server(options, args)
+ return
+
if options.oneshot:
do_oneshot(options, args)
+ return
- if os.path.exists(options.socket_filename):
+ try:
do_client(options, args)
- else:
- do_server(options, args)
+ return
+ except Exception, e:
+ print >>sys.stderr, "Failed to connect to server:", e
if __name__ == "__main__":
main()
-