]>
code.delx.au - bg-scripts/blob - randombg.py
13 from optparse
import OptionParser
16 logging
. basicConfig ( format
= " %(levelname)s : %(message)s " )
18 # Python 2.3's logging.basicConfig does not support parameters
22 import cPickle
as pickle
30 except ImportError , e
:
31 logging
. critical ( "Missing libraries! Exiting..." , exc_info
= 1 )
37 def filter_images ( filenames
):
38 extensions
= ( '.jpg' , '.jpe' , '.jpeg' , '.png' , '.gif' , '.bmp' )
39 for filename
in filenames
:
40 _
, ext
= os
. path
. splitext ( filename
)
41 if ext
. lower () in extensions
:
44 class BaseFileList ( object ):
45 """Base file list implementation"""
50 def add_path ( self
, path
):
51 self
. paths
. append ( path
)
53 def store_cache ( self
, filename
):
55 logging
. debug ( "Attempting to store cache" )
56 fd
= open ( filename
, 'wb' )
57 pickle
. dump ( self
, fd
, 2 )
58 logging
. debug ( "Cache successfully stored" )
60 warning ( "Storing cache: %s " % e
)
62 def load_cache ( self
, filename
):
64 logging
. debug ( "Attempting to load cache from: %s " % filename
)
67 fd
= open ( filename
, 'rb' )
70 if tmp
.__ class
__ != self
.__ class
__ :
71 raise ValueError ( "Using different file list type" )
74 if self
. paths
!= tmp
. paths
:
75 raise ValueError , "Path list changed"
77 # Overwrite this object with the other
78 for attr
, value
in tmp
.__ dict
__ . items ():
79 setattr ( self
, attr
, value
)
84 logging
. warning ( "Loading cache: %s " % e
)
87 def add_to_favourites ( self
):
88 '''Adds the current image to the list of favourites'''
89 self
. favourites
. append ( self
. get_current_image ())
92 raise NotImplementedError ()
94 def get_next_image ( self
):
95 raise NotImplementedError ()
97 def get_prev_image ( self
):
98 raise NotImplementedError ()
100 def get_current_image ( self
):
101 raise NotImplementedError ()
107 class RandomFileList ( BaseFileList
):
109 super ( RandomFileList
, self
) .__ init
__ ()
111 self
. last_image
= None
113 def scan_paths ( self
):
114 for path
in self
. paths
:
115 for dirpath
, dirsnames
, filenames
in os
. walk ( path
):
116 for filename
in filter_images ( filenames
):
117 self
. list . append ( os
. path
. join ( dirpath
, filename
))
119 def add_path ( self
, path
):
120 self
. paths
. append ( path
)
121 logging
. debug ( 'Added path " %s " to the list' % path
)
123 def get_next_image ( self
):
124 n
= random
. randint ( 0 , len ( self
. list )- 1 )
125 self
. last_image
= self
. list [ n
]
126 logging
. debug ( "Picked file ' %s ' from list" % self
. last_image
)
127 return self
. last_image
129 def get_current_image ( self
):
131 return self
. last_image
133 return self
. get_next_image ()
136 return len ( self
. list ) == 0
139 class AllRandomFileList ( BaseFileList
):
141 super ( AllRandomFileList
, self
) .__ init
__ ()
143 self
. imagePointer
= 0
145 # Scan the input directory, and then randomize the file list
146 def scan_paths ( self
):
147 logging
. debug ( "Scanning paths" )
150 for path
in self
. paths
:
151 logging
. debug ( 'Scanning " %s "' % path
)
152 for dirpath
, dirsnames
, filenames
in os
. walk ( path
):
153 for filename
in filter_images ( filenames
):
154 logging
. debug ( 'Adding file " %s "' % filename
)
155 self
. list . append ( os
. path
. join ( dirpath
, filename
))
157 random
. shuffle ( self
. list )
159 def add_path ( self
, path
):
160 self
. paths
. append ( path
)
161 logging
. debug ( 'Added path " %s " to the list' % path
)
163 def store_cache ( self
, filename
):
165 fd
= open ( filename
, 'wb' )
166 pickle
. dump ( self
, fd
, 2 )
167 logging
. debug ( "Cache successfully stored" )
169 logging
. warning ( "Storing cache" , exc_info
= 1 )
171 def get_current_image ( self
):
172 return self
. list [ self
. imagePointer
]
174 def __inc_in_range ( self
, n
, amount
= 1 , rangeMax
= None , rangeMin
= 0 ):
175 if rangeMax
== None : rangeMax
= len ( self
. list )
177 return ( n
+ amount
) % rangeMax
179 def get_next_image ( self
):
180 self
. imagePointer
= self
.__ inc
_ in
_ range
( self
. imagePointer
)
181 imageName
= self
. list [ self
. imagePointer
]
182 logging
. debug ( "Picked file ' %s ' (pointer= %d ) from list" % ( imageName
, self
. imagePointer
))
185 def get_prev_image ( self
):
186 self
. imagePointer
= self
.__ inc
_ in
_ range
( self
. imagePointer
, amount
=- 1 )
187 imageName
= self
. list [ self
. imagePointer
]
188 logging
. debug ( "Picked file ' %s ' (pointer= %d ) from list" % ( imageName
, self
. imagePointer
))
192 return len ( self
. list ) == 0
195 class FolderRandomFileList ( BaseFileList
):
196 """A file list that will pick a file randomly within a directory. Each
197 directory has the same chance of being chosen."""
199 super ( FolderRandomFileList
, self
) .__ init
__ ()
200 self
. directories
= {}
201 self
. last_image
= None
203 def scan_paths ( self
):
206 def add_path ( self
, path
):
207 logging
. debug ( 'Added path " %s " to the list' % path
)
208 for dirpath
, dirs
, filenames
in os
. walk ( path
):
209 logging
. debug ( 'Scanning " %s " for images' % dirpath
)
210 if self
. directories
. has_key ( dirpath
):
212 filenames
= list ( filter_images ( filenames
))
214 self
. directories
[ dirpath
] = filenames
215 logging
. debug ( 'Adding " %s " to " %s "' % ( filenames
, dirpath
))
217 logging
. debug ( "No images found in ' %s '" % dirpath
)
219 def get_next_image ( self
):
220 directory
= random
. choice ( self
. directories
. keys ())
221 logging
. debug ( 'directory: " %s "' % directory
)
222 filename
= random
. choice ( self
. directories
[ directory
])
223 logging
. debug ( 'filename: " %s "' % filename
)
224 return os
. path
. join ( directory
, filename
)
226 def get_current_image ( self
):
228 return self
. last_image
230 return self
. get_next_image ()
233 return len ( self
. directories
. values ()) == 0
236 class Cycler ( object ):
237 def init ( self
, options
, paths
, oneshot
= False ):
238 self
. cycle_time
= options
. cycle_time
239 self
. cache_filename
= options
. cache_filename
241 logging
. debug ( "Initialising wallchanger" )
242 wallchanger
. init ( options
. background_colour
, options
. permanent
, options
. convert
)
244 logging
. debug ( "Initialising file list" )
245 if options
. all_random
:
246 self
. filelist
= AllRandomFileList ()
247 elif options
. folder_random
:
248 self
. filelist
= FolderRandomFileList ()
250 self
. filelist
= RandomFileList ()
253 self
. filelist
. add_path ( path
)
255 if self
. filelist
. load_cache ( self
. cache_filename
):
256 logging
. debug ( "Loaded cache successfully" )
258 logging
. debug ( "Could not load cache" )
259 self
. filelist
. scan_paths ()
261 if self
. filelist
. is_empty ():
262 logging
. error ( "No images were found. Exiting..." )
272 self
. filelist
. store_cache ( self
. cache_filename
)
274 def find_files ( self
, options
, paths
):
279 image
= self
. filelist
. get_next_image ()
280 wallchanger
. set_image ( image
)
284 if self
. task
is not None :
286 self
. task
= asyncsched
. schedule ( self
. cycle_time
, next
)
287 logging
. debug ( "Reset timer for %s seconds" % self
. cycle_time
)
288 self
. filelist
. store_cache ( self
. cache_filename
)
290 def cmd_reload ( self
):
291 image
= self
. filelist
. get_current_image ()
292 wallchanger
. set_image ( image
)
296 image
= self
. filelist
. get_next_image ()
297 wallchanger
. set_image ( image
)
301 image
= self
. filelist
. get_prev_image ()
302 wallchanger
. set_image ( image
)
305 def cmd_rescan ( self
):
306 self
. filelist
. scan_paths ()
309 if self
. task
is not None :
316 def cmd_favourite ( self
):
317 self
. filelist
. add_to_favourites ()
319 class Server ( asynchat
. async_chat
):
320 def __init__ ( self
, cycler
, sock
):
321 asynchat
. async_chat
.__ init
__ ( self
, sock
)
324 self
. set_terminator ( " \n " )
326 def collect_incoming_data ( self
, data
):
327 self
. ibuffer
. append ( data
)
329 def found_terminator ( self
):
330 line
= "" . join ( self
. ibuffer
). lower ()
332 prefix
, cmd
= line
. split ( None , 1 )
334 logging
. debug ( 'Bad line received " %s "' % line
)
336 if hasattr ( self
. cycler
, "cmd_" + cmd
):
337 logging
. debug ( 'Executing command " %s "' % cmd
)
338 getattr ( self
. cycler
, "cmd_" + cmd
)()
340 logging
. debug ( 'Unknown command received " %s "' % cmd
)
343 class SockHackWrap ( object ):
344 def __init__ ( self
, sock
, addr
):
347 def getpeername ( self
):
349 def __getattr__ ( self
, key
):
350 return getattr ( self
.__ sock
, key
)
352 class Listener ( asyncore
. dispatcher
):
353 def __init__ ( self
, socket_filename
, cycler
):
354 asyncore
. dispatcher
.__ init
__ ( self
)
356 self
. create_socket ( socket
. AF_UNIX
, socket
. SOCK_STREAM
)
358 os
. unlink ( socket_filename
)
361 self
. bind ( socket_filename
)
362 self
. listen ( 2 ) # Backlog = 2
364 def handle_accept ( self
):
365 sock
, addr
= self
. accept ()
366 Server ( self
. cycler
, SockHackWrap ( sock
, addr
))
372 def do_server ( options
, paths
):
375 listener
= Listener ( options
. socket_filename
, cycler
)
376 # Initialisation of Cycler delayed so we grab the socket quickly
377 cycler
. init ( options
, paths
)
380 except KeyboardInterrupt :
384 # Make sure that the socket is cleaned up
386 os
. unlink ( options
. socket_filename
)
390 def do_client ( options
, args
):
393 sock
= socket
. socket ( socket
. AF_UNIX
, socket
. SOCK_STREAM
)
394 sock
. connect ( options
. socket_filename
)
395 sock
= sock
. makefile ()
396 for i
, cmd
in enumerate ( args
):
397 sock
. write ( "cmd %s \n " % cmd
)
398 if i
< len ( args
) - 1 :
399 time
. sleep ( options
. cycle_time
)
402 def do_oneshot ( options
, paths
):
404 cycler
. init ( options
, paths
, oneshot
= True )
407 parser
= OptionParser ( version
= "%prog " + VERSION
,
408 description
= "Cycles through random background images." ,
410 " \n (server) %prog [options] dir [dir2 ...]"
411 " \n (client) %prog [options] [next|prev|rescan|reload|pause] [...]"
412 " \n The first instance to be run will be the server. \n "
414 parser
. add_option ( "-p" , "--permanent" ,
415 action
= "store_true" , dest
= "permanent" , default
= False ,
416 help = "Make the background permanent. Note: This will cause all machines logged in with this account to simultaneously change background [Default: %def ault]" )
417 parser
. add_option ( "-v" , '-d' , "--verbose" , "--debug" ,
418 action
= "count" , dest
= "verbose" , default
= 0 ,
419 help = "Make the louder (good for debugging, or those who are curious)" )
420 parser
. add_option ( "-b" , "--background-colour" ,
421 action
= "store" , type = "string" , dest
= "background_colour" , default
= "black" ,
422 help = "Change the default background colour that is displayed if the image is not in the correct aspect ratio [Default: %def ault]" )
423 parser
. add_option ( "--all-random" ,
424 action
= "store_true" , dest
= "all_random" , default
= False ,
425 help = "Make sure that all images have been displayed before repeating an image" )
426 parser
. add_option ( "-1" , "--oneshot" ,
427 action
= "store_true" , dest
= "oneshot" , default
= False ,
428 help = "Set one random image and terminate immediately." )
429 parser
. add_option ( "--folder-random" ,
430 action
= "store_true" , dest
= "folder_random" , default
= False ,
431 help = "Give each folder an equal chance of having an image selected from it" )
432 parser
. add_option ( "--convert" ,
433 action
= "store_true" , dest
= "convert" , default
= False ,
434 help = "Do conversions using ImageMagick or PIL, don't rely on the window manager" )
435 parser
. add_option ( "--cycle-time" ,
436 action
= "store" , type = "int" , default
= 1800 , dest
= "cycle_time" ,
437 help = "Cause the image to cycle every X seconds" )
438 parser
. add_option ( "--socket" ,
439 action
= "store" , type = "string" , dest
= "socket_filename" , default
= os
. path
. expanduser ( '~/.randombg_socket' ),
440 help = "Location of the command/control socket." )
441 parser
. add_option ( "--cache-file" ,
442 action
= "store" , type = "string" , dest
= "cache_filename" , default
= os
. path
. expanduser ( '~/.randombg_cache' ),
443 help = "Stores the location of the last image to be loaded." )
444 parser
. add_option ( "--server" ,
445 action
= "store_true" , dest
= "server" , default
= False ,
446 help = "Run in server mode to listen for clients." )
450 parser
= build_parser ()
451 options
, args
= parser
. parse_args ( sys
. argv
[ 1 :])
453 if options
. verbose
== 1 :
454 logging
. getLogger (). setLevel ( logging
. INFO
)
455 elif options
. verbose
>= 2 :
456 logging
. getLogger (). setLevel ( logging
. DEBUG
)
459 do_server ( options
, args
)
463 do_oneshot ( options
, args
)
467 do_client ( options
, args
)
470 print >> sys
. stderr
, "Failed to connect to server:" , e
473 if __name__
== "__main__" :