No more celery, audiogen used for continous, too
readme is now rst, and included in setup.py
This commit is contained in:
parent
aa71f61425
commit
241eb30c7c
7 changed files with 67 additions and 63 deletions
|
@ -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
|
||||
|
24
larigira/audiogen_mpdrandom.py
Normal file
24
larigira/audiogen_mpdrandom.py
Normal 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']
|
|
@ -1,2 +0,0 @@
|
|||
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
|
||||
CELERYD_POOL_RESTARTS = True
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
12
setup.py
12
setup.py
|
@ -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'
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue