Add the recordplay command to radioob

Record a stream and serve it, then run a media player on the local stream
(thus no bandwidth is wasted).
The recorder is started in the background but still print on stdout.
Ctr-C ends both the recorder and the player.
This commit is contained in:
Johann Dreo 2015-09-30 10:53:27 +02:00
commit 22588ab1a6

View file

@ -19,10 +19,14 @@
from __future__ import print_function from __future__ import print_function
import subprocess import subprocess # FIXME use subprocess everywhere instead of os.spawn*
import os import os
import sys
import re import re
import requests import requests
import signal
import copy
import time
from weboob.capabilities.radio import CapRadio, Radio from weboob.capabilities.radio import CapRadio, Radio
from weboob.capabilities.audio import CapAudio, BaseAudio, Playlist, Album from weboob.capabilities.audio import CapAudio, BaseAudio, Playlist, Album
@ -245,8 +249,72 @@ class Radioob(ReplApplication):
Record each track of an audio stream in a separate file in a directory. Record each track of an audio stream in a separate file in a directory.
""" """
streams, dest = self.record_parse(line)
if isinstance(streams, int):
return streams
for stream in streams:
self.record_stream(stream, dest, background = False)
def do_recordplay(self, line):
"""
recordplay ID [STREAM]
Record each track of an audio stream in a separate file AND play it at the same time.
This takes care of not wasting bandwidth: only one connection to the stream is used.
"""
streams,dest = self.record_parse(line)
if isinstance(streams, int):
return streams
for stream in streams:
# Start the recorder in the background
# recpid = self.record_stream(stream, dest, background=True)
recpid = self.record_stream(stream, dest, background=True)
time.sleep(1)
self.logger.debug(u'streamripper recorder in background with PID: %i' % recpid)
# Play the local relay server, not the original stream.
relay_port = self.config.get('relay_port')
if not relay_port:
relay_port = 8000
self.logger.debug(u'No relay_port in config file, will use default: %i' % relay_port)
# Build a local stream to avoid changing the stream URL.
# localstream = copy.deepcopy(stream) # FIXME reference kept, why?
# localstream.url = u'http://localhost:%i' % relay_port
class Media:
def __init__(self, url, title, _id):
self.url = url
self.title = title
self.id = _id
localstream = Media(u'http://localhost:%i' % relay_port, stream.title, stream.id)
player_name = self.config.get('media_player')
media_player_args = self.config.get('media_player_args')
if not player_name:
self.logger.debug(u'No media_player in config file, will try to guess.')
try:
self.player.play(localstream, player_name=player_name, player_args=media_player_args)
except KeyboardInterrupt:
self.logger.debug(u'Player closed')
pass
except:
e = sys.exc_info()[0]
self.logger.debug(u'Encountered an error: %s' % e)
raise
finally:
# Once player ends, terminate the recorder too.
self.logger.debug(u'Terminate the recorder')
# os.kill(recpid, signal.SIGTERM)
os.kill(recpid, signal.SIGKILL)
def record_parse(self, line):
_id, stream_id = self.parse_command_args(line, 2, 1) _id, stream_id = self.parse_command_args(line, 2, 1)
dest = stream_id
try: try:
stream_id = int(stream_id) stream_id = int(stream_id)
@ -276,12 +344,18 @@ class Radioob(ReplApplication):
print('Radio or Audio file not found:', _id, file=self.stderr) print('Radio or Audio file not found:', _id, file=self.stderr)
return 3 return 3
for stream in streams: dest = "records"
self.record_stream(stream, dest) return streams, dest
def record_stream(self, stream, dest = None): def record_stream(self, stream, dest, background = False):
"""
Run streamripper on the given stream and save files in the given directory.
If background is False, this function returns the process id of the new process;
if background is True, returns the processs exit code if it exits normally,
or -signal, where signal is the signal that killed the process.
"""
if dest is None: if dest is None:
title = stream.title if stream.title else stream.id title = stream.title if stream.title else stream.id
dest = '%s' % re.sub('[?:/]', '-', title) dest = '%s' % re.sub('[?:/]', '-', title)
@ -293,14 +367,34 @@ class Radioob(ReplApplication):
print('The destination "%s" is not a directory' % dest, file=self.stderr) print('The destination "%s" is not a directory' % dest, file=self.stderr)
return 5 return 5
if self.check_exec('streamripper'): if not self.check_exec('streamripper'):
args = ('streamripper', stream.url, '-d', dest)
else:
print('streamripper not found, please install it', file=self.stderr) print('streamripper not found, please install it', file=self.stderr)
return 1 return 1
self.logger.debug(u'Record stream: "%s" in directory "%s"' % (stream.url, dest)) if background:
os.spawnlp(os.P_WAIT, args[0], *args) # Start a relay server, but do not try to find a free port.
relay_port = self.config.get('relay_port')
if not relay_port:
relay_port = 8000
args = ['streamripper', stream.url, '-d', dest, '-z', '-r', str(relay_port)]
spawn = subprocess.Popen
else:
args = ['streamripper', stream.url, '-d', dest]
spawn = subprocess.call
try:
self.logger.debug(u'Invoking: %s' % (u' '.join([str(a) for a in args])))
proc = spawn(args)
except OSError:
print(u'Failed to start the recorder')
return 7
if proc.returncode:
print('The recorder ended unexpectedly', file=self.stderr)
self.logger.debug(u'streamripper error, pid=%i, code=%i' % (proc.pid, proc.returncode))
return 8
return proc.pid