larigira package

Submodules

larigira.audioform_http module

class larigira.audioform_http.AudioForm(*args, **kwargs)[source]

Bases: flask_wtf.form.Form

nick = <UnboundField(StringField, ('Audio nick',), {'validators': [<wtforms.validators.Required object>], 'description': 'A simple name to recognize this audio'})>
populate_from_audiospec(audiospec)[source]
submit = <UnboundField(SubmitField, ('Submit',), {})>
urls = <UnboundField(StringField, ('URLs',), {'validators': [<wtforms.validators.Required object>], 'description': 'URL of the file to download'})>
larigira.audioform_http.audio_receive(form)[source]

larigira.audioform_mostrecent module

class larigira.audioform_mostrecent.AudioForm(*args, **kwargs)[source]

Bases: flask_wtf.form.Form

maxage = <UnboundField(StringField, ('Max age',), {'validators': [<wtforms.validators.Required object>], 'description': 'in seconds, or human-readable (like 9w3d12h)'})>
nick = <UnboundField(StringField, ('Audio nick',), {'validators': [<wtforms.validators.Required object>], 'description': 'A simple name to recognize this audio'})>
path = <UnboundField(AutocompleteStringField, ('dl-suggested-dirs', 'Path'), {'validators': [<wtforms.validators.Required object>], 'description': 'Directory to pick file from'})>
populate_from_audiospec(audiospec)[source]
submit = <UnboundField(SubmitField, ('Submit',), {})>
validate_maxage(field)[source]
larigira.audioform_mostrecent.audio_receive(form)[source]

larigira.audioform_randomdir module

class larigira.audioform_randomdir.Form(*args, **kwargs)[source]

Bases: flask_wtf.form.Form

howmany = <UnboundField(IntegerField, ('Number',), {'validators': [<wtforms.validators.Optional object>], 'default': 1, 'description': 'How many songs to be pickedfrom this dir; defaults to 1'})>
nick = <UnboundField(StringField, ('Audio nick',), {'validators': [<wtforms.validators.Required object>], 'description': 'A simple name to recognize this audio'})>
path = <UnboundField(AutocompleteStringField, ('dl-suggested-dirs', 'Path'), {'validators': [<wtforms.validators.Required object>], 'description': 'Full path to source directory'})>
populate_from_audiospec(audiospec)[source]
submit = <UnboundField(SubmitField, ('Submit',), {})>
larigira.audioform_randomdir.receive(form)[source]

larigira.audioform_script module

class larigira.audioform_script.ScriptAudioForm(*args, **kwargs)[source]

Bases: flask_wtf.form.Form

args = <UnboundField(StringField, ('Arguments',), {'description': 'arguments, separated by ";"'})>
name = <UnboundField(AutocompleteStringField, ('dl-suggested-scripts', 'Name'), {'validators': [<wtforms.validators.Required object>], 'description': 'filename (NOT path) of the script'})>
nick = <UnboundField(StringField, ('Audio nick',), {'validators': [<wtforms.validators.Required object>], 'description': 'A simple name to recognize this audio'})>
populate_from_audiospec(audiospec)[source]
submit = <UnboundField(SubmitField, ('Submit',), {})>
validate_name(field)[source]
larigira.audioform_script.scriptaudio_receive(form)[source]

larigira.audioform_static module

class larigira.audioform_static.StaticAudioForm(*args, **kwargs)[source]

Bases: flask_wtf.form.Form

nick = <UnboundField(StringField, ('Audio nick',), {'validators': [<wtforms.validators.Required object>], 'description': 'A simple name to recognize this audio'})>
path = <UnboundField(AutocompleteStringField, ('dl-suggested-files', 'Path'), {'validators': [<wtforms.validators.Required object>], 'description': 'Full path to audio file'})>
populate_from_audiospec(audiospec)[source]
submit = <UnboundField(SubmitField, ('Submit',), {})>
larigira.audioform_static.staticaudio_receive(form)[source]

larigira.audiogen module

larigira.audiogen.audiogenerate(spec)[source]
larigira.audiogen.check_spec(spec)[source]
larigira.audiogen.get_audiogenerator(kind)[source]

Messes with entrypoints to return an audiogenerator function

larigira.audiogen.get_parser()[source]
larigira.audiogen.main()[source]

Main function for the “larigira-audiogen” executable

larigira.audiogen.read_spec(fname)[source]

larigira.audiogen_http module

larigira.audiogen_http.generate(spec)[source]

resolves audiospec-static

Recognized argument is “paths” (list of static paths)

larigira.audiogen_http.put(url, destdir=None, copy=False)[source]

larigira.audiogen_mostrecent module

larigira.audiogen_mostrecent.generate(spec)[source]

resolves audiospec-randomdir

Recognized arguments:
  • path [mandatory] source dir
  • maxage [default=ignored] max age of audio files to pick
larigira.audiogen_mostrecent.get_mtime(fname)[source]
larigira.audiogen_mostrecent.recent_choose(paths, howmany, minepoch)[source]

larigira.audiogen_mpdrandom module

larigira.audiogen_mpdrandom.generate_by_artist(spec)[source]

choose HOWMANY random artists, and for each one choose a random song

larigira.audiogen_randomdir module

larigira.audiogen_randomdir.candidates(paths)[source]
larigira.audiogen_randomdir.generate(spec)[source]

resolves audiospec-randomdir

Recognized arguments:
  • paths [mandatory] list of source paths
  • howmany [default=1] number of audio files to pick

larigira.audiogen_script module

script audiogenerator: uses an external program to generate audio URIs

a script can be any valid executable in $XDG_CONFIG_DIR/larigira/scripts/<name>; for security reasons, it must be executable and owned by the current user. The audiospec can specify arguments to the script, while the environment cannot be customized (again, this is for security reasons).

The script should assume a minimal environment, and being run from /. It must output one URI per line; please remember that URI must be understood from mpd, so file paths are not valid; file:///file/path.ogg is a valid URI instead. The output MUST be UTF-8-encoded. Empty lines will be skipped. stderr will be logged, so please be careful. any non-zero exit code will result in no files being added.and an exception being logged.

larigira.audiogen_script.generate(spec)[source]
Recognized arguments (fields in spec):
  • name [mandatory] script name
  • args [default=empty] arguments, colon-separated

larigira.audiogen_static module

larigira.audiogen_static.generate(spec)[source]

resolves audiospec-static

Recognized argument is “paths” (list of static paths)

larigira.config module

Taken from flask-appconfig

larigira.config.from_envvars(prefix=None, envvars=None, as_json=True)[source]

Load environment variables in a dictionary

Values are parsed as JSON. If parsing fails with a ValueError, values are instead used as verbatim strings.

Parameters:
  • prefix – If None is passed as envvars, all variables from environ starting with this prefix are imported. The prefix is stripped upon import.
  • envvars – A dictionary of mappings of environment-variable-names to Flask configuration names. If a list is passed instead, names are mapped 1:1. If None, see prefix argument.
  • as_json – If False, values will not be parsed as JSON first.
larigira.config.get_conf(prefix='LARIGIRA_')[source]

This is where everyone should get configuration from

larigira.db module

class larigira.db.EventModel(uri)[source]

Bases: object

add_action(action)[source]
add_alarm(alarm)[source]
add_event(alarm, actions)[source]
delete_action(actionid)[source]
delete_alarm(alarmid)[source]
get_action_by_id(action_id)[source]
get_actions_by_alarm(alarm)[source]
get_alarm_by_id(alarm_id)[source]
get_all_actions()[source]
get_all_alarms()[source]
get_all_alarms_expanded()[source]
reload()[source]
update_action(actionid, new_fields={})[source]
update_alarm(alarmid, new_fields={})[source]

larigira.entrypoints_utils module

larigira.entrypoints_utils.get_avail_entrypoints(group)[source]
larigira.entrypoints_utils.get_one_entrypoint(group, kind)[source]

Messes with entrypoints to return an entrypoint of a given group/kind

larigira.event module

class larigira.event.Monitor(parent_queue, conf)[source]

Bases: larigira.eventutils.ParentedLet

Manages timegenerators and audiogenerators for DB events

The mechanism is partially based on ticks, partially on scheduled actions. Ticks are emitted periodically; at every tick, on_tick checks if any event is “near enough”. If an event is near enough, it is “scheduled”: a greenlet is run which will wait for the right time, then generate the audio, then submit to Controller.

The tick mechanism allows for events to be changed on disk: if everything was scheduled immediately, no further changes would be possible. The scheduling mechanism allows for more precision, catching exactly the right time. Being accurate only with ticks would have required very frequent ticks, which is cpu-intensive.

on_tick()[source]

this is called every EVENT_TICK_SECS. Checks every event in the DB (which might be slightly CPU-intensive, so it is advisable to run it in its own greenlet); if the event is “near enough”, schedule it; if it is too far, or already expired, ignore it.

process_action(timespec, audiospecs)[source]

Generate audio and submit it to Controller

schedule(timespec, audiospecs, delta=None)[source]

prepare an event to be run at a specified time with the specified actions; the DB won’t be read anymore after this call.

This means that this call should not be done too early, or any update to the DB will be ignored.

larigira.event_manage module

larigira.event_manage.main()[source]
larigira.event_manage.main_add(args)[source]
larigira.event_manage.main_getaction(args)[source]
larigira.event_manage.main_list(args)[source]

larigira.eventutils module

class larigira.eventutils.ParentedLet(queue)[source]

Bases: gevent._greenlet.Greenlet

ParentedLet is just a helper subclass that will help you when your greenlet main duty is to “signal” things to a parent_queue.

It won’t save you much code, but “standardize” messages and make explicit the role of that greenlet

parent_msg(kind, *args)[source]
send_to_parent(kind, *args)[source]
class larigira.eventutils.Timer(milliseconds, queue)[source]

Bases: larigira.eventutils.ParentedLet

continously sleeps some time, then send a “timer” message to parent

do_business()[source]
parent_msg(kind, *args)[source]

larigira.forms module

larigira.forms.get_audioform(kind)[source]

Messes with entrypoints to return a AudioForm

larigira.forms.get_timeform(kind)[source]

Messes with entrypoints to return a TimeForm

larigira.formutils module

class larigira.formutils.AutocompleteStringField(datalist, *args, **kwargs)[source]

Bases: wtforms.fields.core.StringField

class larigira.formutils.AutocompleteTextInput(datalist=None)[source]

Bases: wtforms.widgets.core.Input

class larigira.formutils.DateTimeInput(input_type=None)[source]

Bases: wtforms.widgets.core.Input

input_type = 'datetime-local'
class larigira.formutils.EasyDateTimeField(label=None, validators=None, **kwargs)[source]

Bases: wtforms.fields.core.Field

a “fork” of DateTimeField which uses HTML5 datetime-local

The format is not customizable, because it is imposed by the HTML5 specification.

This field does not ensure that browser actually supports datetime-local input type, nor does it provide polyfills.

formats = ('%Y-%m-%dT%H:%M:%S', '%Y-%m-%dT%H:%M')
process_formdata(valuelist)[source]

Process data received over the wire from a form.

This will be called during form construction with data supplied through the formdata argument.

Parameters:valuelist – A list of strings to process.
widget = <larigira.formutils.DateTimeInput object>

larigira.fsutils module

larigira.fsutils.is_audio(fname)[source]
larigira.fsutils.multi_fnmatch(fname, extensions)[source]
larigira.fsutils.scan_dir(dirname, extension=None)[source]
larigira.fsutils.scan_dir_audio(dirname, extensions=('mp3', 'oga', 'wav', 'ogg'))[source]
larigira.fsutils.shortname(path)[source]

larigira.larigira module

This module is for the main application logic

class larigira.larigira.Larigira[source]

Bases: object

start()[source]
larigira.larigira.main()[source]
larigira.larigira.on_main_crash(*args, **kwargs)[source]
larigira.larigira.sd_notify(ready=False, status=None)[source]

larigira.mpc module

class larigira.mpc.Controller(conf)[source]

Bases: gevent._greenlet.Greenlet

class larigira.mpc.MPDWatcher(queue, conf, client=None)[source]

Bases: larigira.eventutils.ParentedLet

MPDWatcher notifies parent about any mpd event

do_business()[source]
refresh_client()[source]
class larigira.mpc.Player(conf)[source]

Bases: object

The player contains different mpd-related methods

check_playlist determines whether the playlist is long enough and run audiogenerator accordingly

enqueue receive audios that have been generated by Monitor and (if filters allow it) enqueue it to MPD playlist

check_playlist()[source]
continous_audiospec
enqueue(songs)[source]
enqueue_filter(songs)[source]
larigira.mpc.get_mpd_client(conf)[source]

larigira.test_unused module

larigira.timeform_base module

class larigira.timeform_base.FrequencyAlarmForm(*args, **kwargs)[source]

Bases: flask_wtf.form.Form

end = <UnboundField(EasyDateTimeField, ('End date and time',), {'validators': [<wtforms.validators.Optional object>], 'description': 'After this, no alarm will ring. Expressed as YYYY-MM-DDTHH:MM:SS. If omitted, the alarm will always ring'})>
interval = <UnboundField(StringField, ('Frequency',), {'validators': [<wtforms.validators.Required object>], 'description': 'in seconds, or human-readable (like 9w3d12h)'})>
nick = <UnboundField(StringField, ('Alarm nick',), {'validators': [<wtforms.validators.Required object>], 'description': 'A simple name to recognize this alarm'})>
populate_from_timespec(timespec)[source]
start = <UnboundField(EasyDateTimeField, ('Start date and time',), {'validators': [<wtforms.validators.Optional object>], 'description': 'Before this, no alarm will ring. Expressed as YYYY-MM-DDTHH:MM:SS. If omitted, the alarm will always ring'})>
submit = <UnboundField(SubmitField, ('Submit',), {})>
validate_interval(field)[source]
weekdays = <UnboundField(SelectMultipleField, ('Days on which the alarm should be played',), {'choices': [('1', 'Monday'), ('2', 'Tuesday'), ('3', 'Wednesday'), ('4', 'Thursday'), ('5', 'Friday'), ('6', 'Saturday'), ('7', 'Sunday')], 'default': ['1', '2', '3', '4', '5', '6', '7'], 'validators': [<wtforms.validators.Required object>], 'description': 'The alarm will ring only on selected weekdays'})>
class larigira.timeform_base.SingleAlarmForm(*args, **kwargs)[source]

Bases: flask_wtf.form.Form

dt = <UnboundField(EasyDateTimeField, ('Date and time',), {'validators': [<wtforms.validators.Required object>], 'description': 'Date to ring on, expressed as 2000-12-31T13:42:00'})>
nick = <UnboundField(StringField, ('Alarm nick',), {'validators': [<wtforms.validators.Required object>], 'description': 'A simple name to recognize this alarm'})>
populate_from_timespec(timespec)[source]
submit = <UnboundField(SubmitField, ('Submit',), {})>
larigira.timeform_base.frequencyalarm_receive(form)[source]
larigira.timeform_base.singlealarm_receive(form)[source]

larigira.timeform_cron module

class larigira.timeform_cron.CronAlarmForm(*args, **kwargs)[source]

Bases: flask_wtf.form.Form

cron_format = <UnboundField(StringField, ('cron-like format',), {'validators': [<wtforms.validators.Required object>], 'description': 'the frequency specification, as in the <tt>cron</tt> command; see <a href="https://crontab.guru/">crontab.guru</a> for a hepl with cron format'})>
exclude = <UnboundField(TextAreaField, ('cron-like format; any matching time will be excluded',), {'description': 'Another cron-like thing to _exclude_ events'})>
nick = <UnboundField(StringField, ('Alarm nick',), {'validators': [<wtforms.validators.Required object>], 'description': 'A simple name to recognize this alarm'})>
populate_from_timespec(timespec)[source]
submit = <UnboundField(SubmitField, ('Submit',), {})>
validate_cron_format(field)[source]
validate_exclude(field)[source]
larigira.timeform_cron.cronalarm_receive(form)[source]

larigira.timegen module

main module to read and get informations about alarms

larigira.timegen.check_spec(spec)[source]
larigira.timegen.get_parser()[source]
larigira.timegen.get_timegenerator(kind)[source]

Messes with entrypoints to return an timegenerator function

larigira.timegen.main()[source]

Main function for the “larigira-timegen” executable

larigira.timegen.read_spec(fname)[source]
larigira.timegen.timegenerate(spec, now=None, howmany=1)[source]

larigira.timegen_cron module

class larigira.timegen_cron.CronAlarm(obj)[source]

Bases: larigira.timegen_every.Alarm

description = 'Frequency specified by cron-like format. nerds preferred'
has_ring(current_time=None)[source]

returns True IFF the alarm will ring exactly at time

is_excluded(dt)[source]
next_ring(current_time=None)[source]

if current_time is None, it is now()

returns the next time it will ring; or None if it will not anymore

larigira.timegen_every module

class larigira.timegen_every.Alarm[source]

Bases: object

all_rings(current_time=None)[source]

all future rings this, of course, is an iterator (they could be infinite)

has_ring(time=None)[source]

returns True IFF the alarm will ring exactly at time

next_ring(current_time=None)[source]

if current_time is None, it is now()

returns the next time it will ring; or None if it will not anymore

class larigira.timegen_every.FrequencyAlarm(obj)[source]

Bases: larigira.timegen_every.Alarm

rings on {t | exists a k integer >= 0 s.t. t = start+k*t, start<t<end}

description = 'Events at a specified frequency. Example: every 30minutes'
has_ring(current_time=None)[source]

returns True IFF the alarm will ring exactly at time

next_ring(current_time=None)[source]

if current_time is None, it is now()

class larigira.timegen_every.SingleAlarm(obj)[source]

Bases: larigira.timegen_every.Alarm

rings a single time

description = 'Only once, at a specified date and time'
has_ring(current_time=None)[source]

returns True IFF the alarm will ring exactly at time

next_ring(current_time=None)[source]

if current_time is None, it is now()

larigira.timegen_every.getdate(val)[source]

larigira.unused module

This component will look for files to be removed. There are some assumptions:
  • Only files in $TMPDIR are removed. Please remember that larigira has its own specific TMPDIR
  • MPD URIs are parsed, and only file:/// is supported
class larigira.unused.UnusedCleaner(conf)[source]

Bases: object

check_playlist()[source]

check playlist + internal watchlist to see what can be removed

watch(uri)[source]

adds fpath to the list of “watched” file

as soon as it leaves the mpc playlist, it is removed

larigira.unused.old_commonpath(directories)[source]

Module contents