audiogen starts working

a decoupled part that will prepare audio. It has a modular design based
on standard python entrypoints.
This allows "kinds" to be registered.
This commit is contained in:
boyska 2014-11-02 02:09:58 +01:00
parent b1f597deae
commit aa71f61425
5 changed files with 181 additions and 1 deletions

42
README.mdwn Normal file
View file

@ -0,0 +1,42 @@
larigira
=========
A radio automation based on MPD. Larigira will sit right to your mpd player and
will keep your playlist never empty. It will also manage a db of "events", so
that you can schedule shows, play jingles every X minutes, etc.
Software stack
---------------
* MPD, of course
* python2
* gevent as an async framework
* flask to provide web interface and rpc
* celery for task dispatching
* ejdb as an embedded database
Why? (aka design features)
-----
Reinventing a player is a bad idea. MPD provides an eccellent base.
Separating the player from "action loops" makes it easy to work on this. For
example, you can stop larigira for some minutes, and the audio will keep
playing. Also, you can replace it.
The "continous playing" part needs to be separated from the "events" part.
`larigira` can be run to perform one, the other, or both the duties.
The "audio generation" part can be used separately by any script that you like.
Installation
-------------
Just run `python setup.py install`. It will, of course, also work in a
virtualenv.
The name
---------
> larigira mai la sbaglia

61
larigira/audiogen.py Normal file
View file

@ -0,0 +1,61 @@
from __future__ import print_function
import sys
import argparse
from pkg_resources import iter_entry_points
import json
import logging
def get_audiogenerator(kind):
'''Messes with entrypoints to return an audiogenerator function'''
points = tuple(iter_entry_points(group='larigira.audiogenerators',
name=kind))
if not points:
raise ValueError('cant find a generator for ', kind)
if len(points) > 1:
logging.warning("Found more than one audiogenerator for '%s'" % kind)
gen = points[0]
return gen.load()
def get_parser():
parser = argparse.ArgumentParser(
description='Generate audio and output paths')
parser.add_argument('audiospec', metavar='AUDIOSPEC', type=str, nargs=1,
help='filename for audiospec, formatted in json')
return parser
def read_spec(fname):
if fname == '-':
return json.load(sys.stdin)
with open(fname) as buf:
return json.load(buf)
def check_spec(spec):
if 'kind' not in spec:
yield "Missing field 'kind'"
def generate(spec):
gen = get_audiogenerator(spec['kind'])
return tuple(gen(spec))
def main():
'''Main function for the "larigira-audiogen" executable'''
args = get_parser().parse_args()
spec = read_spec(args.audiospec[0])
errors = tuple(check_spec(spec))
if errors:
logging.error("Errors in audiospec")
for err in errors:
print(err) # TODO: to stderr
sys.exit(1)
for path in generate(spec):
print(path)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,48 @@
import os
import logging
import shutil
import random
from tempfile import mkstemp
import fnmatch
def scan_dir(dirname, extension=None):
if extension is None:
fnfilter = lambda fnames: fnames
else:
fnfilter = lambda fnames: fnmatch.filter(fnames, extension)
for root, dirnames, filenames in os.walk(dirname):
for fname in fnfilter(filenames):
yield os.path.join(root, fname)
def generate(spec):
'''
resolves audiospec-randomdir
Recognized arguments:
- paths [mandatory] list of source paths
- howmany [mandatory] number of audio files to pick
'''
for attr in ('paths', 'howmany'):
if attr not in spec:
raise ValueError("Malformed audiospec: missing '%s'" % attr)
found_files = set()
for path in spec['paths']:
if not os.path.exists(path):
logging.warn("Can't find requested path: %s" % path)
continue
if os.path.isfile(path):
found_files.add(path)
elif os.path.isdir(path):
found_files.update(scan_dir(path))
picked = random.sample(found_files, int(spec['howmany']))
for path in picked:
tmp = mkstemp(suffix=os.path.splitext(path)[-1],
prefix='audiogen-randomdir-')
os.close(tmp[0])
shutil.copy(path, tmp[1])
yield tmp[1]

View file

@ -0,0 +1,24 @@
import os
import logging
import shutil
from tempfile import mkstemp
def generate(spec):
'''
resolves audiospec-static
Recognized argument is "paths" (list of static paths)
'''
if 'paths' not in spec:
raise ValueError("Malformed audiospec: missing 'paths'")
for path in spec['paths']:
if not os.path.exists(path):
logging.warn("Can't find requested path: %s" % path)
continue
tmp = mkstemp(suffix=os.path.splitext(path)[-1],
prefix='audiogen-static-')
os.close(tmp[0])
shutil.copy(path, tmp[1])
yield tmp[1]

View file

@ -40,6 +40,11 @@ setup(name='larigira',
cmdclass={'test': PyTest},
zip_safe=False,
entry_points={
'console_scripts': ['larigira=larigira.mpc:main']
'console_scripts': ['larigira=larigira.mpc:main',
'larigira-audiogen=larigira.audiogen:main'],
'larigira.audiogenerators': [
'static = larigira.audiogen_static:generate',
'randomdir = larigira.audiogen_randomdir:generate'
]
}
)