Projects
Multimedia
pulseaudio-dlna
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 11
View file
pulseaudio-dlna.changes
Changed
@@ -1,4 +1,22 @@ ------------------------------------------------------------------- +Fri Oct 23 21:00:31 UTC 2015 - antoine.belvire@laposte.net + +- Update to 0.4.6: + * Add support for Google Chromecast Audio (thanks to + leonhandreke) + * Fix a bug where devices which does not specifiy control urls + made the application crash + * Add the --disable-device-stop flag + * Add the --request-timeout option + * You can now also add rules to renderers (e.g. + DISABLE_DEVICE_STOP, REQUEST_TIMEOUT) + * Fix a bug where stream urls where not parsed correctly + * Fix a bug which made a Chomecast Audio throwing exceptions + while stopping + * Fix a bug where the system's default encoding could not be + determined when piping the applications output + +------------------------------------------------------------------- Mon Sep 21 18:31:49 UTC 2015 - antoine.belvire@laposte.net - Update to 0.4.5.2:
View file
pulseaudio-dlna.spec
Changed
@@ -17,7 +17,7 @@ Name: pulseaudio-dlna -Version: 0.4.5.2 +Version: 0.4.6 Release: 0 Summary: A DLNA server which brings DLNA/UPnP support to PulseAudio License: GPL-3.0 @@ -52,7 +52,7 @@ Chromecast support to PulseAudio and Linux. It can stream your current PulseAudio playback to different UPnP devices (UPnP -Media Renderers) in your network. It's main goals are: easy to use, no +Media Renderers) in your network. Its main goals are: easy to use, no configuration hassle, no big dependencies. %prep
View file
pulseaudio-dlna-0.4.5.2.tar.gz/README.md -> pulseaudio-dlna-0.4.6.tar.gz/README.md
Changed
@@ -36,6 +36,16 @@ ## Changelog ## + * __0.4.6__ - (_2015-10-17_) + - Added support for _Google Chromecast Audio_ (thanks to [leonhandreke](https://github.com/leonhandreke)) + - Fixed a bug where devices which does not specifiy control urls made the application crash + - Added the `--disable-device-stop` flag + - Added the `--request-timeout` option + - You can now also add rules to renderers (e.g. `DISABLE_DEVICE_STOP`, `REQUEST_TIMEOUT`) + - Fixed a bug where stream urls where not parsed correctly + - Fixed a bug which made a Chomecast Audio throwing exceptions while stopping + - Fixed a bug where the system's default encoding could not be determined when piping the applications output + * __0.4.5.2__ - (_2015-09-21_) - Fixed a bug where the encoding of SSDP headers was not detected correctly (new dependency: `python-chardet`) @@ -295,7 +305,7 @@ ### CLI ### Usage: - pulseaudio-dlna [--host <host>] [--port <port>] [--encoder <encoders>] [--bit-rate=<rate>] [--filter-device=<filter-device>] [--renderer-urls <urls>] [--debug] [--fake-http10-content-length] [--fake-http-content-length] [--disable-switchback] [--disable-ssdp-listener] + pulseaudio-dlna [--host <host>] [--port <port>] [--encoder <encoders>] [--bit-rate=<rate>] [--filter-device=<filter-device>] [--renderer-urls <urls>] [--request-timeout <timeout>] [--debug] [--fake-http10-content-length] [--fake-http-content-length] [--disable-switchback] [--disable-ssdp-listener] [--disable-device-stop] pulseaudio-dlna [--create-device-config] pulseaudio-dlna [-h | --help | --version] @@ -324,10 +334,12 @@ Devices which get discovered, but won't match the filter text will be skipped. --renderer-urls=<urls> Set the renderer urls yourself. no discovery will commence. + --request-timeout=<timeout> Set the timeout for requests in seconds [default: 10]. --debug enables detailed debug messages. --fake-http-content-length If set, the content-length of HTTP requests will be set to 100 GB. --disable-switchback If set, streams won't switched back to the default sink if a device disconnects. --disable-ssdp-listener If set, the application won't bind to the port 1900 and therefore the automatic discovery of new devices won't work. + --disable-device-stop If set, the application won't send any stop commands to renderers at all -v --version Show the version. -h --help Show the help. @@ -374,6 +386,7 @@ ```json "uuid:e4572d54-c2c7-d491-1eb3-9cf17cf5fe01": { + "rules": [], "flavour": "DLNA", "name": "Device name", "codecs": [ @@ -438,6 +451,7 @@ ```json "uuid:e4572d54-c2c7-d491-1eb3-9cf17cf5fe01": { + "rules": [], "flavour": "DLNA", "name": "Living Room", "codecs": [ @@ -461,6 +475,7 @@ ```json "uuid:e4572d54-c2c7-d491-1eb3-9cf17cf5fe01": { + "rules": [], "flavour": "DLNA", "name": "Living Room", "codecs": [ @@ -547,6 +562,7 @@ Majik DSM | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: [Pi MusicBox](http://www.woutervanwijk.nl/pimusicbox/) | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: Google Chromecast | :white_check_mark: | :white_check_mark: | :white_check_mark: | :no_entry_sign: | :white_check_mark: | :no_entry_sign: | :no_entry_sign: +Google Chromecast Audio | :white_check_mark: | :white_check_mark: | :white_check_mark: | :no_entry_sign: | :white_check_mark: | :no_entry_sign: | :no_entry_sign: Sonos PLAY:1 | :white_check_mark:<sup>3</sup> | :white_check_mark: | :white_check_mark:<sup>3</sup> | :white_check_mark: | :no_entry_sign: | :no_entry_sign: | :grey_question: Sonos PLAY:3 | :white_check_mark:<sup>3</sup> | :white_check_mark: | :white_check_mark:<sup>3</sup> | :white_check_mark: | :no_entry_sign: | :no_entry_sign: | :grey_question: Hame Soundrouter | :white_check_mark:<sup>1</sup> | :no_entry_sign: | :no_entry_sign: | :white_check_mark:<sup>1</sup> | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: @@ -557,6 +573,14 @@ Pure Jongo S3 | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: [Volumio](http://volumio.org) | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: Logitech Media Server | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: +Panasonic TX-50CX680W | :white_check_mark: | :white_check_mark: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: +Yamaha CRX-N560D <sup>4</sup> | :white_check_mark: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: +RaidSonic IB-MP401Air | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: +Medion P85055 | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: +Onkyo TX-NR509 | :grey_question: | :white_check_mark: | :grey_question: | :no_entry_sign: | :grey_question: | :grey_question: | :grey_question: +Denon AVR-3808 | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: +DAMAI Airmusic | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: +Panasonic TX-50CX680W | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: | :grey_question: <sup>1</sup>) Works when specifing the `--fake-http-content-length` flag @@ -564,6 +588,8 @@ <sup>3</sup>) Works since _0.4.5_ (`--fake-http-content-length` is added automatic) +<sup>4</sup>) The device needs to be in _SERVER_ mode to accept instructions + ## Supported encoders ## Encoder | Description | Identifier
View file
pulseaudio-dlna-0.4.5.2.tar.gz/debian/changelog -> pulseaudio-dlna-0.4.6.tar.gz/debian/changelog
Changed
@@ -1,3 +1,21 @@ +pulseaudio-dlna (0.4.6) trusty; urgency=low + + * Added support for Google Chromecast Audio + * Fixed a bug where devices which does not specifiy control urls made the + application crash + * Added the --disable-device-stop flag + * Added the --request-timeout option + * You can now also add rules to renderers + (e.g. DISABLE_DEVICE_STOP, REQUEST_TIMEOUT) + * Fixed a bug where stream urls where not parsed correctly + * Fixed a bug which made a Chomecast Audio throwing exceptions while + stopping + * Fixed a bug where the system's default encoding could not be determined + when piping the applications output + + -- Massimo Mund <mo@lancode.de> Sat, 17 Oct 2015 10:49:13 +0100 + + pulseaudio-dlna (0.4.5.2) trusty; urgency=low * Fixed a bug where the encoding of SSDP headers was not detected correctly
View file
pulseaudio-dlna-0.4.5.2.tar.gz/debian/pulseaudio-dlna.1 -> pulseaudio-dlna-0.4.6.tar.gz/debian/pulseaudio-dlna.1
Changed
@@ -1,11 +1,13 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1. -.TH PULSEAUDIO-DLNA "1" "September 2015" "pulseaudio-dlna 0.4.5.2" "User Commands" +.TH PULSEAUDIO-DLNA "1" "October 2015" "pulseaudio-dlna 0.4.6" "User Commands" .SH NAME -pulseaudio-dlna \- manual page for pulseaudio-dlna 0.4.5.2 +pulseaudio-dlna \- manual page for pulseaudio-dlna 0.4.6 .SH DESCRIPTION .SS "Usage:" +.TP +pulseaudio\-dlna [\-\-host <host>] [\-\-port <port>] [\-\-encoder <encoders>] [\-\-bit\-rate=<rate>] [\-\-filter\-device=<filter\-device>] [\-\-renderer\-urls <urls>] +[\-\-request\-timeout <timeout>] [\-\-debug] [\-\-fake\-http10\-content\-length] [\-\-fake\-http\-content\-length] [\-\-disable\-switchback] [\-\-disable\-ssdp\-listener] [\-\-disable\-device\-stop] .IP -pulseaudio\-dlna [\-\-host <host>] [\-\-port <port>] [\-\-encoder <encoders>] [\-\-bit\-rate=<rate>] [\-\-filter\-device=<filter\-device>] [\-\-renderer\-urls <urls>] [\-\-debug] [\-\-fake\-http10\-content\-length] [\-\-fake\-http\-content\-length] [\-\-disable\-switchback] [\-\-disable\-ssdp\-listener] pulseaudio\-dlna [\-\-create\-device\-config] pulseaudio\-dlna [\-h | \fB\-\-help\fR | \fB\-\-version]\fR .SH OPTIONS @@ -65,6 +67,9 @@ \fB\-\-renderer\-urls=\fR<urls> Set the renderer urls yourself. no discovery will commence. .TP +\fB\-\-request\-timeout=\fR<timeout> +Set the timeout for requests in seconds [default: 10]. +.TP \fB\-\-debug\fR enables detailed debug messages. .TP @@ -77,6 +82,9 @@ \fB\-\-disable\-ssdp\-listener\fR If set, the application won't bind to the port 1900 and therefore the automatic discovery of new devices won't work. .TP +\fB\-\-disable\-device\-stop\fR +If set, the application won't send any stop commands to renderers at all +.TP \fB\-v\fR \fB\-\-version\fR Show the version. .TP
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/__main__.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/__main__.py
Changed
@@ -17,7 +17,7 @@ ''' Usage: - pulseaudio-dlna [--host <host>] [--port <port>] [--encoder <encoders>] [--bit-rate=<rate>] [--filter-device=<filter-device>] [--renderer-urls <urls>] [--debug] [--fake-http10-content-length] [--fake-http-content-length] [--disable-switchback] [--disable-ssdp-listener] + pulseaudio-dlna [--host <host>] [--port <port>] [--encoder <encoders>] [--bit-rate=<rate>] [--filter-device=<filter-device>] [--renderer-urls <urls>] [--request-timeout <timeout>] [--debug] [--fake-http10-content-length] [--fake-http-content-length] [--disable-switchback] [--disable-ssdp-listener] [--disable-device-stop] pulseaudio-dlna [--create-device-config] pulseaudio-dlna [-h | --help | --version] @@ -46,10 +46,12 @@ Devices which get discovered, but won't match the filter text will be skipped. --renderer-urls=<urls> Set the renderer urls yourself. no discovery will commence. + --request-timeout=<timeout> Set the timeout for requests in seconds [default: 10]. --debug enables detailed debug messages. --fake-http-content-length If set, the content-length of HTTP requests will be set to 100 GB. --disable-switchback If set, streams won't switched back to the default sink if a device disconnects. --disable-ssdp-listener If set, the application won't bind to the port 1900 and therefore the automatic discovery of new devices won't work. + --disable-device-stop If set, the application won't send any stop commands to renderers at all -v --version Show the version. -h --help Show the help.
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/application.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/application.py
Changed
@@ -132,6 +132,8 @@ logger.info('Encoder settings:') for _type in pulseaudio_dlna.encoders.ENCODERS: + _type.AVAILABLE = False + for _type in pulseaudio_dlna.encoders.ENCODERS: encoder = _type() encoder.validate() logger.info(' {}'.format(encoder)) @@ -162,6 +164,10 @@ if options['--disable-ssdp-listener']: disable_ssdp_listener = True + disable_device_stop = False + if options['--disable-device-stop']: + disable_device_stop = True + try: stream_server = pulseaudio_dlna.streamserver.ThreadedStreamServer( host, port, bridges, message_queue, @@ -177,6 +183,7 @@ pulse = pulseaudio_dlna.pulseaudio.PulseWatcher( bridges, message_queue, disable_switchback=disable_switchback, + disable_device_stop=disable_device_stop, ) device_filter = None @@ -187,6 +194,12 @@ if options['--renderer-urls']: locations = options['--renderer-urls'].split(',') + if options['--request-timeout']: + request_timeout = float(options['--request-timeout']) + if request_timeout > 0: + pulseaudio_dlna.plugins.renderer.BaseRenderer.REQUEST_TIMEOUT = \ + request_timeout + try: stream_server_address = stream_server.ip, stream_server.port ssdp_listener = pulseaudio_dlna.listener.ThreadedSSDPListener(
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/discover.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/discover.py
Changed
@@ -71,4 +71,4 @@ def _header_received(self, header, address): logger.debug('Recieved the following SSDP header: \n{header}'.format( header=header)) - self.renderer_holder.add_from_search(header) + self.renderer_holder.process_msearch_request(header)
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/encoders.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/encoders.py
Changed
@@ -41,7 +41,7 @@ class BaseEncoder(object): - AVAILABLE = False + AVAILABLE = True def __init__(self): self._binary = None
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/listener.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/listener.py
Changed
@@ -65,7 +65,7 @@ self.socket.setsockopt( socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, multicast) if self.renderer_urls is not None: - self.renderers_holder.add_renderers_by_url(self.renderer_urls) + self.renderers_holder.process_locations(self.renderer_urls) else: discover = RendererDiscover(self.renderers_holder) discover.search()
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/plugins/chromecast/pycastv2/__init__.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/plugins/chromecast/pycastv2/__init__.py
Changed
@@ -30,6 +30,10 @@ pass +class TimeoutException(Exception): + pass + + class ChannelController(object): def __init__(self, socket): self.request_id = 1 @@ -76,6 +80,10 @@ applications.get('sessionId') or self.session_id) self.app_id = ( applications.get('appId') or self.app_id) + else: + self.transport_id = 'receiver-0' + self.session_id = None + self.app_id = None elif response_type == 'PING': self.socket.send(commands.PongCommand()) elif response_type == 'CLOSE': @@ -107,66 +115,54 @@ class ChromecastController(): APP_BACKDROP = 'E8C28D3C' + WAIT_INTERVAL = 0.1 - def __init__(self, ip, max_retries=15): - self.max_retries = max_retries + def __init__(self, ip, timeout=10): + self.timeout = timeout self.socket = cast_socket.CastSocket(ip) self.channel_controller = ChannelController(self.socket) def is_app_running(self, app_id): - if self.channel_controller.app_id is None: - return False return self.channel_controller.app_id == app_id def launch_application(self, app_id): if not self.is_app_running(app_id): self.socket.send(commands.LaunchCommand(app_id)) - retries = 0 + start_time = time.time() while not self.is_app_running(app_id): - if retries > self.max_retries: - return False self.socket.send_and_wait(commands.StatusCommand()) - time.sleep(1) - retries += 1 - return True + current_time = time.time() + if current_time - start_time > self.timeout: + raise TimeoutException() + time.sleep(self.WAIT_INTERVAL) else: logger.debug('Starting not necessary. Application is running ...') - return True def stop_application(self): if not self.is_app_running(self.APP_BACKDROP): self.socket.send(commands.StopCommand()) - retries = 0 - while not self.is_app_running(self.APP_BACKDROP): - if retries > self.max_retries: - return False + start_time = time.time() + while not self.is_app_running(None): self.socket.send_and_wait(commands.StatusCommand()) - time.sleep(1) - retries += 1 - return True + current_time = time.time() + if current_time - start_time > self.timeout: + raise TimeoutException() + time.sleep(self.WAIT_INTERVAL) else: logger.debug('Stop not necessary. Backdrop is running ...') - return True def disconnect_application(self): if not self.is_app_running(self.APP_BACKDROP): self.socket.send(commands.CloseCommand(destination_id=False)) - try: - retries = 0 - while not self.is_app_running(self.APP_BACKDROP): - if retries > self.max_retries: - return False - self.socket.send_and_wait(commands.StatusCommand()) - time.sleep(1) - retries += 1 - return True - except ChannelClosedException: - logger.info('Connection was closed. I guess another ' - 'client has attached to it.') - return True + start_time = time.time() + while not self.is_app_running(None): + self.socket.send_and_wait(commands.StatusCommand()) + current_time = time.time() + if current_time - start_time > self.timeout: + raise TimeoutException() + time.sleep(self.WAIT_INTERVAL) else: logger.debug('Closing not necessary. Backdrop is running ...') - return True def wait(self, timeout): self.socket.wait(timeout) @@ -215,8 +211,8 @@ PLAYER_STATE_PAUSED = 'PAUSED' PLAYER_STATE_IDLE = 'IDLE' - def __init__(self, ip): - ChromecastController.__init__(self, ip) + def __init__(self, ip, timeout=10): + ChromecastController.__init__(self, ip, timeout) self.media_session_id = None self.current_time = None self.media = None
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/plugins/chromecast/pycastv2/example.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/plugins/chromecast/pycastv2/example.py
Changed
@@ -28,5 +28,9 @@ mc.wait(10) mc.disconnect_application() # mc.stop_application() +except pycastv2.ChannelClosedException: + print('Channel was closed.') +except pycastv2.TimeoutException: + print('Request timed out.') finally: mc.cleanup()
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/plugins/chromecast/renderer.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/plugins/chromecast/renderer.py
Changed
@@ -31,6 +31,9 @@ logger = logging.getLogger('pulseaudio_dlna.plugins.chromecast.renderer') +CHROMECAST_MODEL_NAMES = ['Eureka Dongle', 'Chromecast Audio'] + + class ChromecastRenderer(pulseaudio_dlna.plugins.renderer.BaseRenderer): def __init__(self, name, ip, udn, model_name, model_number, manufacturer): @@ -45,7 +48,7 @@ def activate(self, config): if config: - self.set_codecs_from_config(config) + self.set_rules_from_config(config) else: self.codecs = [ pulseaudio_dlna.codecs.Mp3Codec(), @@ -56,7 +59,7 @@ def _get_media_player(self): try: - return pycastv2.MediaPlayerController(self.ip) + return pycastv2.MediaPlayerController(self.ip, self.REQUEST_TIMEOUT) except socket.error as e: if e.errno == 111: logger.info( @@ -69,24 +72,40 @@ def play(self, url): cast = self._get_media_player() if cast is None: + logger.error('No device was found!') return 500 try: - if cast.load(url, self.codec.mime_type) is True: - self.state = self.PLAYING - return 200 - return 500 + cast.load(url, self.codec.mime_type) + self.state = self.PLAYING + return 200 + except pycastv2.ChannelClosedException: + logger.info('Connection was closed. I guess another ' + 'client is attached to it.') + return 423 + except pycastv2.TimeoutException: + logger.error('PLAY command - Could no connect to "{device}". ' + 'Connection timeout.'.format(device=self.label)) + return 408 finally: cast.cleanup() def stop(self): cast = self._get_media_player() if cast is None: + logger.error('No device was found!') return 500 try: self.state = self.IDLE - if cast.disconnect_application() is True: - return 200 - return 500 + cast.disconnect_application() + return 200 + except pycastv2.ChannelClosedException: + logger.info('Connection was closed. I guess another ' + 'client is attached to it.') + return 423 + except pycastv2.TimeoutException: + logger.error('STOP command - Could no connect to "{device}". ' + 'Connection timeout.'.format(device=self.label)) + return 408 finally: cast.cleanup() @@ -118,12 +137,12 @@ 'Could no connect to {url}. ' 'Connection refused.'.format(url=url)) return None - soup = BeautifulSoup.BeautifulSoup(response.content) + soup = BeautifulSoup.BeautifulSoup(response.content.decode('utf-8')) url_object = urlparse.urlparse(url) ip, port = url_object.netloc.split(':') try: model_name = soup.root.device.modelname.text - if model_name.strip() != 'Eureka Dongle': + if model_name.strip() not in CHROMECAST_MODEL_NAMES: logger.info( 'The Chromecast seems not to be an original Chromecast! ' 'Model name: "{model_name}" Skipping device ...'.format(
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/plugins/renderer.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/plugins/renderer.py
Changed
@@ -26,6 +26,7 @@ import base64 import pulseaudio_dlna.pulseaudio +import pulseaudio_dlna.rules logger = logging.getLogger('pulseaudio_dlna.plugins.renderer') @@ -42,6 +43,8 @@ PAUSE = 'paused' STOP = 'stopped' + REQUEST_TIMEOUT = 10 + def __init__(self, udn, model_name=None, model_number=None, manufacturer=None): self._udn = udn @@ -58,6 +61,7 @@ self._encoder = None self._flavour = None self._codecs = [] + self._rules = pulseaudio_dlna.rules.Rules() @property def udn(self): @@ -167,9 +171,20 @@ def codecs(self, value): self._codecs = value + @property + def rules(self): + return self._rules + + @rules.setter + def rules(self, value): + self._rules = value + def activate(self): pass + def validate(self): + return True + def play(self): raise NotImplementedError() @@ -202,6 +217,11 @@ self.codecs.sort(key=sorting_algorithm, reverse=True) def check_for_device_rules(self): + for rule in self.rules: + if type(rule) is pulseaudio_dlna.rules.REQUEST_TIMEOUT: + self.REQUEST_TIMEOUT = rule.timeout + + def check_for_codec_rules(self): if self.manufacturer == 'Sonos, Inc.': for codec in self.codecs: if type(codec) in [ @@ -214,8 +234,10 @@ if type(codec) is pulseaudio_dlna.codecs.WavCodec: codec.mime_type = 'audio/mpeg' - def set_codecs_from_config(self, config): + def set_rules_from_config(self, config): self.name = config['name'] + for rule in config.get('rules', []): + self.rules.append(rule) for codec_properties in config.get('codecs', []): codec_type = pulseaudio_dlna.codecs.CODECS[ codec_properties['identifier']] @@ -227,6 +249,7 @@ for rule in codec_properties.get('rules', []): codec.rules.append(rule) self.codecs.append(codec) + self.check_for_device_rules() logger.debug( 'Loaded the following device configuration:\n{}'.format( self.__str__(True))) @@ -247,7 +270,7 @@ def __str__(self, detailed=False): return ( '<{} name="{}" short="{}" state="{}" udn="{}" model_name="{}" ' - 'model_number="{}" manufacturer="{}">{}').format( + 'model_number="{}" manufacturer="{}" timeout="{}">{}{}').format( self.__class__.__name__, self.name, self.short_name, @@ -256,6 +279,10 @@ self.model_name, self.model_number, self.manufacturer, + self.REQUEST_TIMEOUT, + ('\n' if len(self.rules) > 0 else '') + '\n'.join( + [' - ' + str(rule) for rule in self.rules] + ) if detailed else '', '\n' + '\n'.join([ ' ' + codec.__str__(detailed) for codec in self.codecs ]) if detailed else '', @@ -266,6 +293,7 @@ 'name': self.name, 'flavour': self.flavour, 'codecs': self.codecs, + 'rules': self.rules, } @@ -287,7 +315,7 @@ 'udn': self.udn, } data_string = ','.join( - ['{}={}'.format(k, v) for k, v in settings.iteritems()]) + ['{}="{}"'.format(k, v) for k, v in settings.iteritems()]) stream_name = '/{base_string}/stream.{suffix}'.format( base_string=urllib.quote(base64.b64encode(data_string)), suffix=self.codec.suffix,
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/plugins/upnp/renderer.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/plugins/upnp/renderer.py
Changed
@@ -125,7 +125,6 @@ class UpnpMediaRenderer(pulseaudio_dlna.plugins.renderer.BaseRenderer): ENCODING = 'utf-8' - REQUEST_TIMEOUT = 10 def __init__( self, name, ip, port, udn, model_name, model_number, manufacturer, @@ -155,10 +154,28 @@ def activate(self, config): if config: - self.set_codecs_from_config(config) + self.set_rules_from_config(config) else: self.get_protocol_info() + def validate(self): + if self.service_transport is None: + logger.info( + 'The device "{}" does not specify a service transport url. ' + 'Device skipped!'.format(self.label)) + return False + if self.service_connection is None: + logger.info( + 'The device "{}" does not specify a service connection url. ' + 'Device skipped!'.format(self.label)) + return False + if self.service_rendering is None: + logger.info( + 'The device "{}" does not specify a service rendering url. ' + 'Device skipped!'.format(self.label)) + return False + return True + def _load_xml_files(self): content = {} self.xml_files = { @@ -263,7 +280,7 @@ attributes = sink.strip().split(':') if len(attributes) >= 4: self.add_mime_type(attributes[2]) - self.check_for_device_rules() + self.check_for_codec_rules() self.prioritize_codecs() except IndexError: logger.error( @@ -366,10 +383,12 @@ def play(self, url=None, codec=None): try: stream_url = url or self.get_stream_url() - if UpnpMediaRenderer.register(self, stream_url, codec) == 200: + return_code = UpnpMediaRenderer.register(self, stream_url, codec) + if return_code == 200: return UpnpMediaRenderer.play(self) else: logger.error('"{}" registering failed!'.format(self.name)) + return return_code except requests.exceptions.ConnectionError: logger.error('The device refused the connection!') return 404
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/pulseaudio.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/pulseaudio.py
Changed
@@ -34,6 +34,7 @@ import pulseaudio_dlna.plugins.renderer import pulseaudio_dlna.notification +import pulseaudio_dlna.utils.encoding logger = logging.getLogger('pulseaudio_dlna.pulseaudio') @@ -394,7 +395,8 @@ ASYNC_EXECUTION = True - def __init__(self, bridges_shared, message_queue, disable_switchback=False): + def __init__(self, bridges_shared, message_queue, disable_switchback=False, + disable_device_stop=False): PulseAudio.__init__(self) self.bridges = [] @@ -406,6 +408,7 @@ self.signal_timers = {} self.disable_switchback = disable_switchback + self.disable_device_stop = disable_device_stop def terminate(self, signal_number=None, frame=None): self.cleanup() @@ -494,17 +497,22 @@ def switch_back(self, bridge, reason): title = 'Device "{label}"'.format(label=bridge.device.label) - message = ('{reason}. Your streams were switched ' - 'back to <b>{name}</b>'.format( - reason=reason, - name=(self.fallback_sink.label).encode( - locale.getpreferredencoding()))) - pulseaudio_dlna.notification.show(title, message) - - self._block_device_handling(bridge.sink.object_path) - if bridge.sink == self.default_sink: - self.fallback_sink.set_as_default_sink() - bridge.sink.switch_streams_to_fallback_source() + if self.fallback_sink: + message = ('{reason}. Your streams were switched ' + 'back to <b>{name}</b>'.format( + reason=reason, + name=pulseaudio_dlna.utils.encoding.encode_default( + self.fallback_sink.label))) + pulseaudio_dlna.notification.show(title, message) + + self._block_device_handling(bridge.sink.object_path) + if bridge.sink == self.default_sink: + self.fallback_sink.set_as_default_sink() + bridge.sink.switch_streams_to_fallback_source() + else: + message = ('Your streams could not get switched back because you ' + 'did not set a default sink in pulseaudio.') + pulseaudio_dlna.notification.show(title, message) def on_bridge_disconnected(self, stopped_bridge): @@ -593,7 +601,9 @@ for bridge in self.bridges: logger.debug('\n{}'.format(bridge)) if bridge.device.state == bridge.device.PLAYING: - if len(bridge.sink.streams) == 0: + if len(bridge.sink.streams) == 0 and ( + not self.disable_device_stop and + 'DISABLE_DEVICE_STOP' not in bridge.device.rules): logger.info( 'Instructing the device "{}" to stop ...'.format( bridge.device.label))
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/renderers.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/renderers.py
Changed
@@ -40,15 +40,7 @@ self.message_queue = message_queue self.lock = threading.Lock() for plugin in plugins: - self._register(plugin.st_header, plugin) - - def add_renderers_by_url(self, locations): - for plugin in self.registered.values(): - for device in plugin.lookup(locations): - self._add_renderer(device.udn, device) - - def _register(self, identifier, _type): - self.registered[identifier] = _type + self.registered[plugin.st_header] = plugin def _retrieve_header_map(self, header): header = re.findall(r"(?P<name>.*?): (?P<value>.*?)\n", header) @@ -63,36 +55,12 @@ return match.group(1) return None - def _add_renderer_with_filter_check(self, device_id, device): - if self.device_filter is None or device.name in self.device_filter: - self._add_renderer(device_id, device) - else: - logger.info('Skipped the device "{name}."'.format( - name=device.name)) - - def _add_renderer(self, device_id, device): - config = self.device_config.get(device.udn, None) - device.activate(config) - if config: - logger.info( - 'Using device configuration:\n' + device.__str__(True)) - ip, port = self.stream_server_address - device.set_server_location(ip, port) - self.renderers[device_id] = device - self.message_queue.put({ - 'type': 'add_device', - 'device': device - }) - - def _remove_renderer_by_id(self, device_id): - device = self.renderers[device_id] - self.message_queue.put({ - 'type': 'remove_device', - 'device': device - }) - del self.renderers[device_id] + def process_locations(self, locations): + for plugin in self.registered.values(): + for device in plugin.lookup(locations): + self._add_renderer(device.udn, device) - def add_from_search(self, header): + def process_msearch_request(self, header): header = self._retrieve_header_map(header) device_id = self._retrieve_device_id(header) @@ -127,3 +95,33 @@ self._remove_renderer_by_id(device_id) finally: self.lock.release() + + def _add_renderer_with_filter_check(self, device_id, device): + if self.device_filter is None or device.name in self.device_filter: + self._add_renderer(device_id, device) + else: + logger.info('Skipped the device "{name}" ...'.format( + name=device.label)) + + def _add_renderer(self, device_id, device): + if device.validate(): + config = self.device_config.get(device.udn, None) + device.activate(config) + if config: + logger.info( + 'Using device configuration:\n' + device.__str__(True)) + ip, port = self.stream_server_address + device.set_server_location(ip, port) + self.renderers[device_id] = device + self.message_queue.put({ + 'type': 'add_device', + 'device': device + }) + + def _remove_renderer_by_id(self, device_id): + device = self.renderers[device_id] + self.message_queue.put({ + 'type': 'remove_device', + 'device': device + }) + del self.renderers[device_id]
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/rules.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/rules.py
Changed
@@ -74,6 +74,19 @@ pass +class DISABLE_DEVICE_STOP(BaseRule): + pass + + +class REQUEST_TIMEOUT(BaseRule): + def __init__(self, timeout=None): + self.timeout = float(timeout or 10) + + def __str__(self): + return '{} (timeout="{}")'.format( + self.__class__.__name__, self.timeout) + + # class EXAMPLE_PROPERTIES_RULE(BaseRule): # def __init__(self, prop1=None, prop2=None): # self.prop1 = prop1 or 'abc'
View file
pulseaudio-dlna-0.4.5.2.tar.gz/pulseaudio_dlna/streamserver.py -> pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/streamserver.py
Changed
@@ -497,8 +497,7 @@ data_quoted, suffix = re.findall(r'/(.*?)/stream\.(.*)', path)[0] data_string = base64.b64decode(urllib.unquote(data_quoted)) settings = { - k: v for k, v in - [pair.split('=') for pair in data_string.split(',')] + k: v for k, v in re.findall('(.*?)="(.*?)",?', data_string) } logger.info( 'URL settings: {path} ({data_string})'.format(
View file
pulseaudio-dlna-0.4.6.tar.gz/pulseaudio_dlna/utils/encoding.py
Added
@@ -0,0 +1,27 @@ +#!/usr/bin/python + +# This file is part of pulseaudio-dlna. + +# pulseaudio-dlna is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# pulseaudio-dlna 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 General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with pulseaudio-dlna. If not, see <http://www.gnu.org/licenses/>. + +from __future__ import unicode_literals + +import sys +import locale + + +def encode_default(bytes): + return bytes.encode( + sys.stdout.encoding or locale.getpreferredencoding() or 'ascii', + errors='replace')
View file
pulseaudio-dlna-0.4.5.2.tar.gz/scripts/radio.py -> pulseaudio-dlna-0.4.6.tar.gz/scripts/radio.py
Changed
@@ -1,4 +1,5 @@ #!/usr/bin/python +# -*- coding: utf-8 -*- # This file is part of pulseaudio-dlna. @@ -21,6 +22,7 @@ import requests import logging import sys +import concurrent.futures level = logging.INFO logging.getLogger('requests').setLevel(logging.WARNING) @@ -47,10 +49,15 @@ pulseaudio_dlna.plugins.chromecast.ChromecastPlugin(), ] - def __init__(self): + def __init__(self, max_workers=10): self.devices = self._discover_devices() + self.thread_pool = concurrent.futures.ThreadPoolExecutor( + max_workers=max_workers) def stop(self, name, flavour=None): + self.thread_pool.submit(self._stop, name, flavour) + + def _stop(self, name, flavour=None): device = self._get_device(name, flavour) if device: return_code = device.stop() @@ -64,6 +71,9 @@ name=device.label, code=return_code)) def play(self, url, name, flavour=None): + self.thread_pool.submit(self._play, url, name, flavour) + + def _play(self, url, name, flavour=None): if url.lower().endswith('.m3u'): url = self._get_playlist_url(url) codec = self._get_codec(url)
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.