You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
5.5 KiB
196 lines
5.5 KiB
#!/usr/bin/env python3 |
|
|
|
import threading |
|
import os |
|
import os.path |
|
import random |
|
import time |
|
import sys |
|
import getopt |
|
|
|
import blup.output |
|
import blup.animation |
|
import blup.frame |
|
|
|
|
|
class AnimationLoaderThread(threading.Thread): |
|
""" |
|
This class is used by the player to pre-load an animation while another one |
|
is being played. |
|
|
|
""" |
|
|
|
def __init__(self, filename): |
|
threading.Thread.__init__(self) |
|
self.__filename = filename |
|
self.__anim = None |
|
self.__error = False |
|
|
|
def run(self): |
|
try: |
|
self.__anim = blup.animation.load(self.__filename) |
|
except Exception as e: |
|
print('%s: %s while loading %s' % (self.__class__.__name__, |
|
repr(e), self.__filename)) |
|
self.__error = True |
|
self.__anim = None |
|
|
|
def getAnim(self): |
|
self.join() |
|
return self.__anim |
|
|
|
|
|
class MiniPlayer(object): |
|
""" |
|
Minimal animation player that does nothing but randomly selecting |
|
animations out of a directory and playing them. |
|
|
|
""" |
|
|
|
def __init__(self, animDir, output): |
|
self.__animDir = animDir |
|
self.__output = output |
|
self.loopTime = 10000 |
|
self.gap = 800 |
|
self.maxPlayNext = 3 |
|
self.__playNext = [] |
|
self.__running = False |
|
self.__anims = {} |
|
self.__anims_remaining = {} |
|
|
|
def terminate(self): |
|
self.__running = False |
|
|
|
def playNext(self, filename): |
|
""" Add an animation to the queue. """ |
|
|
|
if filename.find('/') >= 0: |
|
raise ValueError('filename must not contain slashes') |
|
|
|
if ( os.path.isfile(os.path.join(self.__animDir, filename)) and |
|
len(self.__playNext) < self.maxPlayNext ): |
|
self.__playNext.append(filename) |
|
else: |
|
return False |
|
|
|
def getNextFilename(self): |
|
""" |
|
Return the next animation filename to be played, either from the queue, |
|
or randomly chosen. |
|
|
|
""" |
|
|
|
if len(self.__playNext) > 0 and os.path.isfile(self.__playNext[0]): |
|
anim = self.__playNext.pop(0) |
|
else: |
|
anims = set() |
|
for dp, dns, fns in os.walk(self.__animDir, followlinks=True): |
|
if os.path.basename(dp).startswith('.'): |
|
continue |
|
for fn in filter(lambda f: not f.startswith('.'), fns): |
|
anims.add(os.path.join(dp, fn)) |
|
|
|
if anims != self.__anims or len(self.__anims_remaining) == 0: |
|
self.__anims = anims |
|
self.__anims_remaining = set(anims) |
|
anim = random.sample(self.__anims_remaining, 1)[0] |
|
self.__anims_remaining.remove(anim) |
|
|
|
return anim |
|
|
|
def run(self): |
|
""" |
|
Runs the player until terminate() gets called (e.g. by another thread). |
|
|
|
""" |
|
|
|
self.__running = True |
|
currentAnim = None |
|
player = blup.animation.AnimationPlayer() |
|
|
|
while self.__running: |
|
|
|
# begin to load the next animation |
|
loader = AnimationLoaderThread(self.getNextFilename()) |
|
loader.start() |
|
|
|
# play the animation in case it had been successfully loaded before |
|
if currentAnim is not None: |
|
if currentAnim.duration < self.loopTime: |
|
count = self.loopTime // currentAnim.duration |
|
else: |
|
count = 1 |
|
player.play(currentAnim, self.__output, count=count) |
|
|
|
# show a blank frame for some thime |
|
if self.gap > 0 and self.__running: |
|
# TODO: use correct frame size |
|
#dim = blup.frame.FrameDimension(18,8,8,3) |
|
dim = blup.frame.FrameDimension(22, 16, 256, 3) |
|
self.__output.sendFrame(blup.frame.Frame(dim)) |
|
time.sleep(self.gap / 1000.0) |
|
|
|
# get the next animation from the loader |
|
currentAnim = loader.getAnim() |
|
|
|
|
|
def printUsage(errMsg=None): |
|
if errMsg is not None: |
|
print('error: %s\n' % (errMsg)) |
|
print('usage: %s [OPTIONS] PATH' % (sys.argv[0])) |
|
print('where PATH is the directory containing the animations to play') |
|
print('supported options:') |
|
print(' -o OUTPUT where to output the frames (default: shell)') |
|
print(' --output OUTPUT\n') |
|
print(' -h print(this text') |
|
print(' --help') |
|
|
|
def main(): |
|
try: |
|
(opts, args) = getopt.gnu_getopt(sys.argv, 'ho:', ['help', 'output=']) |
|
opts = dict(opts) |
|
except getopt.GetoptError as e: |
|
printUsage(e.msg) |
|
sys.exit(1) |
|
|
|
if '--help' in opts: |
|
printUsage() |
|
sys.exit(0) |
|
|
|
if '-o' in opts: |
|
output = opts['-o'] |
|
elif '--output' in opts: |
|
output = opts['--output'] |
|
else: |
|
output = 'shell' |
|
|
|
try: |
|
out = blup.output.getOutput(output) |
|
except blup.output.IllegalOutputSpecificationError: |
|
print('illegal output specification') |
|
print('available outputs:') |
|
print(blup.output.getOutputDescriptions()) |
|
sys.exit(1) |
|
except Exception as e: |
|
print('could not initialize output: %s' % (str(e))) |
|
sys.exit(1) |
|
|
|
if len(args) != 2: |
|
printUsage() |
|
sys.exit(1) |
|
else: |
|
animDir = args[1] |
|
|
|
if not os.path.isdir(animDir): |
|
print('%s is not a directory' % (animDir)) |
|
sys.exit(1) |
|
|
|
p = MiniPlayer(animDir, out) |
|
try: |
|
p.run() |
|
except KeyboardInterrupt: |
|
sys.exit(0) |
|
|
|
if __name__ == '__main__': |
|
main() |
|
|
|
|