audiogen_script.py 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. '''
  2. script audiogenerator: uses an external program to generate audio URIs
  3. a script can be any valid executable in
  4. $XDG_CONFIG_DIR/larigira/scripts/<name>; for security reasons, it must be
  5. executable and owned by the current user. The audiospec can specify arguments
  6. to the script, while the environment cannot be customized (again, this is for
  7. security reasons).
  8. The script should assume a minimal environment, and being run from /. It must
  9. output one URI per line; please remember that URI must be understood from mpd,
  10. so file paths are not valid; file:///file/path.ogg is a valid URI instead.
  11. Empty lines will be skipped. stderr will be logged, so please be careful. any
  12. non-zero exit code will result in no files being added.and an exception being
  13. logged.
  14. '''
  15. import logging
  16. import os
  17. import subprocess
  18. from config import get_conf
  19. log = logging.getLogger('audioscript')
  20. def generate(spec):
  21. '''
  22. Recognized arguments (fields in spec):
  23. - name [mandatory] script name
  24. - args [default=empty] arguments, space-separated
  25. '''
  26. conf = get_conf()
  27. spec.setdefault('args', '')
  28. args = spec['args'].split()
  29. for attr in ('name', ):
  30. if attr not in spec:
  31. raise ValueError("Malformed audiospec: missing '%s'" % attr)
  32. if '/' in spec['name']:
  33. raise ValueError("Script name is a filename, not a path ({} provided)"
  34. .format(spec['name']))
  35. scriptpath = os.path.join(conf['SCRIPTS_PATH'], spec['name'])
  36. if not os.path.exists(scriptpath):
  37. raise ValueError("Script %s not found", spec['name'])
  38. if not os.access(scriptpath, os.R_OK | os.X_OK):
  39. raise ValueError("Insufficient privileges for script %s" % scriptpath)
  40. if os.stat(scriptpath).st_uid != os.getuid():
  41. raise ValueError("Script %s owned by %d, should be owned by %d"
  42. % (spec['name'], os.stat(scriptpath).st_uid,
  43. os.getuid()))
  44. try:
  45. log.info('Going to run {}'.format([scriptpath] + args))
  46. env = dict(
  47. HOME=os.environ['HOME'],
  48. PATH=os.environ['PATH'],
  49. MPD_HOST=conf['MPD_HOST'],
  50. MPD_PORT=str(conf['MPD_PORT'])
  51. )
  52. if 'TMPDIR' in os.environ:
  53. env['TMPDIR'] = os.environ['TMPDIR']
  54. out = subprocess.check_output([scriptpath] + args,
  55. env=env,
  56. cwd='/')
  57. except subprocess.CalledProcessError as exc:
  58. log.error("Error %d when running script %s" %
  59. (exc.returncode, spec['name']))
  60. return []
  61. out = [p for p in out.split('\n') if p]
  62. logging.debug('Script %s produced %d files' % (spec['name'], len(out)))
  63. return out