]>
code.delx.au - bg-scripts/blob - bin/randombg.py
6 import asyncore
, asynchat
, socket
7 import os
, os
. path
, random
, sys
, time
8 from optparse
import OptionParser
10 from logging
import debug
, info
, warning
, error
, critical
11 logging
. basicConfig ( format
= " %(levelname)s : %(message)s " )
13 import cPickle
as pickle
21 except ImportError , e
:
22 critical ( "Missing libraries! Exiting..." )
28 def filter_images ( filenames
):
29 extensions
= ( '.jpg' , '.jpe' , '.jpeg' , '.png' , '.gif' , '.bmp' )
30 for filename
in filenames
:
31 _
, ext
= os
. path
. splitext ( filename
)
32 if ext
. lower () in extensions
:
35 class BaseFileList ( object ):
36 """Base file list implementation"""
38 raise NotImplementedError ()
40 def add_path ( self
, path
):
41 raise NotImplementedError ()
43 def store_cache ( self
, path
):
46 def load_cache ( self
, filename
, rescanPaths
= False ):
49 def get_next_image ( self
):
50 raise NotImplementedError ()
52 def get_prev_image ( self
):
53 raise NotImplementedError ()
55 def get_current_image ( self
):
56 raise NotImplementedError ()
62 class RandomFileList ( BaseFileList
):
66 self
. last_image
= None
69 for path
in self
. paths
:
70 for dirpath
, dirsnames
, filenames
in os
. walk ( path
):
71 for filename
in filter_images ( filenames
):
72 self
. list . append ( os
. path
. join ( dirpath
, filename
))
74 def add_path ( self
, path
):
75 self
. paths
. append ( path
)
76 debug ( 'Added path " %s " to the list' % path
)
78 def get_next_image ( self
):
79 n
= random
. randint ( 0 , len ( self
. list )- 1 )
80 self
. last_image
= self
. list [ n
]
81 debug ( "Picked file ' %s ' from list" % self
. last_image
)
82 return self
. last_image
85 return len ( self
. list ) > 0
88 class AllRandomFileList ( BaseFileList
):
94 # Scan the input directory, and then randomize the file list
96 debug ( "Scanning paths" )
99 for path
in self
. paths
:
100 debug ( 'Scanning " %s "' % path
)
101 for dirpath
, dirsnames
, filenames
in os
. walk ( path
):
102 for filename
in filter_images ( filenames
):
103 debug ( 'Adding file " %s "' % filename
)
104 self
. list . append ( os
. path
. join ( dirpath
, filename
))
106 random
. shuffle ( self
. list )
108 def add_path ( self
, path
):
109 self
. paths
. append ( path
)
110 debug ( 'Added path " %s " to the list' % path
)
112 def store_cache ( self
, filename
):
114 fd
= open ( filename
, 'wb' )
115 pickle
. dump ( obj
= self
, file = fd
, protocol
= 2 )
116 debug ( "Cache successfully stored" )
118 warning ( "Exception while storing cache: ' %s '" % e
)
120 def load_cache ( self
, filename
, rescanPaths
= False ):
121 debug ( 'Attempting to load cache from " %s "' % filename
)
124 fd
= open ( filename
, 'rb' )
125 tmp
= pickle
. load ( fd
)
126 if self
. paths
== tmp
. paths
:
127 debug ( "Path lists match, copying properties" )
128 # Overwrite this object with the other
129 for attr
in ( 'list' , 'imagePointer' ):
130 setattr ( self
, attr
, getattr ( tmp
, attr
))
132 debug ( "Ignoring cache, path lists do not match" )
134 warning ( "Exception while loading cache: ' %s '" % e
)
136 def get_current_image ( self
):
137 return self
. list [ self
. imagePointer
]
139 def __inc_in_range ( self
, n
, amount
= 1 , rangeMax
= None , rangeMin
= 0 ):
140 if rangeMax
== None : rangeMax
= len ( self
. list )
142 return ( n
+ amount
) % rangeMax
144 def get_next_image ( self
):
145 self
. imagePointer
= self
.__ inc
_ in
_ range
( self
. imagePointer
)
146 imageName
= self
. list [ self
. imagePointer
]
147 debug ( "Picked file ' %s ' (pointer= %d ) from list" % ( imageName
, self
. imagePointer
))
150 def get_prev_image ( self
):
151 self
. imagePointer
= self
.__ inc
_ in
_ range
( self
. imagePointer
, amount
=- 1 )
152 imageName
= self
. list [ self
. imagePointer
]
153 debug ( "Picked file ' %s ' (pointer= %d ) from list" % ( imageName
, self
. imagePointer
))
159 class FolderRandomFileList ( BaseFileList
):
160 """A file list that will pick a file randomly within a directory. Each
161 directory has the same chance of being chosen."""
163 self
. directories
= {}
165 def add_path ( self
, path
):
166 debug ( 'Added path " %s " to the list' % path
)
167 for dirpath
, dirs
, filenames
in os
. walk ( path
):
168 debug ( 'Scanning " %s " for images' % dirpath
)
169 if self
. directories
. has_key ( dirpath
):
171 filenames
= filter_images ( filenames
)
173 self
. directories
[ dirpath
] = filenames
174 debug ( 'Adding " %s " to " %s "' % ( filenames
, dirpath
))
176 debug ( "No images found in ' %s '" % dirpath
)
178 def get_next_image ( self
):
179 directory
= random
. choice ( self
. directories
. keys ())
180 debug ( 'directory: " %s "' % directory
)
181 filename
= random
. choice ( self
. directories
[ directory
])
182 debug ( 'filename: " %s "' % filename
)
183 return os
. path
. join ( directory
, filename
)
186 return len ( self
. directories
. values ())
189 class Cycler ( object ):
190 def __init__ ( self
, options
, paths
):
191 self
. filelist
= self
. find_files ( options
, paths
)
192 if not self
. filelist
. is_empty ():
193 error ( "No images were found. Exiting..." )
196 debug ( "Initialising RandomBG" )
197 self
. randombg
= WallChanger
. RandomBG ( options
. background_colour
, options
. permanent
)
198 self
. cycle_time
= options
. cycle_time
203 def find_files ( self
, options
, paths
):
204 if options
. all_random
:
205 filelist
= AllRandomFileList ()
206 elif options
. folder_random
:
207 filelist
= FolderRandomFileList ()
209 filelist
= RandomFileList ()
212 filelist
. add_path ( path
)
214 if filelist
. load_cache ( options
. history_filename
):
215 debug ( "Loaded cache successfully" )
217 debug ( "Could not load cache" )
218 filelist
. scan_paths ()
223 image
= self
. filelist
. get_next_image ()
224 self
. randombg
. setImage ( image
)
228 if self
. task
is not None :
230 self
. task
= asyncsched
. schedule ( self
. cycle_time
, next
)
231 debug ( "Reset timer for %s seconds" % self
. cycle_time
)
233 def cmd_reload ( self
):
234 image
= self
. filelist
. get_current_image ()
235 self
. randombg
. setImage ( image
)
239 image
= self
. filelist
. get_next_image ()
240 self
. randombg
. setImage ( image
)
244 image
= self
. filelist
. get_prev_image ()
245 self
. randombg
. setImage ( image
)
248 def cmd_rescan ( self
):
249 self
. filelist
. scan_paths ()
253 if self
. task
is not None :
257 class Server ( asynchat
. async_chat
):
258 def __init__ ( self
, cycler
, conn
, addr
):
259 asynchat
. async_chat
.__ init
__ ( self
, conn
= conn
)
262 self
. set_terminator ( " \n " )
264 def collect_incoming_data ( self
, data
):
265 self
. ibuffer
. append ( data
)
267 def found_terminator ( self
):
268 line
= "" . join ( self
. ibuffer
). lower ()
270 prefix
, cmd
= line
. split ( None , 1 )
272 debug ( 'Bad line received " %s "' % line
)
274 if hasattr ( self
. cycler
, "cmd_" + cmd
):
275 debug ( 'Executing command " %s "' % cmd
)
276 getattr ( self
. cycler
, "cmd_" + cmd
)()
278 debug ( 'Unknown command received " %s "' % cmd
)
282 class Listener ( asyncore
. dispatcher
):
283 def __init__ ( self
, socket_filename
, randombg
):
284 asyncore
. dispatcher
.__ init
__ ( self
)
285 self
. randombg
= randombg
286 self
. create_socket ( socket
. AF_UNIX
, socket
. SOCK_STREAM
)
287 self
. bind ( socket_filename
)
288 self
. listen ( 2 ) # Backlog = 2
290 def handle_accept ( self
):
291 conn
, addr
= self
. accept ()
292 Server ( self
. randombg
, conn
, addr
)
295 def do_server ( options
, paths
):
298 cycler
= Cycler ( options
, paths
)
299 listener
= Listener ( options
. socket_filename
, cycler
)
301 except KeyboardInterrupt :
304 # Make sure that the socket is cleaned up
306 os
. unlink ( options
. socket_filename
)
310 def do_client ( options
, args
):
313 sock
= socket
. socket ( socket
. AF_UNIX
, socket
. SOCK_STREAM
)
314 sock
. connect ( options
. socket_filename
)
315 sock
= sock
. makefile ()
316 for i
, cmd
in enumerate ( args
):
317 sock
. write ( "cmd %s \n " % cmd
)
319 time
. sleep ( options
. cycle_time
)
324 parser
= OptionParser ( version
= "%prog " + VERSION
,
325 description
= "Cycles through random background images." ,
327 " \n (server) %prog [options] dir [dir2 ...]"
328 " \n (client) %prog [options] [next|prev|rescan|reload|pause] [...]"
329 " \n The first instance to be run will be the server. \n "
331 parser
. add_option ( "-p" , "--permanent" ,
332 action
= "store_true" , dest
= "permanent" , default
= False ,
333 help = "Make the background permanent. Note: This will cause all machines logged in with this account to simultaneously change background [Default: %def ault]" )
334 parser
. add_option ( "-v" , '-d' , "--verbose" , "--debug" ,
335 action
= "count" , dest
= "verbose" , default
= 0 ,
336 help = "Make the louder (good for debugging, or those who are curious)" )
337 parser
. add_option ( "-b" , "--background-colour" ,
338 action
= "store" , type = "string" , dest
= "background_colour" , default
= "black" ,
339 help = "Change the default background colour that is displayed if the image is not in the correct aspect ratio [Default: %def ault]" )
340 parser
. add_option ( "--all-random" ,
341 action
= "store_true" , dest
= "all_random" , default
= False ,
342 help = "Make sure that all images have been displayed before repeating an image" )
343 parser
. add_option ( "--folder-random" ,
344 action
= "store_true" , dest
= "folder_random" , default
= False ,
345 help = "Give each folder an equal chance of having an image selected from it" )
346 parser
. add_option ( "--cycle-time" ,
347 action
= "store" , type = "int" , default
= 1800 , dest
= "cycle_time" ,
348 help = "Cause the image to cycle every X seconds" )
349 parser
. add_option ( "--socket" ,
350 action
= "store" , type = "string" , dest
= "socket_filename" , default
= os
. path
. expanduser ( '~/tmp/tmp_socket' ),
351 help = "Location of the command/control socket." )
352 parser
. add_option ( "--history-file" ,
353 action
= "store" , type = "string" , dest
= "history_filename" , default
= os
. path
. expanduser ( '~/.randombg_historyfile' ),
354 help = "Stores the location of the last image to be loaded." )
358 parser
= build_parser ()
359 options
, args
= parser
. parse_args ( sys
. argv
[ 1 :])
361 logging
. basicConfig ( level
= 10 * options
. verbose
)
363 if os
. path
. exists ( options
. socket_filename
):
364 do_client ( options
, args
)
366 do_server ( options
, args
)
369 if __name__
== "__main__" :