X-Git-Url: https://code.delx.au/bg-scripts/blobdiff_plain/f9c37d3e9223c407abe5775490b258fb8ad5bdac..1ad2850b8c0ec352f1d096882d961325604afd71:/bin/randombg.py diff --git a/bin/randombg.py b/bin/randombg.py deleted file mode 100755 index dad637d..0000000 --- a/bin/randombg.py +++ /dev/null @@ -1,404 +0,0 @@ -#!/usr/bin/env python - -VERSION = "2.0" - - -import asyncore, asynchat, socket -import os, os.path, random, sys, time -from optparse import OptionParser -import logging -from logging import debug, info, warning, error, critical -logging.basicConfig(format="%(levelname)s: %(message)s") -try: - import cPickle as pickle -except ImportError: - import pickle - -try: - # Required libraries - import asyncsched - import wallchanger -except ImportError, e: - critical("Missing libraries! Exiting...") - sys.exit(1) - - - - -def filter_images(filenames): - extensions = ('.jpg', '.jpe', '.jpeg', '.png', '.gif', '.bmp') - for filename in filenames: - _, ext = os.path.splitext(filename) - if ext.lower() in extensions: - yield filename - -class BaseFileList(object): - """Base file list implementation""" - def scan_paths(self): - raise NotImplementedError() - - def add_path(self, path): - raise NotImplementedError() - - def store_cache(self, path): - pass - - def load_cache(self, filename, rescanPaths = False): - pass - - def get_next_image(self): - raise NotImplementedError() - - def get_prev_image(self): - raise NotImplementedError() - - def get_current_image(self): - raise NotImplementedError() - - def is_empty(self): - return True - - -class RandomFileList(BaseFileList): - def __init__(self): - self.list = [] - self.paths = [] - self.last_image = None - - def scan_paths(self): - for path in self.paths: - for dirpath, dirsnames, filenames in os.walk(path): - for filename in filter_images(filenames): - self.list.append(os.path.join(dirpath, filename)) - - def add_path(self, path): - self.paths.append(path) - 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) - return self.last_image - - def is_empty(self): - return len(self.list) == 0 - - -class AllRandomFileList(BaseFileList): - def __init__(self): - 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") - - self.list = [] - for path in self.paths: - debug('Scanning "%s"' % path) - for dirpath, dirsnames, filenames in os.walk(path): - for filename in filter_images(filenames): - 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) - - def store_cache(self, filename): - try: - fd = open(filename, 'wb') - pickle.dump(obj = self, file = fd, protocol = 2) - 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) - - def get_current_image(self): - return self.list[self.imagePointer] - - def __inc_in_range(self, n, amount = 1, rangeMax = None, rangeMin = 0): - if rangeMax == None: rangeMax = len(self.list) - assert rangeMax > 0 - return (n + amount) % rangeMax - - 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)) - 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)) - 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): - self.directories = {} - - def scan_paths(self): - pass - - def add_path(self, path): - debug('Added path "%s" to the list' % path) - for dirpath, dirs, filenames in os.walk(path): - 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)) - else: - debug("No images found in '%s'" % dirpath) - - def get_next_image(self): - directory = random.choice(self.directories.keys()) - debug('directory: "%s"' % directory) - filename = random.choice(self.directories[directory]) - debug('filename: "%s"' % filename) - return os.path.join(directory, filename) - - def is_empty(self): - return len(self.directories.values()) == 0 - - -class Cycler(object): - def init(self, options, paths): - self.cycle_time = options.cycle_time - self.history_filename = options.history_filename - - debug("Initialising wallchanger") - wallchanger.init(options.background_colour, options.permanent) - - debug("Initialising file list") - if options.all_random: - self.filelist = AllRandomFileList() - elif options.folder_random: - self.filelist = FolderRandomFileList() - else: - self.filelist = RandomFileList() - - for path in paths: - self.filelist.add_path(path) - - if self.filelist.load_cache(self.history_filename): - debug("Loaded cache successfully") - else: - debug("Could not load cache") - self.filelist.scan_paths() - - if self.filelist.is_empty(): - error("No images were found. Exiting...") - sys.exit(1) - - self.task = None - self.cmd_next() - - def finish(self): - self.filelist.store_cache(self.history_filename) - - def find_files(self, options, paths): - return filelist - - def cmd_reset(self): - def next(): - image = self.filelist.get_next_image() - wallchanger.set_image(image) - self.task = None - self.cmd_reset() - - 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) - - def cmd_reload(self): - image = self.filelist.get_current_image() - wallchanger.set_image(image) - self.cmd_reset() - - def cmd_next(self): - image = self.filelist.get_next_image() - wallchanger.set_image(image) - self.cmd_reset() - - def cmd_prev(self): - image = self.filelist.get_prev_image() - wallchanger.set_image(image) - self.cmd_reset() - - def cmd_rescan(self): - self.filelist.scan_paths() - self.cmd_next() - - def cmd_pause(self): - if self.task is not None: - self.task.cancel() - self.task = None - - def cmd_exit(self): - asyncsched.exit() - -class Server(asynchat.async_chat): - def __init__(self, cycler, conn, addr): - asynchat.async_chat.__init__(self, conn=conn) - self.cycler = cycler - self.ibuffer = [] - self.set_terminator("\n") - - def collect_incoming_data(self, data): - self.ibuffer.append(data) - - def found_terminator(self): - line = "".join(self.ibuffer).lower() - self.ibuffer = [] - prefix, cmd = line.split(None, 1) - if prefix != "cmd": - debug('Bad line received "%s"' % line) - return - if hasattr(self.cycler, "cmd_" + cmd): - debug('Executing command "%s"' % cmd) - getattr(self.cycler, "cmd_" + cmd)() - else: - debug('Unknown command received "%s"' % cmd) - - - -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) - self.bind(socket_filename) - self.listen(2) # Backlog = 2 - - def handle_accept(self): - conn, addr = self.accept() - Server(self.cycler, conn, addr) - - def writable(self): - return False - - -def do_server(options, paths): - try: - cycler = Cycler() - listener = Listener(options.socket_filename, cycler) - # Initialisation of Cycler delayed so we grab the socket quickly - cycler.init(options, paths) - try: - asyncsched.loop() - except KeyboardInterrupt: - print - cycler.finish() - finally: - # Make sure that the socket is cleaned up - try: - os.unlink(options.socket_filename) - except: - pass - -def do_client(options, args): - if len(args) == 0: - args = ["next"] - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(options.socket_filename) - sock = sock.makefile() - for i, cmd in enumerate(args): - sock.write("cmd %s\n" % cmd) - if i+1 != len(args): - time.sleep(options.cycle_time) - sock.close() - -def do_oneshot(options, paths): - cycler = Cycler() - cycler.init(options, paths) - -def build_parser(): - parser = OptionParser(version="%prog " + VERSION, - description = "Cycles through random background images.", - usage = - "\n(server) %prog [options] dir [dir2 ...]" - "\n(client) %prog [options] [next|prev|rescan|reload|pause] [...]" - "\nThe first instance to be run will be the server.\n" - ) - parser.add_option("-p", "--permanent", - action="store_true", dest="permanent", default=False, - help="Make the background permanent. Note: This will cause all machines logged in with this account to simultaneously change background [Default: %default]") - parser.add_option("-v", '-d', "--verbose", "--debug", - action="count", dest="verbose", default=0, - help="Make the louder (good for debugging, or those who are curious)") - parser.add_option("-b", "--background-colour", - action="store", type="string", dest="background_colour", default="black", - help="Change the default background colour that is displayed if the image is not in the correct aspect ratio [Default: %default]") - parser.add_option("--all-random", - action="store_true", dest="all_random", default=False, - help="Make sure that all images have been displayed before repeating an image") - parser.add_option("-1", "--oneshot", - action="store_true", dest="oneshot", default=False, - help="Set one random image and terminate immediately.") - parser.add_option("--folder-random", - action="store_true", dest="folder_random", default=False, - help="Give each folder an equal chance of having an image selected from it") - parser.add_option("--convert", - action="store_true", dest="convert", default=False, - help="Do conversions using ImageMagick or PIL, don't rely on the window manager") - parser.add_option("--cycle-time", - action="store", type="int", default=1800, dest="cycle_time", - help="Cause the image to cycle every X seconds") - 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'), - help="Stores the location of the last image to be loaded.") - return parser - -def main(): - parser = build_parser() - options, args = parser.parse_args(sys.argv[1:]) - - if options.verbose == 1: - logging.getLogger().setLevel(logging.INFO) - elif options.verbose >= 2: - logging.getLogger().setLevel(logging.DEBUG) - - if options.oneshot: - do_oneshot(options, args) - - if os.path.exists(options.socket_filename): - do_client(options, args) - else: - do_server(options, args) - - -if __name__ == "__main__": - main() -