def _do_notify(self, usn): """ Do a notification for the usn specified. @param usn: st @type usn: string """ log.debug('Sending alive notification for %s', usn) response = [ 'NOTIFY * HTTP/%s' % self.http_version, 'HOST: %s:%d' % (SSDP_ADDR, SSDP_PORT), 'NTS: ssdp:alive', ] stcpy = dict(self.advertised[usn].iteritems()) stcpy['NT'] = stcpy['ST'] del stcpy['EXT'] del stcpy['ST'] response.extend(map(lambda x: ': '.join(x), stcpy.iteritems())) append = response.append [append('%s: %s' % (k, v)) for k, v in self.additional_headers.items()] response.extend(('', '')) log.debug('Sending notify message with content %s' % response) try: self.udp_transport.send_data('\r\n'.join(response), SSDP_ADDR, SSDP_PORT) self.udp_transport.send_data('\r\n'.join(response), SSDP_ADDR, SSDP_PORT) except Exception, e: log.info("Failure sending notify with message %s" % str(e))
def double_discover(self, search_type=DEFAULT_SEARCH_TYPE): """ Sends a MSearch imediatelly. Each call to this method will yield a MSearch message, that is, it won't repeat automatically. """ log.info("Doing double discover for %s", search_type) self.discover(search_type) self.discover(search_type)
def load_plugins(self): """ Instantiates plugins and loads them using PluginLoader. """ self._instantiate_plugins() names = [p.name for p in self.plugins_instances.values() if p.name != "rootplugin"] log.info("Detected plugins: %s", ", ".join(names)) self._load_plugins()
def load_plugins(self): """ Instantiates plugins and loads them using PluginLoader. """ self._instantiate_plugins() names = [p.name for p in self.plugins_instances.values() \ if p.name!='rootplugin'] log.info('Detected plugins: %s', ', '.join(names)) self._load_plugins()
def play(self): if self.av_uri is not None: if (self.player.set_state(gst.STATE_PLAYING) == gst.STATE_CHANGE_FAILURE): log.error("error trying to play %s.", self.av_uri) self.player_state = 1 else: log.info("av_uri is None, unable to play.")
def play(self): if self.av_uri is not None: if (self.player.set_state( gst.STATE_PLAYING) == gst.STATE_CHANGE_FAILURE): log.error("error trying to play %s.", self.av_uri) self.player_state = 1 else: log.info("av_uri is None, unable to play.")
def lookup_function(self, function_name): """ Lookup published SOAP function. """ log.info('Finding service action %s' % function_name) for action_name, action in self.service._actions.iteritems(): if action_name == function_name: return action log.info('Action %s not founded' % function_name) return None
def _load_plugin(self, plugin): """ Loads a plugin using PluginLoader. @param plugin: plugin instance to be loaded @type plugin: Plugin """ log.info('Loading plugin %s', plugin.name) plugin.plugin_manager = self plugin.execute()
def soap_Previous(self, *args, **kwargs): """Advance to the previous track. This is a convenient action to advance to the previous track. This action is functionally equivalent to Seek(TRACK_NR, CurrentTrackNr-1). This action does not cycle back to the last track.""" log.info('Previous()') return {}
def _load_plugin(self, plugin): """ Loads a plugin using PluginLoader. @param plugin: plugin instance to be loaded @type plugin: Plugin """ log.info("Loading plugin %s", plugin.name) plugin.plugin_manager = self plugin.execute()
def play(self): if self.av_uri is not None: if self.player.set_state(gst.STATE_PLAYING) == \ gst.STATE_CHANGE_FAILURE: log.error("gst_renderer error while trying to play") return self.player_state = 1 else: log.info("av_uri is None, unable to play.")
def soap_Next(self, *args, **kwargs): """Advance to the next track. This is a convenient action to advance to the next track. This action is functionally equivalent to Seek(TRACK_NR, CurrentTrackNr+1). This action does not cycle back to the first track.""" log.info('Next()') return {}
def double_discover(self, search_type=DEFAULT_SEARCH_TYPE, http_version="1.1", man='"ssdp:discover"', mx=1, additionals={}): """ Sends a MSearch imediatelly. Each call to this method will yield a MSearch message, that is, it won't repeat automatically. """ log.info("Doing double discover for %s, HTTP_VERSION=%s, MAN=%s, \ MX=%d, additionals=%s" % (search_type, http_version, man, mx, additionals)) self.discover(search_type, http_version, man, mx, additionals)
def soap_GetTransportSettings(self, *args, **kwargs): """Return information on various settings of instance. This action returns information on various settings of the specified instance, such as the current play mode and the current recording quality mode. This action has no effect on state.""" log.info('GetTransportSettings()') return {'PlayMode': 'DIRECT_1', 'ReqQualityMode': 'NOT_IMPLEMENTED'}
def soap_Stop(self, *args, **kwargs): """Stop progression of current resource. This action stops the progression of the current resource that is associated with the specified instance.""" log.info('Stop()') self.transport_state = 'TRANSITIONING' self.gst_player.stop() self.transport_state = 'STOPPED' return {}
def soap_Seek(self, *args, **kwargs): """Seek through the resource controlled. This action starts seeking through the resource controlled by the specified instance - as fast as possible - to the position, specified in the Target argument.""" (instance_id, unit, target) = args self.gst_player.seek(unit, target) log.info('Seek()') return {}
def soap_GetDeviceCapabilities(self, *args, **kwargs): """Return information on device capabilities. This action returns information on device capabilities of the specified instance, such as the supported playback and recording formats, and the supported quality levels for recording. This action has no effect on state.""" log.info('GetDeviceCapabilities()') return {'PlayMedia': 'NONE', 'RecMedia': 'NOT_IMPLEMENTED', 'ReqQualityMode': 'NOT_IMPLEMENTED'}
def _removed_device_event(self, device_info): """ Receives a removed device event. @param device_info: information about the device @type device_info: dict """ udn = device_info['USN'].split('::')[0] if udn in self._known_devices: log.info('Device is gone: %s' % self._known_devices[udn].friendly_name) self._known_devices.pop(udn, None) self._callback("removed_device_event", udn)
def _publish(self): assert self.webserver is not None, 'Device was told not to create '\ 'webserver (with False on the '\ 'create_webserver parameter) and'\ 'device.webserver was not set'\ 'manually as it should.' log.info('Publishing device %s' % self.friendly_name) DeviceXMLBuilder(self).generate_to_file(self._xml_filepath) self.webserver.add_static_file(webserver.StaticFile(self.xml_filename, self._xml_filepath)) log.info('Publishing device\'s services') for v in self.services.values(): v.publish(self.webserver)
def soap_Play(self, *args, **kwargs): """Play the resource of instance. This action starts playing the resource of the specified instance, at the specified speed, starting at the current position, according to the current play mode.""" log.info('Play()') log.debug('Playing uri: %r', self.gst_player.av_uri) self.transport_state = 'TRANSITIONING' self.gst_player.play() self.transport_state = 'PLAYING' return {}
def soap_Pause(self, *args, **kwargs): """Pause the resouce of instance. This action starts playing the resource of the specified instance, at the specified speed, starting at the current position, according to the current play mode.""" log.info('Pause()') self.transport_state = 'TRANSITIONING' self.gst_player.pause() self.transport_state = 'PAUSED_PLAYBACK' return {}
def soap_GetMediaInfo(self, *args, **kwargs): """Return information of current Media. This action returns information associated with the current media of the specified instance; it has no effect on state.""" log.info('GetMediaInfo()') return {'NrTracks': str(self.number_of_tracks), 'MediaDuration': str(self.gst_player.query_duration()[0]), 'CurrentURI': self.av_transport_uri, 'CurrentURIMetaData': '', 'NextURI': '', 'NextURIMetaData': '', 'PlayMedium': 'NETWORK', 'RecordMedium': '', 'WriteStatus': ''}
def _new_device_event_impl(self, device_info, device): """ Real implementation of the new device event handler. @param device_info: informations about the device @param device: the device object itself @type device_info: dict @type device: Device """ if not device and self._ssdp_server: # Device creation failed, tell SSDPSearch to forget it self._ssdp_server.discovered_device_failed(device_info) return self._known_devices[device.udn] = device self._callback("new_device_event", device) log.info('Device found: %s' % device.friendly_name)
def publish(self, webserver): log.info("Publishing service %s" % self.id) if not len(self.get_variables()): raise InvalidService("The service must have one " "or more state variables") res = webserver.CustomResource(self.id) if self._create_xml: ServiceXMLBuilder(self).generate_to_file(self._xml_filepath) f = webserver.StaticFile("scpd.xml", self._xml_filepath) res.add_static_file(f) for k in ["control", "eventSub", "presentation"]: controller = getattr(self, "%s_controller" % k) if controller: res.add_resource(controller) webserver.add_resource(res)
def _new_device_event_impl(self, device_info, device): """ Real implementation of the new device event handler. @param device_info: informations about the device @param device: the device object itself @type device_info: dict @type device: Device """ if not device and self._ssdp_server: # Device creation failed, tell SSDPSearch to forget it self._ssdp_server.discovered_device_failed(device_info) return device.additional_headers = device_info['ADDITIONAL_HEADERS'] self._known_devices[device.udn] = device self._callback("new_device_event", device) log.info('Device found: %s' % device.friendly_name)
def publish(self, webserver): log.info('Publishing service %s' % self.id) if not len(self.get_variables()): raise InvalidService('The service must have one '\ 'or more state variables') res = webserver.CustomResource(self.id) if self._create_xml: ServiceXMLBuilder(self).generate_to_file(self._xml_filepath) f = webserver.StaticFile('scpd.xml', self._xml_filepath) res.add_static_file(f) for k in ['control', 'eventSub', 'presentation']: controller = getattr(self, '%s_controller' % k) if controller: res.add_resource(controller) webserver.add_resource(res)
def soap_SetAVTransportURI(self, *args, **kwargs): """Specifies the URI of resource. This action specifies the URI of the resource to be controlled by the specified AVTransport instance. It is RECOMMENDED that the AVTransport service checks the MIME-type of the specified resource when executing this action""" instance_id = kwargs['InstanceID'] current_uri = kwargs['CurrentURI'] current_uri_metadata = kwargs['CurrentURIMetaData'] self.av_transport_uri = current_uri self.number_of_tracks = 1 self.gst_player.av_uri = current_uri self.transport_state = 'STOPPED' log.info('SetAVTransportURI(%s, %s, %s)' % (instance_id, current_uri, current_uri_metadata)) return {}
def soap_GetMediaInfo_Ext(self, *args, **kwargs): """Return information of current Media and CurrentType argment. This action returns information associated with the current media of the specified instance; it has no effect on state.The information returned is identical to the information returned by the GetMediaInfo() action, except for the additionally returned CurrentType argument """ log.info('GetMediaInfo_Ext()') return {'CurrentType': 'TRACK_UNAWARE', 'NrTracks': str(self.number_of_tracks), 'MediaDuration': str(self.gst_player.query_duration()[0]), 'CurrentURI': self.av_transport_uri, 'CurrentURIMetaData': '', 'NextURI': '', 'NextURIMetaData': '', 'PlayMedium': 'NETWORK', 'RecordMedium': '', 'WriteStatus': ''}
def _new_device_event_impl(self, device_info, device): """ Real implementation of the new device event handler. @param device_info: informations about the device @param device: the device object itself @type device_info: dict @type device: Device """ # print "control_point _new_device_event_impl udn: " + str(device.udn) log.debug('device_info: %s, device: %s', device_info, device) if not device and self._ssdp_server: # Device creation failed, tell SSDPSearch to forget it self._ssdp_server.discovered_device_failed(device_info) return self._known_devices[device.udn] = device self._callback("new_device_event", device) log.info('Device found: %s' % device.friendly_name)
def _do_notify(self, usn): """ Do a notification for the usn specified. @param usn: st @type usn: string """ log.debug('Sending alive notification for %s', usn) response = ['NOTIFY * HTTP/%s' % self.http_version, 'HOST: %s:%d' % (SSDP_ADDR, SSDP_PORT), 'NTS: ssdp:alive', ] stcpy = dict(self.advertised[usn].iteritems()) stcpy['NT'] = stcpy['ST'] del stcpy['EXT'] del stcpy['ST'] response.extend(map(lambda x: ': '.join(x), stcpy.iteritems())) append = response.append [append('%s: %s' % (k, v)) for k, v in self.additional_headers.items()] response.extend(('', '')) log.debug('Sending notify message with content %s' % response) try: self.udp_transport.send_data('\r\n'.join(response), SSDP_ADDR, SSDP_PORT) self.udp_transport.send_data('\r\n'.join(response), SSDP_ADDR, SSDP_PORT) except Exception, e: log.info("Failure sending notify with message %s" % str(e))
def start(self): """ Starts the device. """ log.info('Starting device %s' % self.friendly_name) log.info('Starting device\'s services') for k, v in self.services.items(): try: log.info('Starting service %s' % k) v.start() except Exception, e: log.error('Error starting service %s: %s' % (k, e))
def main(self): """ Enters the RUNNING state by running the main loop until main_quit() is called. """ if self.state != REACTOR_STATE_STOPPED: raise ReactorAlreadyRunningException('main() called twice or '\ 'together with main_loop_iterate()') self.state = REACTOR_STATE_RUNNING log.info('Preparing main loop') self._main_call_before_start_funcs() log.info('Entering main loop') while self.state == REACTOR_STATE_RUNNING: try: if not self.main_loop_iterate(): break except: break log.info('Preparing to exit main loop') self._main_call_before_stop_funcs() log.info('Exited main loop')
def start(self): """ Starts the device. """ log.info('Starting device %s' % self.friendly_name) log.info('Starting device\'s services') for k, v in list(self.services.items()): try: if self._force_event_reload: v._set_force_event_reload(self._force_event_reload) else: if self._event_reload_time: v._set_event_reload_time(self._event_reload_time) log.info('Starting service %s' % k) v.start() except Exception as e: log.error('Error starting service %s: %s' % (k, str(e))) self._publish() self.SSDP.register_device(self) self.webserver.start() self.SSDP.start() self.SSDP.announce_device() log.info('Finished starting device %s' % self.friendly_name)
def start(self): """ Starts the device. """ log.info('Starting device %s' % self.friendly_name) log.info('Starting device\'s services') for k, v in self.services.items(): try: if self._force_event_reload: v._set_force_event_reload(self._force_event_reload) else: if self._event_reload_time: v._set_event_reload_time(self._event_reload_time) log.info('Starting service %s' % k) v.start() except Exception, e: log.error('Error starting service %s: %s' % (k, str(e)))
def start(self): """ Starts the device. """ log.info('Starting device %s' % self.friendly_name) log.info('Starting device\'s services') for k, v in self.services.items(): try: if self._force_event_reload: v._set_force_event_reload(self._force_event_reload) else: if self._event_reload_time: v._set_event_reload_time(self._event_reload_time) log.info('Starting service %s' % k) v.start() except Exception, e: log.error('Error starting service %s: %s' % (k, e))
def publish(self, webserver): log.info('Publishing service %s' % self.id) if not len(self.get_variables()): raise InvalidService('The service must have one '\ 'or more state variables') res = webserver.CustomResource(self.id) if self._create_xml: ServiceXMLBuilder(self).generate_to_file(self._xml_filepath) f = webserver.StaticFile('scpd.xml', self._xml_filepath) res.add_static_file(f) for k in ['control', 'eventSub', 'presentation']: log.info('Publishing service, controller %s' % k) controller = getattr(self, '%s_controller' % k) if controller: log.info('Publishing service, controller, add_resource %s' % controller) res.add_resource(controller) webserver.add_resource(res)
def publish_plugins(self, webserver): log.info('Publishing plugins for ContentDirectory') for v in self.plugins_instances.values(): v.publish(webserver)
def unload_plugins(self): """ Unload all plugins. """ for p in self.plugins_instances.values(): log.info('Unloading plugin %s', p.name) p.unload()
class Device(BaseDevice): """ Class that represents a device. When used with default settings, usage should be minimized to instantiation, start() and stop(). The special setting is a create_webserver keyword that can be passed during construction. If False, it disables the automatic creation of the internal webserver for serving device files, so, the user should create his own webserver and set Device.webserver to it. """ def __init__(self, *args, **kwargs): """ Constructor for the Device class. @param device_type: device type as described on the device reference @param friendly_name: a friendly name Optional parameters follow below: @param udn: uuid for the device. If not specified will be automatically generated. @param parent: parent device @param manufacturer: manufacturer @param manufacturer_url: manufacturer url @param model_description: model description @param model_name: model name @param model_number: model number @param model_url: model url @param serial_number: serial number @param upc: upc @param presentation_url: presentation url @param create_webserver: see class description for more information. Default value is True and you should normally don't pass anything @param force_listen_url: forces the webserver to listen on a specific address. If it's not possible to listen on that address, another random one will be generated and used automatically. @type device_type: string @type friendly_name: string @type udn: string @type parent: Device @type manufacturer: string @type manufacturer_url: string @type model_description: string @type model_name: string @type model_number: string @type model_url: string @type serial_number: string @type upc: string @type presentation_url: string @type create_webserver: bool @type force_listen_url: url to use for webserver """ create_webserver = kwargs.pop('create_webserver', True) force_listen_url = kwargs.pop('force_listen_url', '') udp_listener = kwargs.pop('udp_listener', '') BaseDevice.__init__(self, *args, **kwargs) self._generate_xml() self.SSDP = SSDPServer(self.friendly_name, self.xml_filename, udp_listener=udp_listener) self.webserver = None if create_webserver: self._create_webserver(force_listen_url) def add_service(self, service): """ Adds a service to the device. """ assert self.location, 'service does not have a location attribute yet'\ '. It must have a location set before adding '\ 'services. If you passed create_webserver=Fal'\ 'se, you must create the webserver in your '\ 'own fashion and set self.location to the '\ 'listening URL before adding services. This '\ 'problem may also occur if you pass an '\ 'invalid force_listen_url parameter.' service.url_base = self.location BaseDevice.add_service(self, service) def _create_webserver(self, force_listen_url=''): if force_listen_url: p = network.parse_url(force_listen_url) self.webserver = webserver.WebServer(host=p.hostname, port=p.port) else: self.webserver = webserver.WebServer() self.location = self.webserver.get_listen_url() def _generate_xml(self): self.xml_filename = '%s-root-device.xml' % self.friendly_name self.xml_filename = self.xml_filename.replace(' ', '') self._xml_filepath = path.join(config.manager.brisa_home, 'tmp_xml') if not path.exists(self._xml_filepath): mkdir(self._xml_filepath) self._xml_filepath = path.join(self._xml_filepath, self.xml_filename) def _publish(self): assert self.webserver is not None, 'Device was told not to create '\ 'webserver (with False on the '\ 'create_webserver parameter) and'\ 'device.webserver was not set'\ 'manually as it should.' log.info('Publishing device %s' % self.friendly_name) DeviceXMLBuilder(self).generate_to_file(self._xml_filepath) self.webserver.add_static_file(webserver.StaticFile(self.xml_filename, self._xml_filepath)) log.info('Publishing device\'s services') for v in self.services.values(): v.publish(self.webserver) def start(self): """ Starts the device. """ log.info('Starting device %s' % self.friendly_name) log.info('Starting device\'s services') for k, v in self.services.items(): try: log.info('Starting service %s' % k) v.start() except Exception, e: log.error('Error starting service %s: %s' % (k, e)) self._publish() self.SSDP.register_device(self) self.webserver.start() self.SSDP.start() log.info('Finished starting device %s' % self.friendly_name)
class Device(BaseDevice): """ Class that represents a device. When used with default settings, usage should be minimized to instantiation, start() and stop(). The special setting is a create_webserver keyword that can be passed during construction. If False, it disables the automatic creation of the internal webserver for serving device files, so, the user should create his own webserver and set Device.webserver to it. """ def __init__(self, *args, **kwargs): """ Constructor for the Device class. @param device_type: device type as described on the device reference @param friendly_name: a friendly name Optional parameters follow below: @param udn: uuid for the device. If not specified will be automatically generated. @param parent: parent device @param manufacturer: manufacturer @param manufacturer_url: manufacturer url @param model_description: model description @param model_name: model name @param model_number: model number @param model_url: model url @param serial_number: serial number @param upc: upc @param presentation_url: presentation url @param create_webserver: see class description for more information. Default value is True and you should normally don't pass anything @param force_listen_url: forces the webserver to listen on a specific address. If it's not possible to listen on that address, another random one will be generated and used automatically. @param additional_ssdp_headers: can be used to add more field in the notify message or http responses. @type device_type: string @type friendly_name: string @type udn: string @type parent: Device @type manufacturer: string @type manufacturer_url: string @type model_description: string @type model_name: string @type model_number: string @type model_url: string @type serial_number: string @type upc: string @type presentation_url: string @type create_webserver: bool @type force_listen_url: bool @type additional_ssdp_headers: dict """ create_webserver = kwargs.pop('create_webserver', True) force_listen_url = kwargs.pop('force_listen_url', '') additional_ssdp_headers = kwargs.pop('additional_ssdp_headers', {}) BaseDevice.__init__(self, *args, **kwargs) self._generate_xml() self.SSDP = SSDPServer(self.friendly_name, self.xml_filename, additional_headers=additional_ssdp_headers) self.webserver = None if create_webserver: self._create_webserver(force_listen_url) self._event_reload_time = None self._force_event_reload = None self.extra_elts = [] def add_extra_elts(self, elts): """ Append extra elements to the device description, such as DLNA descriptive elements. Extra elements must be added before start(). @param elts: list of Element objects @type elts: list """ self.extra_elts = elts[:] def add_service(self, service): """ Adds a service to the device. """ assert self.location, 'service does not have a location attribute yet'\ '. It must have a location set before adding '\ 'services. If you passed create_webserver=Fal'\ 'se, you must create the webserver in your '\ 'own fashion and set self.location to the '\ 'listening URL before adding services. This '\ 'problem may also occur if you pass an '\ 'invalid force_listen_url parameter.' service.url_base = self.location service.parent_udn = self.udn BaseDevice.add_service(self, service) def set_event_reload_time(self, time): """ Sets a event reload time for all the services. @param time: event reload time in seconds @type time: int """ self._event_reload_time = time def force_event_reload(self, force): """ Forces event reload. @param force: forces event reload @type force: bool """ self._force_event_reload = force def __add__(self, obj): if issubclass(obj.__class__, Device): log.debug("Adding device %s as embedded for %s", obj, self) self.add_device(obj) elif issubclass(obj.__class__, BaseService): log.debug("Adding service %s as service for %s", obj, self) self.add_service(obj) else: log.warning("Ignored invalid addition: %s + %s", self, obj) return self def _create_webserver(self, force_listen_url=''): if force_listen_url: p = network.parse_url(force_listen_url) self.webserver = webserver.WebServer(host=p.hostname, port=p.port) else: self.webserver = webserver.WebServer() self.location = self.webserver.get_listen_url() def _generate_xml(self): self.xml_filename = '%s-root-device.xml' % self.friendly_name self.xml_filename = self.xml_filename.replace(' ', '') self._xml_filepath = path.join(config.manager.brisa_home, 'tmp_xml') if not path.exists(self._xml_filepath): mkdir(self._xml_filepath) self._xml_filepath = path.join(self._xml_filepath, self.xml_filename) def _publish(self): assert self.webserver is not None, 'Device was told not to create '\ 'webserver (with False on the '\ 'create_webserver parameter) and'\ 'device.webserver was not set'\ 'manually as it should.' log.info('Publishing device %s' % self.friendly_name) DeviceXMLBuilder(self, self.extra_elts).generate_to_file(self._xml_filepath) self.webserver.add_static_file( webserver.StaticFile(self.xml_filename, self._xml_filepath)) log.info('Publishing device\'s services') for v in self.services.values(): v.publish(self.webserver) def start(self): """ Starts the device. """ log.info('Starting device %s' % self.friendly_name) log.info('Starting device\'s services') for k, v in self.services.items(): try: if self._force_event_reload: v._set_force_event_reload(self._force_event_reload) else: if self._event_reload_time: v._set_event_reload_time(self._event_reload_time) log.info('Starting service %s' % k) v.start() except Exception, e: log.error('Error starting service %s: %s' % (k, str(e))) self._publish() self.SSDP.register_device(self) self.webserver.start() self.SSDP.start() self.SSDP.announce_device() log.info('Finished starting device %s' % self.friendly_name)
def publish_plugins(self, webserver): log.info("Publishing plugins for ContentDirectory") for v in self.plugins_instances.values(): v.publish(webserver)
def unload_plugins(self): """ Unload all plugins. """ for p in self.plugins_instances.values(): log.info("Unloading plugin %s", p.name) p.unload()