import config import logging import paho.mqtt.client as paho import report import socket import subprocess import time import spotipy from spotipy.oauth2 import SpotifyClientCredentials import config import json try: from config import APP_NAME as ROOT_LOGGER_NAME except ImportError: ROOT_LOGGER_NAME = 'root' logger = logging.getLogger(ROOT_LOGGER_NAME).getChild('main') class librespot(object): ON_CMD = ['play', ] OFF_CMD = ['pause', 'stop', ] def __init__(self, state_callback, title_callback): logger.info("Starting Librespot...") self.__state_callback__ = state_callback self.__title_callback__ = title_callback self.__process__ = subprocess.Popen(["librespot", "-v", "--name", config.DEVICE_NAME], shell=False, # We pipe the output to an internal pipe stderr=subprocess.PIPE) self.__state__ = None self.__preload_state__ = False self.__title__ = None self.__spot_id__ = None self.__spot_id_preload__ = None self.set_state(False) self.set_title("") def run(self): while True: output = self.__process__.stderr.readline() # Polling returns None when the program is still running, return_code otherwise return_code = self.__process__.poll() if return_code is not None: self.__process__.close() # Program ended, get exit/return code raise RuntimeError("Command '{}' finished with exit code {}".format(command, return_code)) # If the output is not empty, feed it to the function, strip the newline first if output: out_txt = output.decode('utf-8').strip('\n').strip() out_txt = out_txt[out_txt.find(']') + 2:] #logger.debug("librespot output: %s", out_txt) # TODO: Parse for "librespot output: Loading with Spotify URI " if out_txt.lower().startswith("loading"): logger.debug("librespot: %s", out_txt) if self.__preload_state__: self.__spot_id_preload__ = out_txt.split("<")[2][:-1] logger.info("Upcomming Track-ID %s identified", self.__spot_id__) else: self.__spot_id__ = out_txt.split("<")[2][:-1] logger.info("Current Track-ID %s identified", self.__spot_id__) if "command=" in out_txt: command = out_txt[out_txt.find('command=') + 8:].strip().lower() logger.debug("librespot command: %s", command) if command.startswith("preload"): self.__preload_state__ = True if command.startswith("load"): self.set_state(command.split(',')[2].strip() == 'true') # self.__preload_state__ = False self.__spot_id__ = self.__spot_id_preload__ # elif command in self.ON_CMD: self.set_state(True) elif command in self.OFF_CMD: self.set_state(False) if self.__state__: self.set_title(self.get_title_by_id()) else: self.set_title("") def get_title_by_id(self): if self.__spot_id__ is None: return "" else: sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials(client_id=config.SPOTIFY_CLIENT_ID,client_secret=config.SPOTIFY_CLIENT_SECRET)) try: track = sp.track(self.__spot_id__) except Exception: return None return track["artists"][0]["name"] + " - " + track["name"] def set_state(self, target_state): if target_state != self.__state__: self.__state__ = target_state logger.info("spotify state changed to %s", self.__state__) self.__state_callback__(self.__state__) def set_title(self, title): if title != self.__title__: self.__title__ = title logger.info("spotify title changed to \"%s\"", self.__title__) self.__title_callback__(self.__title__) def send_state_msg_mqtt(state): client= paho.Client("spotify") client.username_pw_set(config.MQTT_USER, config.MQTT_PASS) try: client.connect(config.MQTT_SERVER, 1883) topic = config.MQTT_TOPIC + "/state" logger.info("Sending Spotify status information to mqtt %s = %s", topic, str(state)) client.publish(topic, "true" if state else "false") except (socket.timeout, OSError) as e: logger.warning("Erro while sending state information") def send_title_msg_mqtt(title): client= paho.Client("spotify") client.username_pw_set(config.MQTT_USER, config.MQTT_PASS) try: client.connect(config.MQTT_SERVER, 1883) topic = config.MQTT_TOPIC + "/title" logger.info("Sending Spotify status information to mqtt %s = %s", topic, title) client.publish(topic, title) except (socket.timeout, OSError) as e: logger.warning("Erro while sending title information") if __name__ == '__main__': report.appLoggingConfigure(config.__BASEPATH__, config.LOGTARGET, ((config.APP_NAME, config.LOGLVL), ), fmt=config.formatter, host=config.LOGHOST, port=config.LOGPORT) ls = librespot(send_state_msg_mqtt, send_title_msg_mqtt) ls.run()