# -*- coding: utf-8 -*- # Copyright(C) 2010-2012 Romain Bignon # # This file is part of weboob. # # weboob is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # weboob is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with weboob. If not, see . import subprocess import sys import os import re from weboob.capabilities.radio import ICapRadio, Radio from weboob.capabilities.audio import ICapAudio, BaseAudio from weboob.capabilities.base import empty from weboob.tools.application.repl import ReplApplication, defaultcount from weboob.tools.application.media_player import InvalidMediaPlayer, MediaPlayer, MediaPlayerNotFound from weboob.tools.application.formatters.iformatter import PrettyFormatter __all__ = ['Radioob'] class RadioListFormatter(PrettyFormatter): MANDATORY_FIELDS = ('id', 'title', 'description') def get_title(self, obj): return obj.title def get_description(self, obj): result = '%-30s' % obj.description if hasattr(obj, 'current') and not empty(obj.current): if obj.current.who: result += ' (Current: %s - %s)' % (obj.current.who, obj.current.what) else: result += ' (Current: %s)' % obj.current.what return result class Radioob(ReplApplication): APPNAME = 'radioob' VERSION = '0.i' COPYRIGHT = 'Copyright(C) 2010-2013 Romain Bignon\nCopyright(C) 2013 Pierre Maziere' DESCRIPTION = "Console application allowing to search for web radio stations, listen to them and get information " \ "like the current song." SHORT_DESCRIPTION = "search, show or listen to radio stations" CAPS = (ICapRadio, ICapAudio) EXTRA_FORMATTERS = {'radio_list': RadioListFormatter} COMMANDS_FORMATTERS = {'ls': 'radio_list', 'search': 'radio_list', 'playlist': 'radio_list', } COLLECTION_OBJECTS = (Radio, BaseAudio, ) PLAYLIST = [] def __init__(self, *args, **kwargs): ReplApplication.__init__(self, *args, **kwargs) self.player = MediaPlayer(self.logger) def main(self, argv): self.load_config() return ReplApplication.main(self, argv) def complete_download(self, text, line, *ignored): args = line.split(' ') if len(args) == 2: return self._complete_object() elif len(args) >= 3: return self.path_completer(args[2]) def do_download(self, line): """ download ID [FILENAME] Download an audio file """ _id, dest = self.parse_command_args(line, 2, 1) audio = self.get_object(_id, 'get_audio', ['url']) if not audio: print >>sys.stderr, 'Audio file not found: %s' % _id return 3 if not audio.url: print >>sys.stderr, 'Error: the direct URL is not available.' return 4 def check_exec(executable): with open('/dev/null', 'w') as devnull: process = subprocess.Popen(['which', executable], stdout=devnull) if process.wait() != 0: print >>sys.stderr, 'Please install "%s"' % executable return False return True def audio_to_file(_audio): ext = _audio.ext if not ext: ext = 'audiofile' return '%s.%s' % (re.sub('[?:/]', '-', _audio.id), ext) if dest is not None and os.path.isdir(dest): dest += '/%s' % audio_to_file(audio) if dest is None: dest = audio_to_file(audio) if audio.url.startswith('rtmp'): if not check_exec('rtmpdump'): return 1 args = ('rtmpdump', '-e', '-r', audio.url, '-o', dest) elif audio.url.startswith('mms'): if not check_exec('mimms'): return 1 args = ('mimms', '-r', audio.url, dest) else: if check_exec('wget'): args = ('wget', '-c', audio.url, '-O', dest) elif check_exec('curl'): args = ('curl', '-C', '-', audio.url, '-o', dest) else: return 1 os.spawnlp(os.P_WAIT, args[0], *args) def complete_play(self, text, line, *ignored): args = line.split(' ') if len(args) == 2: return self._complete_object() def do_play(self, line): """ play ID [stream_id] Play a radio or a audio file with a found player (optionnaly specify the wanted stream). """ _id, stream_id = self.parse_command_args(line, 2, 1) if not _id: print >>sys.stderr, 'This command takes an argument: %s' % self.get_command_help('play', short=True) return 2 try: stream_id = int(stream_id) except (ValueError,TypeError): stream_id = 0 radio = self.get_object(_id, 'get_radio') audio = self.get_object(_id, 'get_audio') if radio is None and audio is None: print >>sys.stderr, 'Radio or Audio file not found:', _id return 3 if audio is None: try: stream = radio.streams[stream_id] except IndexError: print >>sys.stderr, 'Stream #%d not found' % stream_id return 1 else: stream = audio try: player_name = self.config.get('media_player') media_player_args = self.config.get('media_player_args') if not player_name: self.logger.debug(u'You can set the media_player key to the player you prefer in the radioob ' 'configuration file.') self.player.play(stream, player_name=player_name, player_args=media_player_args) except (InvalidMediaPlayer, MediaPlayerNotFound) as e: print '%s\nRadio URL: %s' % (e, stream.url) def do_playlist(self, line): """ playlist cmd [args] playlist add ID [ID2 ID3 ...] playlist remove ID [ID2 ID3 ...] playlist export [FILENAME] playlist display """ if not line: print >>sys.stderr, 'This command takes an argument: %s' % self.get_command_help('playlist') return 2 cmd, args = self.parse_command_args(line, 2, req_n=1) if cmd == "add": _ids = args.strip().split(' ') for _id in _ids: audio = self.get_object(_id, 'get_audio') if not audio: print >>sys.stderr, 'Audio file not found: %s' % _id return 3 if not audio.url: print >>sys.stderr, 'Error: the direct URL is not available.' return 4 self.PLAYLIST.append(audio) elif cmd == "remove": _ids = args.strip().split(' ') for _id in _ids: audio_to_remove = self.get_object(_id, 'get_audio') if not audio_to_remove: print >>sys.stderr, 'Audio file not found: %s' % _id return 3 if not audio_to_remove.url: print >>sys.stderr, 'Error: the direct URL is not available.' return 4 for audio in self.PLAYLIST: if audio.id == audio_to_remove.id: self.PLAYLIST.remove(audio) break elif cmd == "export": filename = "playlist.m3u" if args: filename = args file = open(filename, 'w') for audio in self.PLAYLIST: file.write('%s\r\n' % audio.url) file.close() elif cmd == "display": for audio in self.PLAYLIST: self.cached_format(audio) else: print >>sys.stderr, 'Playlist command only support "add", "remove", "display" and "export" arguments.' return 2 def complete_info(self, text, line, *ignored): args = line.split(' ') if len(args) == 2: return self._complete_object() def do_info(self, _id): """ info ID Get information about a radio or an audio file. """ if not _id: print >>sys.stderr, 'This command takes an argument: %s' % self.get_command_help('info', short=True) return 2 radio = self.get_object(_id, 'get_radio') audio = self.get_object(_id, 'get_audio') if radio is None and audio is None: print >>sys.stderr, 'Radio or Audio file not found:', _id return 3 if audio is None: self.format(radio) else: self.format(audio) @defaultcount(10) def do_search(self, pattern=None): """ search PATTERN List radios matching a PATTERN. If PATTERN is not given, this command will list all the radios. """ self.set_formatter_header(u'Search pattern: %s' % pattern if pattern else u'All radios') self.change_path([u'search']) for backend, radio in self.do('iter_radios_search', pattern=pattern): self.add_object(radio) self.format(radio) for backend, audio in self.do('search_audio', pattern=pattern): self.add_object(audio) self.format(audio) def do_ls(self, line): """ ls List radios """ ret = super(Radioob, self).do_ls(line) return ret