Beispiel #1
0
    def __init__(self, log, setting, text):

        self.log = log
        self.setting = setting
        self.text = text

        self.tts = BaiduTts(log, setting)
        self.stt = BaiduStt(log, setting)
        self.recorder = XiaolanRecorder(log, setting)
        self.player = XiaolanPlayer(log, setting)
Beispiel #2
0
    def __init__(self, log, setting):

        self.log = log
        self.setting = setting
        self.read_chunk = setting["playerSettings"]["readChunk"]
        self.player = XiaolanPlayer(log, setting)
        self.token = ""

        self.app_key = setting["apiSettings"]["tts"]["appKey"]
        self.secret_key = setting["apiSettings"]["tts"]["secretKey"]

        self.get_token()
Beispiel #3
0
    def __init__(self):

        self.setting = json.load(
            open("./data/json/setting.json", "r", encoding="utf-8"))
        self.log = XiaolanLog()

        self.tts = BaiduTts(self.log, self.setting)
        self.stt = BaiduStt(self.log, self.setting)
        self.nlu = XiaolanNlu(self.log, self.setting)
        self.player = XiaolanPlayer(self.log, self.setting)
        self.recorder = XiaolanRecorder(self.log, self.setting)
        self.skills = XiaolanSkillsManager(self.log, self.setting)
        self.snowboy = XiaolanSnowboy(self.log, self.setting)
Beispiel #4
0
    def __init__(self, log, setting, text):

        self.log = log
        self.setting = setting
        self.text = text

        self.stt = BaiduStt(log, setting)
        self.tts = BaiduTts(log, setting)
        self.nlu = XiaolanNlu(log, setting)
        self.recorder = XiaolanRecorder(log, setting)
        self.player = XiaolanPlayer(log, setting)

        self.hass_settings = setting["skillSettings"]["hass"]
        self.hass_addr = self.hass_settings["hassAddress"]
        self.hass_token = self.hass_settings["token"]

        self.headers = {
            "Authorization": "Bearer " + self.hass_token,
            "Content-Type": "application/json"
        }

        self.components = self.hass_settings["components"]
        self.unit_system = self.hass_settings["unitSystem"]
        self.domain_services = self.hass_settings["domainServices"]
        self.entity_dict = self.hass_settings["entityDict"]
        self.friendly_name_dict = self.hass_settings["friendlyNameDict"]
        self.intent_service_list = self.hass_settings["intentToServiceList"]

        self.nlu_intent_dict = {
            "更新数据": "update_data",
            "打开": "turn_on",
            "关闭": "turn_off",
            "颜色": "set_color",
            "查看": "get_state",
            "设置": "set",
            "改": "set",
            "获取": "get_state",
            "是": "yes",
            "否": "no",
            "不": "no",
            "退出": "quit"
        }
Beispiel #5
0
    def __init__(self, log, setting, text):

        self.log = log
        self.setting = setting
        self.tts = BaiduTts(log, setting)
        self.player = XiaolanPlayer(log, setting)
        self.text = text

        self.tuling_api_key = self.setting["skillSettings"]["tuling"][
            "apiKey"][0]  # 869429e05ea142ef9e3784f8e7965d1c
        self.tuling_user_id = self.setting["skillSettings"]["tuling"][
            "userId"]  # "167031"
Beispiel #6
0
    def __init__(self, log, setting, text):

        self.log = log
        self.setting = setting
        self.text = text

        self.stt = BaiduStt(log, setting)
        self.tts = BaiduTts(log, setting)
        self.nlu = XiaolanNlu(log, setting)
        self.player = XiaolanPlayer(log, setting)
        self.recorder = XiaolanRecorder(log, setting)

        self.play_list = {}

        self.nlu_dict = {
            "恢复": "resume",
            "播放": "play",
            "暂停": "pause",
            "下一首": "next",
            "上一首": "previous",
            "添加": "add",
            "退出": "quit"
        }
Beispiel #7
0
class XiaolanSkillFlightSearcher:
    def __init__(self, log, setting, text):

        self.log = log
        self.setting = setting
        self.text = text

        self.tts = BaiduTts(log, setting)
        self.stt = BaiduStt(log, setting)
        self.recorder = XiaolanRecorder(log, setting)
        self.player = XiaolanPlayer(log, setting)

    def request_csairlines(self, dep, arr, date, cities_info_list, token):

        flight_list = {}
        url = "https://b2c.csair.com/portal/flight/direct/query"
        dep_code = cities_info_list[dep.decode()][0]
        arr_code = cities_info_list[arr.decode()][0]
        self.log.add_log("Skill-FlightSearcher: dep city code: " + dep_code, 1)
        self.log.add_log("Skill-FlightSearcher: arr city code: " + arr_code, 1)
        param = {
            "depCity": dep_code,
            "arrCity": arr_code,
            "flightDate": str(date),
            "adultNum": "1",
            "childNum": "0",
            "infantNum": "0",
            "cabinOrder": "0",
            "airLine": 1,
            "flyType": 0,
            "international": "0",
            "action": "0",
            "segType": "1",
            "cache": 1,
            "preUrl": "",
            "isMember": ""
        }
        r = requests.post(url,
                          data=json.dumps(param),
                          headers={"Content-Type": "application/json"})
        res = r.json()
        if res["success"] is True:
            res_data = res["data"]["segment"][0]["dateFlight"]
            for i in res_data["flight"]:
                flight_list[i["flightNo"]] = i
        else:
            self.log.add_log("Skill-FlightSearcher: Flight search fail", 3)
            self.log.add_log("    Error code:" + res["errorCode"], 3)
            self.log.add_log("    Error msg:" + res["errorMsg"], 3)
            self.log.add_log("    The response: \n" + str(res), 3)
        return flight_list

    def get_cities_info_list(self):

        cities_info_list = {}
        url = "https://www.csair.com/cn/scripts/city/getcitylist.html?callback=getB2CCmsCity&_=1564394534382"
        r = requests.get(url)
        content = r.content.decode().replace("getB2CCmsCity(",
                                             "").replace(")", "")
        res = json.loads(content)

        for i in res["cities"]:
            cities_info_list[i[0]] = [i[4], i[11]]
        return cities_info_list

    def main(self):

        self.tts.start("Welcome using the FlightTracer. Made by Lan_zhijiang")
        self.tts.start("Loading cities... please wait...")
        cities_info_list = self.get_cities_info_list()
        self.tts.start("Load finished")

        self.tts.start("请问你要从哪里出发?请说出城市名")
        self.player.ding()
        self.recorder.record()
        self.player.dong()
        dep = self.stt.start()

        self.tts.start("请问你要到哪里去?请说出城市名")
        self.player.ding()
        self.recorder.record()
        self.player.dong()
        arr = self.stt.start()

        date = self.log.get_date()
        self.tts.start("请问你要什么时候出发?月份+日期,今天是" + date)
        self.player.ding()
        self.recorder.record()
        self.player.dong()
        date = "20190811"

        self.log.add_log("Skill-FlightSearcher: Confirming info...", 1)

        self.log.add_log.tts("Information confirm completed", 1)
        self.log.add_log("Searching for the flight...please wait...", 1)
        flight_list = self.request_csairlines(dep, arr, date, cities_info_list,
                                              1)
        flight_num_list = list(flight_list.keys())
        for i in flight_num_list:
            dep_time = flight_list[i]["depTime"][0:2] + ":" + flight_list[i][
                "depTime"][2:4]
            arr_time = flight_list[i]["arrTime"][0:2] + ":" + flight_list[i][
                "arrTime"][2:4]
            self.tts.start(i + ",这趟航班的经济舱价格是" +
                           str(flight_list[i]["cabin"][-1]["adultPrice"]) +
                           ",出发时间是" + str(dep_time) + ",到达时间是" + str(arr_time))
Beispiel #8
0
class XiaolanInit():
    def __init__(self):

        self.setting = json.load(
            open("./data/json/setting.json", "r", encoding="utf-8"))
        self.log = XiaolanLog()

        self.tts = BaiduTts(self.log, self.setting)
        self.stt = BaiduStt(self.log, self.setting)
        self.nlu = XiaolanNlu(self.log, self.setting)
        self.player = XiaolanPlayer(self.log, self.setting)
        self.recorder = XiaolanRecorder(self.log, self.setting)
        self.skills = XiaolanSkillsManager(self.log, self.setting)
        self.snowboy = XiaolanSnowboy(self.log, self.setting)

    def run(self):
        """
        启动小蓝
        :return:
        """
        print("""
        
            ########################################
                  小蓝-中文智能家居语音交互机器人
              (c)Lan_zhijiang all rights reserved
              http://github.com/xiaoland/xiaolan
                E-mail: [email protected]
            ########################################
        
        """)
        self.log.add_log("Xiaolan start! ", 1)
        self.log.add_log("Self checking...", 1)

        self.log.add_log("XiaolanInit: Play the welcome speech(online tts)", 1)
        self.tts.start("你好啊,主人~这里是小蓝哦!")

        self.log.add_log("Start pulseaudio", 1)
        os.system("pulseaudio --start")

        self.log.add_log("Run snowboy awaken engine", 1)
        self.snowboy.run(self.callback)

    def callback(self):
        """
        回调函数
        :return:
        """
        self.log.add_log("XiaolanInit: Detected awaken from snowboy", 1)
        self.conversation()

    def conversation(self):
        """
        开始对话
        :return:
        """
        self.log.add_log("XiaolanInit: Start new conversation", 1)

        self.player.ding()
        self.recorder.record()
        self.player.dong()

        text = self.stt.start()
        if text is None:
            self.log.add_log(
                "XiaolanInit: Text is none, play text_none(online tts)", 1)
            self.tts.start("抱歉,我好像没听清")
            return
        else:
            intent = self.nlu.get_intent(text)
            if intent[0] is None:
                self.log.add_log(
                    "XiaolanInit: Intent is none, play intent_none(online tts)",
                    1)
                self.tts.start("我不是很明白你的意思")
                return
            else:
                if intent[0] == "call_skill":
                    self.skills.skill_skills_list[intent[1]](self.log,
                                                             self.setting,
                                                             text).start()
                else:
                    self.skills.intent_skills_list[intent[0]](self.log,
                                                              self.setting,
                                                              text).start()
Beispiel #9
0
class XiaolanSkillHass():
    def __init__(self, log, setting, text):

        self.log = log
        self.setting = setting
        self.text = text

        self.stt = BaiduStt(log, setting)
        self.tts = BaiduTts(log, setting)
        self.nlu = XiaolanNlu(log, setting)
        self.recorder = XiaolanRecorder(log, setting)
        self.player = XiaolanPlayer(log, setting)

        self.hass_settings = setting["skillSettings"]["hass"]
        self.hass_addr = self.hass_settings["hassAddress"]
        self.hass_token = self.hass_settings["token"]

        self.headers = {
            "Authorization": "Bearer " + self.hass_token,
            "Content-Type": "application/json"
        }

        self.components = self.hass_settings["components"]
        self.unit_system = self.hass_settings["unitSystem"]
        self.domain_services = self.hass_settings["domainServices"]
        self.entity_dict = self.hass_settings["entityDict"]
        self.friendly_name_dict = self.hass_settings["friendlyNameDict"]
        self.intent_service_list = self.hass_settings["intentToServiceList"]

        self.nlu_intent_dict = {
            "更新数据": "update_data",
            "打开": "turn_on",
            "关闭": "turn_off",
            "颜色": "set_color",
            "查看": "get_state",
            "设置": "set",
            "改": "set",
            "获取": "get_state",
            "是": "yes",
            "否": "no",
            "不": "no",
            "退出": "quit"
        }

    def start(self):
        """
        开始
        :return:
        """
        self.log.add_log("XiaolanSkillHass: Initializing...", 1)

        self.tts.start("Hi!~ 这里是智能家居控制系统~")

        self.main()

    def conversation(self):
        """
        对话(减少重复)
        :return:
        """
        self.player.ding()
        self.recorder.record()
        self.player.dong()

        text = self.stt.start()

        intent, slot = self.nlu.skill_nlu(text, self.nlu_intent_dict)
        if intent is None:
            self.log.add_log("XiaolanSkillHass: Intent is none, quit", 1)
            self.tts.start("不好意思,我没明白,退出咯~")
            intent, text, slot = None, None, None
        self.log.add_log(
            "XiaolanSkillHass: Intent: " + intent + " Text: " + text +
            " Slot: " + slot, 1)
        return intent, text, slot

    def main(self):
        """
        主逻辑程序(可多次对话)
        :return:
        """
        self.tts.start("有何贵干呐?敬请吩咐。若不明白如何操作,请查阅本技能的wiki")

        intent, text, slot = self.conversation()
        if intent is None:
            return

        self.log.add_log("XiaolanSkillHass: Recognized intent: " + intent, 1)

        if intent == "update_data":
            self.update_data()
            self.tts.start("所有数据已更新")
        elif intent == "get_state":
            friendly_name = slot
            state = self.get_state(self.friendly_name_dict[friendly_name])
            self.tts.start(friendly_name + "的状态是" +
                           state["state"].replace("_", " "))
        elif intent == "quit":
            self.tts.start("好的,我走啦,下次再见~")
            return
        else:
            service = self.intent_service_list[intent]
            service_path = self.domain_services[service]
            param = {"entity_id": self.friendly_name_dict[slot]}
            self.call_service_v2(service_path, param)

        self.tts.start("还要继续吗?")

        intent, text, slot = self.conversation()
        if intent is None:
            return
        if intent == "yes":
            self.main()
        else:
            self.tts.start("好的,我走啦,下次再见~")

    def request(self, url, type, param=None):
        """
        使用requests库进行get/post请求(减少重复代码)
        :param url: 请求的地址
        :param param: 请求的数据 post才需要
        :param type: 请求类型 get/post
        :return:
        """
        if type == "get":
            r = requests.get(url, headers=self.headers)
        elif type == "post":
            r = requests.post(url, data=param, headers=self.headers)
        else:
            return None

        if r.status_code == 200:
            return r
        else:
            self.log.add_log("XiaolanSkillHass: Request " + str(r.status_code),
                             3)
            self.log.add_log("    Data: " + str(param), 1)
            self.log.add_log("    Res: " + str(r.text), 1)
            return None

    def update_data(self):
        """
        更新所有数据
        :return:
        """
        self.log.add_log("XiaolanSkillHass: Update all data...", 1)
        self.get_services()
        self.get_entity_list()
        self.get_config()

        self.hass_settings["components"] = self.components
        self.hass_settings["unitSystem"] = self.unit_system
        self.hass_settings["domainServices"] = self.domain_services
        self.hass_settings["entityDict"] = self.entity_dict
        self.hass_settings["friendlyNameDict"] = self.friendly_name_dict

        self.setting["hassSettings"] = self.hass_settings

        json.dump(self.setting,
                  open("./data/json/setting.json", "w", encoding="utf-8"))

        self.log.add_log("XiaolanSkillHass: Updated", 1)

    def get_config(self):
        """
        获取hass的配置
        :return:
        """
        self.log.add_log("XiaolanSkillHass: Get config", 1)
        r = self.request(self.hass_addr + "/api/config", "get")
        if r is not None:
            res = r.json()
            self.components = res["components"]
            self.unit_system = res["unit_system"]

    def get_services(self):
        """
        获取所有服务
        :return:
        """
        self.log.add_log("XiaolanSkillHass: Get all services' info", 1)

        r = self.request(self.hass_addr + "/api/services", "get")
        if r is not None:
            res = r.json()
            for event in res:
                for service in event["services"]:
                    self.domain_services[
                        service] = event["domain"] + "/" + service

    def get_entity_list(self):
        """
        获取所有对象的状态
        :return:
        """
        self.log.add_log("XiaolanSkillHass: Get all entities' info", 1)

        r = self.request(self.hass_addr + "/api/states", "get")
        if r is not None:
            res = r.json()
            for event in res:
                state = self.get_state(event["entity_id"])
                self.entity_dict[event["entity_id"]] = {
                    "attr": state["attributes"],
                    "state": state["state"],
                    "friendly_name": state["friendly_name"]
                }
                self.friendly_name_dict[
                    state["friendly_name"]] = event["entity_id"]

    def get_state(self, entity_id):
        """
        获取指定对象的状态
        :param entity_id: 实体id
        :return:
        """
        self.log.add_log("XiaolanSkillHass: Get state of" + entity_id, 1)

        r = self.request(self.hass_addr + "/api/states/" + entity_id, "get")
        if r is not None:
            res = r.json()
            if r.status_code == 404:
                self.log.add_log(
                    "XiaolanSkillHass: Can't find " + entity_id + "'s state",
                    3)
                return None
            return res

    def call_service(self, domain, service, param):
        """
        呼叫某个服务
        :param domain: 服务所属领域
        :param service: 服务名
        :param param: 服务参数
        :return:
        """
        self.log.add_log(
            "XiaolanSkillHass: Call " + domain + "/" + service + " with " +
            str(param), 1)
        r = self.request(
            self.hass_addr + "/api/services/" + domain + "/" + service, "post",
            param)
        if r is not None:
            res = r.json()
            for event in res:
                self.entity_dict[event["entity_id"]] = {
                    "attr": event["attributes"],
                    "state": event["state"]
                }
            return res

    def call_service_v2(self, service_path, param):
        """
        呼叫某个服务
        :param service_path: 服务路径
        :param param: 服务参数
        :return:
        """
        self.log.add_log(
            "XiaolanSkillHass: Call " + service_path + " with " + str(param),
            1)
        r = self.request(self.hass_addr + "/api/services/" + service_path,
                         "post", param)
        if r is not None:
            res = r.json()
            for event in res:
                self.entity_dict[event["entity_id"]] = {
                    "attr": event["attributes"],
                    "state": event["state"]
                }
            return res
Beispiel #10
0
class BaiduTts():
    def __init__(self, log, setting):

        self.log = log
        self.setting = setting
        self.read_chunk = setting["playerSettings"]["readChunk"]
        self.player = XiaolanPlayer(log, setting)
        self.token = ""

        self.app_key = setting["apiSettings"]["tts"]["appKey"]
        self.secret_key = setting["apiSettings"]["tts"]["secretKey"]

        self.get_token()

    def get_token(self):
        """
        获取token
        :return:
        """
        param = {
            'grant_type': 'client_credentials',
            'client_id': self.app_key,
            'client_secret': self.secret_key
        }
        params = urllib.parse.urlencode(param)
        r = requests.get('http://openapi.baidu.com/oauth/2.0/token',
                         params=params)
        try:
            r.raise_for_status()
            token = r.json()['access_token']
            self.token = token
            return token
        except requests.exceptions.HTTPError:
            self.log.add_log(
                "BaiduTTS: Getting token http error! Status code: " +
                str(r.status_code), 3)
            return ""

    def put_data(self, data):
        """
        将数据放入player的输出队列中(2号线程)
        :param data: 要被放入的总数据
        :return:
        """
        self.log.add_log(
            "BaiduTts: Putting data start, put chunk data into player's queue",
            1)

        start = 0
        end = self.read_chunk - 1

        c_data = data[start:end]
        while c_data != "":
            self.log.add_log("BaiduTts: Send chunk to player's queue",
                             0,
                             is_print=False)
            self.player.put(c_data)
            start = end + 1
            end += self.read_chunk
            c_data = data[start:end]

        for i in range(0, 2):
            self.player.put('')

    def start(self, text, is_play=True):
        """
        开始生成语音
        :param text: 要转换的文本
        :param is_play: 是否立即播放
        :return:
        """
        data = {
            'tex': text,
            'lan': 'zh',
            'tok': self.token,
            'ctp': 1,
            'cuid': 'b0-10-41-92-84-4d',
            'per': 4,
            'cue': 6
        }
        r = requests.post('http://tsn.baidu.com/text2audio',
                          data=data,
                          headers={'content-type': 'application/json'},
                          stream=True)

        if r.status_code == 200:
            r.raw.decode_content = True
            if is_play:
                put_data_t = threading.Thread(target=self.put_data,
                                              args=(r.raw, ))
                stream_out_t = threading.Thread(
                    target=self.player.stream_output, args=())
                put_data_t.start()
                stream_out_t.start()
            else:
                with open("./data/audio/say.wav", "wb") as f:
                    shutil.copyfileobj(r.raw, f)
        else:
            self.log.add_log(
                "BaiduTTS: Tts network error! Status code: " +
                str(r.status_code), 3)
Beispiel #11
0
class BaiduTts():
    def __init__(self, log, setting):

        self.log = log
        self.setting = setting
        self.read_chunk = setting["playerSettings"]["readChunk"]
        self.player = XiaolanPlayer(log, setting)
        self.token = ""

        self.app_key = setting["apiSettings"]["tts"]["appKey"]
        self.secret_key = setting["apiSettings"]["tts"]["secretKey"]

        self.get_token()

    def get_token(self):
        """
        获取token
        :return:
        """
        param = {
            'grant_type': 'client_credentials',
            'client_id': self.app_key,
            'client_secret': self.secret_key
        }
        params = urllib.parse.urlencode(param)
        r = requests.get('http://openapi.baidu.com/oauth/2.0/token',
                         params=params)
        try:
            r.raise_for_status()
            token = r.json()['access_token']
            self.token = token
            return token
        except requests.exceptions.HTTPError:
            self.log.add_log(
                "BaiduTTS: Getting token http error! Status code: " +
                str(r.status_code), 3)
            return ""

    def put_data(self, data):
        """
        将数据放入player的输出队列中(2号线程)
        :param data: 要被放入的总数据
        :return:
        """
        self.log.add_log(
            "BaiduTts: Putting data start, put chunk data into player's queue",
            1)

        start = 0
        end = self.read_chunk - 1

        c_data = data[start:end]
        while c_data != b'':
            self.log.add_log("BaiduTts: Send chunk to player's queue",
                             0,
                             is_print=False)
            self.player.put(c_data)
            start = end + 1
            end += self.read_chunk
            c_data = data[start:end]

        for i in range(0, 2):
            time.sleep(0.05)
            self.player.put('')

    def start(self, text, is_play=True):
        """
        开始生成语音
        :param text: 要转换的文本
        :param is_play: 是否立即播放
        :return:
        """
        text = urllib.parse.quote_plus(text)
        data = {
            'tex': text,
            'lan': 'zh',
            'tok': self.token,
            'ctp': 1,
            'cuid': 'xiaolan-client',
            'per': 4,
            'aue': 6
        }

        data = urllib.parse.urlencode(data)
        req = urllib.request.Request('http://tsn.baidu.com/text2audio',
                                     data.encode('utf-8'))

        f = urllib.request.urlopen(req)
        result_str = f.read()

        headers = dict(
            (name.lower(), value) for name, value in f.headers.items())

        if "audio" in headers["content-type"]:
            print("BaiduTts: tts requested success")
            if is_play:
                put_data_t = threading.Thread(target=self.put_data,
                                              args=(result_str, ))
                stream_out_t = threading.Thread(
                    target=self.player.stream_output, args=())
                put_data_t.start()
                stream_out_t.start()
            else:
                with open("./data/audio/say.wav", "wb+") as f:
                    f.write(result_str)
                self.player.say()
                return True
        else:
            self.log.add_log("BaiduTTS: Tts network error! Respnse: ", 3)
            print(result_str)
            return False
Beispiel #12
0
class XiaolanSkillMusicPlayer(object):
    def __init__(self, log, setting, text):

        self.log = log
        self.setting = setting
        self.text = text

        self.stt = BaiduStt(log, setting)
        self.tts = BaiduTts(log, setting)
        self.nlu = XiaolanNlu(log, setting)
        self.player = XiaolanPlayer(log, setting)
        self.recorder = XiaolanRecorder(log, setting)

        self.play_list = {}

        self.nlu_dict = {
            "恢复": "resume",
            "播放": "play",
            "暂停": "pause",
            "下一首": "next",
            "上一首": "previous",
            "添加": "add",
            "退出": "quit"
        }

    def start(self):
        """
        启动
        :return:
        """
        self.log.add_log("XiaolanSkillMusicPlayer: Start", 1)

        self.tts.start("请问你要听什么音乐呢?")
        self.main()

    def conversation(self):
        """
        对话(减少重复代码)
        :return:
        """
        self.player.ding()
        self.recorder.record()
        self.player.dong()

        text = self.stt.start()

        intent, slot = self.nlu.skill_nlu(text, self.nlu_dict)
        if intent is None:
            self.log.add_log("XiaolanSkillMusicPlayer: Intent is none, quit",
                             1)
            self.tts.start("不好意思,我没明白,退出咯~")
            intent, text, slot = None, None, None
        self.log.add_log(
            "XiaolanSkillMusicPlayer: Intent: " + intent + " Text: " + text +
            " Slot: " + slot, 1)
        return intent, text, slot

    def main(self):
        """
        主业务逻辑
        :return:
        """
        intent, text, slot = self.conversation()

        if intent == "quit":
            self.tts.start("好的,那么bye~")
            return
        elif intent == "play":
            result = self.search(slot)
            if result is None:
                self.tts.start("没有找到你要找的曲目,你可以试着换个说法")
                self.main()
            else:
                self.play(result)
        else:
            pass
            # NOT FINISHED

        self.tts.start("还要继续吗?")

        intent, text, slot = self.conversation()
        if intent is None:
            return
        if intent == "yes":
            self.main()
        else:
            self.tts.start("好的,我走啦,下次再见~")

    def play(self, data):
        """
        播放
        :param data: 播放参数
        :return:
        """
        self.log.add_log("XiaolanSkillMusicPlayer: Start play...", 1)

        url = "https://wwwapi.kugou.com/yy/index.php?"
        param = {"r": "play/getdata", "hash": data["fileHash"]}
        param = urllib.parse.urlencode(param)

        r = requests.get(url + param)
        if r.status_code == 200:
            res = r.json()
            play_url = res["data"]["play_url"].replace("\\\\", "")

            self.log.add_log("XiaolanSkillMusicPlayer: Play url: " + play_url,
                             1)

            r2 = requests.get(play_url)
            file = open("./data/audio/music" + data["fileFormat"], "wb")
            file.write(r2.content)
            file.close()

            self.player.format_converter(
                "/data/audio/music" + data["fileFormat"], data["fileFormat"],
                ".wav")

            self.player.play("./data/audio/music.wav")
        else:
            self.log.add_log(
                "XiaolanSkillMusicPlayer: Can't get: " + data["fileHash"] +
                " 's play url. Http error", 3)
            return None

    def search(self, keyword):
        """
        搜索曲目
        :param keyword: 关键词
        :return:
        """
        self.log.add_log(
            "XiaolanSkillMusicPlayer: Search for song named: " + keyword, 1)

        url = "https://complexsearch.kugou.com/v2/search/song?"

        param = {
            "callback": "callback123",
            "keyword": keyword,
            "page": 1,
            "pagesize": 30,
            "bitrate": 0,
            "isfuzzy": 0,
            "tag": "em",
            "inputtype": 0,
            "platform": "WebFilter",
            "userid": -1,
            "clientver": 2000,
            "iscorrection": 1,
            "privilege_filter": 0,
            "srcappid": 2919,
            "clienttime": 1601950702328,
            "mid": 1601950702328,
            "uuid": 1601950702328,
            "dfid": "-",
            "signature": "10C3F1C841892B24FD43DE172EB765EA"
        }
        param = urllib.parse.urlencode(param)

        r = requests.get(url + param)
        if r.status_code == 200:
            res = r.json()
            data = res["data"]["lists"]
            file_format = "." + data[0]["HQExtName"]
            song_name = data[0]["SongName"].replace("em>", "").replace(
                "<", "").replace("/", "")
            singer_name = data[0]["SingerName"]
            file_hash = data[0]["HQFileHash"]

            self.log.add_log(
                "XiaolanSkillMusicPlayer: song: " + song_name + " singer: " +
                singer_name + " file hash: " + file_hash, 1)

            return {
                "fileFormat": file_format,
                "fileHash": file_hash,
                "songName": song_name,
                "singerName": singer_name
            }
        else:
            self.log.add_log(
                "XiaolanSkillMusicPlayer: Search: " + keyword +
                " failed. Http error", 3)
            return None