]> code.delx.au - bg-scripts/blob - bin/randombg.py
Major updates to randombg - stdlib is good :)
[bg-scripts] / bin / randombg.py
1 #!/usr/bin/env python
2
3 VERSION = "2.0"
4
5
6 import asyncore, asynchat, socket
7 import os, os.path, sys, time
8 from optparse import OptionParser
9 import logging
10 from logging import debug, info, warning, error, critical
11 logging.basicConfig(format="%(levelname)s: %(message)s")
12
13 try:
14 # Required libraries
15 import asyncsched
16 from FileLists import *
17 import WallChanger
18 except ImportError, e:
19 critical("Missing libraries! Exiting...")
20 sys.exit(1)
21
22
23 class Cycler(object):
24 def __init__(self, options, paths):
25 filelist = self.find_files(options, paths)
26 if not filelist.hasImages():
27 error("No images were found. Exiting...")
28 sys.exit(1)
29
30 debug("Initilizing RandomBG")
31 self.randombg = WallChanger.RandomBG(filelist, options.background_colour, options.permanent)
32 self.cycle_time = options.cycle_time
33
34 self.task = None
35 self.cmd_next()
36
37 def find_files(self, options, paths):
38 if options.all_random:
39 filelist = AllRandomFileList()
40 elif options.folder_random:
41 filelist = FolderRandomFileList()
42 else:
43 filelist = RandomFileList()
44
45 for path in paths:
46 filelist.doAddPath(path)
47
48 if filelist.attemptCacheLoad(options.history_filename):
49 debug("Loaded cache successfully")
50 else:
51 debug("Could not load cache")
52 filelist.doScanPaths()
53 return filelist
54
55 def cmd_reset(self):
56 def next():
57 self.randombg.cycleNext()
58 self.task = None
59 self.cmd_reset()
60
61 if self.task is not None:
62 self.task.cancel()
63 self.task = asyncsched.schedule(self.cycle_time, next)
64 debug("Reset timer for %s seconds" % self.cycle_time)
65
66 def cmd_reload(self):
67 self.randombg.cycleReload()
68 self.cmd_reset()
69
70 def cmd_next(self):
71 self.randombg.cycleNext()
72 self.cmd_reset()
73
74 def cmd_prev(self):
75 self.randombg.cyclePrev()
76 self.cmd_reset()
77
78 def cmd_rescan(self):
79 self.randombg.filelist.doScanPaths()
80 self.cmd_next()
81
82 def cmd_pause(self):
83 if self.task is not None:
84 self.task.cancel()
85 self.task = None
86
87 class Server(asynchat.async_chat):
88 def __init__(self, cycler, conn, addr):
89 asynchat.async_chat.__init__(self, conn=conn)
90 self.cycler = cycler
91 self.ibuffer = []
92 self.set_terminator("\n")
93
94 def collect_incoming_data(self, data):
95 self.ibuffer.append(data)
96
97 def found_terminator(self):
98 line = "".join(self.ibuffer).lower()
99 self.ibuffer = []
100 prefix, cmd = line.split(None, 1)
101 if prefix != "cmd":
102 debug('Bad line received "%s"' % line)
103 return
104 if hasattr(self.cycler, "cmd_" + cmd):
105 debug('Executing command "%s"' % cmd)
106 getattr(self.cycler, "cmd_" + cmd)()
107 else:
108 debug('Unknown command received "%s"' % cmd)
109
110
111
112 class Listener(asyncore.dispatcher):
113 def __init__(self, socket_filename, randombg):
114 asyncore.dispatcher.__init__(self)
115 self.randombg = randombg
116 self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM)
117 self.bind(socket_filename)
118 self.listen(2) # Backlog = 2
119
120 def handle_accept(self):
121 conn, addr = self.accept()
122 Server(self.randombg, conn, addr)
123
124
125 def do_server(options, paths):
126 try:
127 cycler = Cycler(options, paths)
128 listener = Listener(options.socket_filename, cycler)
129 asyncsched.loop()
130 except KeyboardInterrupt:
131 print
132 finally:
133 # Make sure that the socket is cleaned up
134 try:
135 os.unlink(options.socket_filename)
136 except:
137 pass
138
139 def do_client(options, args):
140 if len(args) == 0:
141 args = ["next"]
142 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
143 sock.connect(options.socket_filename)
144 sock = sock.makefile()
145 for i, cmd in enumerate(args):
146 sock.write("cmd %s\n" % cmd)
147 if i+1 != len(args):
148 time.sleep(options.cycle_time)
149 sock.close()
150
151
152 def build_parser():
153 parser = OptionParser(version="%prog " + VERSION,
154 description = "Cycles through random background images.",
155 usage =
156 "\n(server) %prog [options] dir [dir2 ...]"
157 "\n(client) %prog [options] [next|prev|rescan|reload|pause] [...]"
158 "\nThe first instance to be run will be the server.\n"
159 )
160 parser.add_option("-p", "--permanent",
161 action="store_true", dest="permanent", default=False,
162 help="Make the background permanent. Note: This will cause all machines logged in with this account to simultaneously change background [Default: %default]")
163 parser.add_option("-v", '-d', "--verbose", "--debug",
164 action="count", dest="verbose", default=0,
165 help="Make the louder (good for debugging, or those who are curious)")
166 parser.add_option("-b", "--background-colour",
167 action="store", type="string", dest="background_colour", default="black",
168 help="Change the default background colour that is displayed if the image is not in the correct aspect ratio [Default: %default]")
169 parser.add_option("--all-random",
170 action="store_true", dest="all_random", default=False,
171 help="Make sure that all images have been displayed before repeating an image")
172 parser.add_option("--folder-random",
173 action="store_true", dest="folder_random", default=False,
174 help="Give each folder an equal chance of having an image selected from it")
175 parser.add_option("--cycle-time",
176 action="store", type="int", default=1800, dest="cycle_time",
177 help="Cause the image to cycle every X seconds")
178 parser.add_option("--socket",
179 action="store", type="string", dest="socket_filename", default=os.path.expanduser('~/tmp/tmp_socket'),
180 help="Location of the command/control socket.")
181 parser.add_option("--history-file",
182 action="store", type="string", dest="history_filename", default=os.path.expanduser('~/.randombg_historyfile'),
183 help="Stores the location of the last image to be loaded.")
184 return parser
185
186 def main():
187 parser = build_parser()
188 options, args = parser.parse_args(sys.argv[1:])
189
190 logging.basicConfig(level=10*options.verbose)
191
192 if os.path.exists(options.socket_filename):
193 do_client(options, args)
194 else:
195 do_server(options, args)
196
197
198 if __name__ == "__main__":
199 main()
200