Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
22588ab1a6 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.
2015-09-30 10:53:27 +02:00
4ee5a52d15 Add the "record" command in radioob
The record command use streamripper as a way to save each track of the stream in a separated file.

Extract check_exec as a member.
2015-09-28 00:46:54 +02:00

View file

@ -19,10 +19,14 @@
from __future__ import print_function
import subprocess
import subprocess # FIXME use subprocess everywhere instead of os.spawn*
import os
import sys
import re
import requests
import signal
import copy
import time
from weboob.capabilities.radio import CapRadio, Radio
from weboob.capabilities.audio import CapAudio, BaseAudio, Playlist, Album
@ -198,14 +202,6 @@ class Radioob(ReplApplication):
audio.url = _obj.url
def check_exec(executable):
with open(os.devnull, 'w') as devnull:
process = subprocess.Popen(['which', executable], stdout=devnull)
if process.wait() != 0:
print('Please install "%s"' % executable, file=self.stderr)
return False
return True
def audio_to_file(_audio):
ext = _audio.ext
if not ext:
@ -220,23 +216,188 @@ class Radioob(ReplApplication):
dest = audio_to_file(audio)
if audio.url.startswith('rtmp'):
if not check_exec('rtmpdump'):
if not self.check_exec('rtmpdump'):
return 1
args = ('rtmpdump', '-e', '-r', audio.url, '-o', dest)
elif audio.url.startswith('mms'):
if not check_exec('mimms'):
if not self.check_exec('mimms'):
return 1
args = ('mimms', '-r', audio.url, dest)
else:
if check_exec('wget'):
if self.check_exec('wget'):
args = ('wget', '-c', audio.url, '-O', dest)
elif check_exec('curl'):
elif self.check_exec('curl'):
args = ('curl', '-C', '-', audio.url, '-o', dest)
else:
return 1
os.spawnlp(os.P_WAIT, args[0], *args)
def check_exec(self, executable):
with open(os.devnull, 'w') as devnull:
process = subprocess.Popen(['which', executable], stdout=devnull)
if process.wait() != 0:
print('Please install "%s"' % executable, file=self.stderr)
return False
return True
def do_record(self, line):
"""
record ID [STREAM]
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)
try:
stream_id = int(stream_id)
except (ValueError, TypeError):
stream_id = 0
obj = self.retrieve_obj(_id)
if obj is None:
print('No object matches with this id:', _id, file=self.stderr)
return 3
if isinstance(obj, Radio):
try:
streams = [obj.streams[stream_id]]
except IndexError:
print('Stream %d not found' % stream_id, file=self.stderr)
return 1
elif isinstance(obj, BaseAudio):
streams = [obj]
else:
streams = obj.tracks_list
if len(streams) == 0:
print('Radio or Audio file not found:', _id, file=self.stderr)
return 3
dest = "records"
return streams, dest
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:
title = stream.title if stream.title else stream.id
dest = '%s' % re.sub('[?:/]', '-', title)
if not os.path.exists(dest):
os.makedirs(dest)
if not os.path.isdir(dest):
print('The destination "%s" is not a directory' % dest, file=self.stderr)
return 5
if not self.check_exec('streamripper'):
print('streamripper not found, please install it', file=self.stderr)
return 1
if background:
# 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
def complete_play(self, text, line, *ignored):
args = line.split(' ')
if len(args) == 2: