No more celery, audiogen used for continous, too

readme is now rst, and included in setup.py
This commit is contained in:
boyska 2014-11-03 00:26:32 +01:00
parent aa71f61425
commit 241eb30c7c
7 changed files with 67 additions and 63 deletions

View file

@ -1,6 +1,10 @@
=========
larigira
=========
About
-------
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.
@ -12,31 +16,36 @@ Software stack
* 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.
playing. It also means that you can easily replace specific parts of your radio
automation.
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 "continous playing" part is separated from the "events" part. ``larigira``
can be run to perform one, the other, or both.
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.
Just run ``python setup.py install``. It will, of course, also work in a
virtualenv. Apart from running an MPD server, there is no additional setup.
You will find some command in your PATH now; they all begin with ``larigira``,
so the usual ``<TAB><TAB>`` is a good way to explore them ;)
The name
---------
> larigira mai la sbaglia
larigira mai la sbaglia...
-- https://www.youtube.com/watch?v=K9XJkOSSdEA

View file

@ -0,0 +1,24 @@
import logging
log = logging.getLogger('mpdrandom')
import random
from mpd import MPDClient
def generate_by_artist(spec):
'''choose HOWMANY random artists, and for each one choose a random song'''
for attr in ('howmany',):
if attr not in spec:
raise ValueError("Malformed audiospec: missing '%s'" % attr)
log.info('generating')
c = MPDClient()
c.connect('localhost', 6600) # TODO: read global options somehow
artists = c.list('artist')
log.debug("got %d artists" % len(artists))
if not artists:
raise ValueError("no artists in your mpd database")
for _ in xrange(spec['howmany']):
artist = random.choice(artists)
yield random.choice(c.find('artist', artist))['file']

View file

@ -1,2 +0,0 @@
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
CELERYD_POOL_RESTARTS = True

View file

@ -12,11 +12,13 @@ class ParentedLet(gevent.Greenlet):
def __init__(self, queue):
gevent.Greenlet.__init__(self)
self.parent_queue = queue
self.tracker = None # set this to recognize easily
def parent_msg(self, kind, *args):
return {
'emitter': self,
'class': self.__class__.__name__,
'tracker': self.tracker,
'kind': kind,
'args': args
}

View file

@ -1,10 +1,12 @@
from __future__ import print_function
from gevent import monkey
monkey.patch_all(subprocess=True)
import os
import logging
FORMAT = '%(asctime)s|%(levelname)s[%(name)s:%(lineno)d] %(message)s'
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(message)s',
format=FORMAT,
datefmt='%H:%M:%S')
import signal
@ -13,9 +15,13 @@ from gevent.queue import Queue
from gevent.wsgi import WSGIServer
from mpd import MPDClient
from eventutils import ParentedLet, CeleryTask, Timer
from task import create as create_continous
from eventutils import ParentedLet, Timer
import rpc
from audiogen import generate
CONTINOUS_AUDIODESC = dict(kind='mpd', howmany=1)
MPD_HOST = os.getenv('MPD_HOST', 'localhost')
MPD_PORT = int(os.getenv('MPD_PORT', '6600'))
class MpcWatcher(ParentedLet):
@ -24,13 +30,12 @@ class MpcWatcher(ParentedLet):
if client is None:
self.client = MPDClient()
# TODO: use config values
self.client.connect("localhost", 6600)
self.client.connect(MPD_HOST, MPD_PORT)
else:
self.client = client # assume it is already connected
def do_business(self):
while True:
# status = check_output(['mpc', 'idle']).decode('utf-8').strip()
status = self.client.idle()[0]
logging.info(status)
yield ('mpc', status)
@ -50,8 +55,9 @@ class Player(gevent.Greenlet):
if(len(songs) >= self.min_playlist_length):
return
logging.info('need to add new songs')
CeleryTask(create_continous, self.q).start()
CeleryTask(create_continous, self.q).start()
picker = gevent.Greenlet(generate, CONTINOUS_AUDIODESC)
picker.link_value(lambda g: mpd_client.add(next(g.value).strip()))
picker.start()
def _run(self):
MpcWatcher(self.q, client=None).start()
@ -67,8 +73,8 @@ class Player(gevent.Greenlet):
logging.info('CLOCK')
if kind == 'timer' or (kind == 'mpc' and args[0] == 'playlist'):
gevent.Greenlet.spawn(self.check_playlist)
elif kind == 'celery':
logging.info("celery: %s" % str(args))
elif value['tracker'] == 'mpd_generate':
logging.info("generated! %s" % tuple(args[0]))
else:
logging.warning("Unknown message: %s" % str(value))
logging.info(str(value))

View file

@ -1,41 +0,0 @@
import time
import logging
import random
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(message)s',
datefmt='%H:%M:%S')
from celery import Celery
celery = Celery('hello', backend='redis://localhost',
broker='redis://localhost:6379/0')
@celery.task(name='create_continous')
def create():
sec = random.uniform(2, 5)
time.sleep(sec)
logging.info('hello world')
return 'slept! %.2f' % sec
if __name__ == '__main__':
celery.control.broadcast('pool_restart',
arguments={'reload': True})
res = []
N = 14
def callback(*args, **kwargs):
print(args)
print(kwargs)
print('---')
for i in xrange(N):
print('append', i)
res.append(create.apply_async(expires=2))
for i in xrange(N):
logging.info('wait %d' % i)
val = res[i].get()
logging.info('got %s' % str(val))
time.sleep(30)

View file

@ -1,9 +1,15 @@
import sys
import os
from setuptools import setup
from setuptools.command.test import test as TestCommand
def read(fname):
with open(os.path.join(os.path.dirname(__file__), fname)) as buf:
return buf.read()
class PyTest(TestCommand):
user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]
@ -25,6 +31,7 @@ class PyTest(TestCommand):
setup(name='larigira',
version='0.1',
description='A radio automation based on MPD',
long_description=read('README.rst'),
author='boyska',
author_email='piuttosto@logorroici.org',
license='AGPL',
@ -32,9 +39,7 @@ setup(name='larigira',
install_requires=[
'gevent',
'flask',
'python-mpd2',
'redis',
'celery'
'python-mpd2'
],
tests_require=['pytest'],
cmdclass={'test': PyTest},
@ -43,6 +48,7 @@ setup(name='larigira',
'console_scripts': ['larigira=larigira.mpc:main',
'larigira-audiogen=larigira.audiogen:main'],
'larigira.audiogenerators': [
'mpd = larigira.audiogen_mpdrandom:generate_by_artist',
'static = larigira.audiogen_static:generate',
'randomdir = larigira.audiogen_randomdir:generate'
]