def aksk_connect(ak, sk, url, params, http_method, config=None): """ 根据url,返回json :param ak: ak :param sk: sk :param url: 完整请求url :param params: 请求参数, dict :param http_method: 请求方法,'POST' or 'GET', 其他会报错 :param config: SisConfig(), 配置超时和代理 :return: http返回结果转化为json """ sis_config = config if sis_config is None: sis_config = SisConfig() if not isinstance(sis_config, SisConfig): error_msg = 'the param \'config\' in aksk_connect must be SisConfig class' logger.error(error_msg) raise ClientException(error_msg) signed_headers = _get_signed_headers(ak, sk, url, params, http_method) time_out = (sis_config.get_connect_timeout(), sis_config.get_read_timeout()) resp = http_utils.http_connect(url, signed_headers, params, http_method, time_out, sis_config.get_proxy()) json_result = http_utils.parse_resp(resp) if resp is not None: resp.close() return json_result
def _on_message(ws, message): result = json.loads(message) _check_result(result) result_type = result['resp_type'] trace_id = result['trace_id'] if result_type == 'START': self._callback.on_start(trace_id) elif result_type == 'EVENT': event = result['event'] if event == 'EXCEEDED_AUDIO': logger.warn( 'the duration of the audio is too long, the rest won\'t be recognized' ) self._status = 'end' elif event == 'EXCEEDED_SILENCE': logger.error( 'silent time is too long, the audio won\'t be recognized' ) self._callback.on_error( 'silent time is too long, the audio won\'t be recognized' ) self._status = 'error' elif event == 'VOICE_END': logger.warn( 'detect voice end, the rest won\'t be recognized') self._status = 'end' elif result_type == 'RESULT': self._callback.on_response(result) elif result_type == 'END': self._status = 'end' self._callback.on_end(trace_id) else: logger.error('%s don\'t belong to any type' % result_type)
def _generate_request_proxy(proxy): if proxy is None: return proxy if not isinstance(proxy, list) or (not len(proxy) == 2 and not len(proxy) == 4): logger.error( 'Proxy must be list, the format is [host, port] or [host, port, username, password]' ) raise ClientException( 'Proxy must be list, the format is [host, port] or [host, port, username, password]' ) proxy_str = str(proxy[0]) + ':' + str(proxy[1]) if len(proxy) == 2: proxy = { 'http': 'http://' + proxy_str, 'https': 'https://' + proxy_str } else: proxy = { 'http': 'http://' + str(proxy[2]) + ':' + str(proxy[3]) + '@' + proxy_str, 'https': 'https://' + str(proxy[2]) + ':' + str(proxy[3]) + '@' + proxy_str } return proxy
def encode_file(file_path): if not os.path.exists(file_path): logger.error('The Path %s doesn\'t exist' % file_path) raise ClientException('The Path %s doesn\'t exist' % file_path) with open(file_path, 'rb') as f: data = f.read() base64_data = str(base64.b64encode(data), 'utf-8') return base64_data
def get_token(user_name, password, domain_name, region, url=None, config=SisConfig()): """ 获取token :param user_name: 用户名 :param password: 密码 :param domain_name: 账户名,一般等同用户名 :param region: 区域,如cn-north-4 :param url: 请求token的url,可使用默认值 :param config 配置信息 :return: 请求的token """ if url is None: url = 'https://iam.' + region + '.myhuaweicloud.com/v3/auth/tokens' if not isinstance(config, SisConfig): error_msg = 'the param \'config\' in token_service must be SisConfig class' logger.error(error_msg) raise ClientException(error_msg) time_out = (config.get_connect_timeout(), config.get_read_timeout()) proxy = config.get_proxy() auth_data = { "auth": { "identity": { "password": { "user": { "name": user_name, "password": password, "domain": { "name": domain_name } } }, "methods": ["password"] }, "scope": { "project": { "name": region } } } } headers = {'Content-Type': 'application/json'} req = http_utils.http_connect(url, headers, auth_data, 'POST', time_out, proxy) if 'X-Subject-Token' not in req.headers: logger.error('Error occurs in getting token, %s' % req.text) raise ClientException('Error occurs in getting token, %s' % req.text) token = req.headers['X-Subject-Token'] return token
def generate_scheme_host_uri(url): if url.find('//') == -1 or url.find('com') == -1: error_msg = '%s is invalid' % url logger.error(error_msg) raise ClientException(error_msg) split1s = url.split('//') split2s = split1s[1].split('com') scheme = split1s[0] + '//' host = split2s[0] + 'com' uri = split2s[1] return scheme, host, uri
def create(self, request): """ 创建热词表 :param request: 热词表请求 :return: 热词表id """ if not isinstance(request, HotWordRequest): logger.error('the parameter in \'create_hot_word(request)\' should be HotWordRequest class') raise ClientException('the parameter in \'create_hot_word(request)\' should be HotWordRequest class') url = self._service_endpoint + '/v1/' + self._project_id + '/asr/vocabularies' params = request.construct_params() result = aksk_service.aksk_connect(self._ak, self._sk, url, params, 'POST', self._sis_config) return result
def construct_params(self): paras = dict() paras['encode_type'] = self._encode_type paras['sample_rate'] = self._sample_rate if self._data is None and self._url is None: logger.error('data and url can\'n be both None') raise ClientException('data and url can\'n be both None, you can choose set one parameter') if self._data is not None and self._url is not None: logger.warn('when data and url are not both None, only data can take effect.') if self._data is not None: paras['data'] = self._data else: paras['url'] = self._url return paras
def assessment_video(self, request): """ 多模态评测接口 :param request: 多模态评测请求 :return: 响应结果,返回为json格式 """ if not isinstance(request, PaVideoRequest): error_msg = 'the parameter in \'assessment_video(request)\' should be PaVideoRequest class' logger.error(error_msg) raise ClientException(error_msg) url = self._service_endpoint + '/v1/' + self._project_id + '/assessment/video' params = request.construct_parameter() result = aksk_service.aksk_connect(self._ak, self._sk, url, params, 'POST', self._sis_config) return result
def get_short_response(self, request): """ 一句话识别接口 :param request: 一句话识别请求AsrCustomShortRequest :return: 一句话识别响应结果,返回为json格式 """ if not isinstance(request, AsrCustomShortRequest): error_msg = 'the parameter in \'get_short_response(request)\' should be AsrCustomShortRequest class' logger.error(error_msg) raise ClientException(error_msg) url = self._service_endpoint + '/v1/' + self._project_id + '/asr/short-audio' params = request.construct_params() result = aksk_service.aksk_connect(self._ak, self._sk, url, params, 'POST', self._sis_config) return result
def _check_result(result): result_str = json.dumps(result) if 'error_code' in result and 'error_msg' in result: logger.error(result_str) # 睡眠2s保证关闭前,发送端有充足时间收到关闭请求。 self._status = 'close' self._callback.on_error(result_str) time.sleep(2) self._ws.close() raise ClientException(result_str) if 'resp_type' not in result: self._status == 'error' error_msg = 'result doesn\'t contain key resp, result is %s' % result_str logger.error(error_msg) self._callback.on_error(error_msg) raise ClientException(error_msg)
def get_asr_response(self, request): """ 短语音识别接口 :param request: 短语音识别请求 AsrRequest :return: 短语音识别响应,json格式 """ if not isinstance(request, AsrRequest): logger.error( 'the parameter in \'get_asr_response(request)\' should be AsrRequest class' ) raise ClientException( 'the parameter in \'get_asr_response(request)\' should be AsrRequest class' ) url = self._service_endpoint + '/v1.0/voice/asr/sentence' params = request.construct_params() result = aksk_service.aksk_connect(self._ak, self._sk, url, params, 'POST', self._sis_config) return result
def submit_job(self, request): """ 录音文件识别,提交任务接口 :param request: 录音文件识别请求 :return: job_id """ if not isinstance(request, AsrCustomLongRequest): error_msg = 'the parameter in \'submit_job(request)\' should be AsrCustomLongRequest class' logger.error(error_msg) raise ClientException(error_msg) url = self._service_endpoint + '/v1/' + self._project_id + '/asr/transcriber/jobs' params = request.construct_parameter() result = aksk_service.aksk_connect(self._ak, self._sk, url, params, 'POST', self._sis_config) if 'job_id' not in result: error_msg = 'The result of long audio transcription doesn\'t contain key job_id, result is ' % result logger.error(error_msg) raise ClientException(error_msg) return result['job_id']
def parse_resp(resp): """ requests响应转化为json格式 :param resp: requests请求返回的响应 :return: json """ if resp is None or resp.text is None or resp.text == '': return None text = resp.text try: result = json.loads(text) except Exception as e: error_msg = 'Parsing json failed, the text is %s' % text logger.error(error_msg) raise ClientException(error_msg) if 'error_code' in result and 'error_msg' in result: error_msg = json.dumps(result) logger.error(error_msg) raise ServerException(result['error_code'], result['error_msg']) return result
def get_ttsc_response(self, request): """ 定制语音合成接口 :param request: 定制语音合成请求,TtsCustomRequest :return: 请求结果,json格式 """ if not isinstance(request, TtsCustomRequest): logger.error('the parameter in \'get_ttsc_response(request)\' should be TtsCustomRequest class') raise ClientException('the parameter in \'get_ttsc_response(request)\' should be TtsCustomRequest class') url = self._service_endpoint + '/v1/' + self._project_id + '/tts' params = request.construct_params() result = aksk_service.aksk_connect(self._ak, self._sk, url, params, 'POST', self._sis_config) if 'result' not in result: error_msg = 'The result of tts customization is invalid. Result is %s ' % json.dumps(result) logger.error(error_msg) raise ClientException(error_msg) if request.get_saved(): base_str = result['result']['data'] io_utils.save_audio_from_base64str(base_str, request.get_saved_path()) result['is_saved'] = True result['saved_path'] = request.get_saved_path() return result
def _on_error(ws, error): logger.error(error) self._status = 'error' self._callback.on_error(error)
def http_connect(url, header, data, http_method='POST', time_out=5, proxy=None): """ post请求,带有header信息(用于认证) :param url: - :param header: 头部 :param data: post数据 :param time_out: 超时 :param proxy: 代理 :param http_method: http方法,目前支持put、delete、post、get :return: http请求的response """ if isinstance(data, dict): data = json.dumps(data) if proxy is not None: proxy = _generate_request_proxy(proxy) else: proxy = {'http': None, 'https': None} # 加入重试机制 count = 0 resp = None while count < NUM_MAX_RETRY: try: if http_method == 'POST': resp = requests.post(url, headers=header, data=data, timeout=time_out, verify=False, proxies=proxy) elif http_method == 'GET': resp = requests.get(url, headers=header, params=data, timeout=time_out, verify=False, proxies=proxy) elif http_method == 'PUT': resp = requests.put(url, headers=header, data=data, timeout=time_out, verify=False, proxies=proxy) elif http_method == 'DELETE': resp = requests.delete(url, headers=header, params=data, timeout=time_out, verify=False, proxies=proxy) else: logger.error('%s is invalid' % http_method) raise ClientException('%s is invalid' % http_method) break except requests.exceptions.RequestException as e: logger.error( 'Error occurs in %s, the client will retry 5 times. Error message is %s' % (http_method, e)) count += 1 if resp is None: logger.error('%s Response is empety, url is %s' % (http_method, url)) raise ClientException('%s Response is empety, url is %s' % (http_method, url)) return resp
def _check_request(request): if not isinstance(request, RasrRequest): error_msg = 'The parameter of request in RasrClient should be RasrRequest class' logger.error(error_msg) raise ClientException(error_msg)
def __init__(self, user_name, password, domain_name, region, project_id, callback, config=SisConfig(), service_endpoint=None, token_url=None, retry_sleep_time=1): """ 实时语音转写client初始化 :param user_name: 用户名 :param password: 密码 :param domain_name: 账户名,一般等同用户名 :param region: 区域,如cn-north-4 :param project_id: 项目ID,可参考https://support.huaweicloud.com/api-sis/sis_03_0008.html :param callback: 回调类RasrCallBack,用于监听websocket连接、响应、断开、错误等 :param service_endpoint: 终端节点,一般使用默认即可 :param token_url: 请求token的url,一般使用默认即可 :param retry_sleep_time: 当websocket连接失败重试的间隔时间,默认为5s """ if service_endpoint is None: self._service_endpoint = 'wss://sis-ext.' + region + '.myhuaweicloud.com' else: self._service_endpoint = service_endpoint if token_url is None: self._token_url = 'https://iam.' + region + '.myhuaweicloud.com/v3/auth/tokens' else: self._token_url = token_url if not isinstance(callback, RasrCallBack): logger.error('The parameter callback must be RasrCallBack class') raise ClientException( 'The parameter callback must be RasrCallBack class') if not isinstance(config, SisConfig): logger.error('The parameter config must by SisConfig class') raise ClientException( 'The parameter config must by SisConfig class') self._project_id = project_id self._config = config # token 缓存必须在client进行,才可以在多线程中生效。 now_time = time.time() self._token = None if user_name in user_dict and user_name in time_dict: token = user_dict[user_name] save_time = time_dict[user_name] if now_time - save_time < 5 * 3600: logger.info('use token cache') self._token = token if self._token is None: self._token = token_service.get_token(user_name, password, domain_name, region, url=self._token_url, config=self._config) user_dict[user_name] = self._token time_dict[user_name] = now_time self._callback = callback self._status = 'pre_start' self._request = None self._retry_sleep_time = retry_sleep_time
def on_error(self, error): logger.error(error)
def _connect(self, url): def _check_result(result): result_str = json.dumps(result) if 'error_code' in result and 'error_msg' in result: logger.error(result_str) # 睡眠2s保证关闭前,发送端有充足时间收到关闭请求。 self._status = 'close' self._callback.on_error(result_str) time.sleep(2) self._ws.close() raise ClientException(result_str) if 'resp_type' not in result: self._status == 'error' error_msg = 'result doesn\'t contain key resp, result is %s' % result_str logger.error(error_msg) self._callback.on_error(error_msg) raise ClientException(error_msg) def _on_open(ws): logger.info('websocket open') self._status = 'start' self._callback.on_open() def _on_message(ws, message): result = json.loads(message) _check_result(result) result_type = result['resp_type'] trace_id = result['trace_id'] if result_type == 'START': self._callback.on_start(trace_id) elif result_type == 'EVENT': event = result['event'] if event == 'EXCEEDED_AUDIO': logger.warn( 'the duration of the audio is too long, the rest won\'t be recognized' ) self._status = 'end' elif event == 'EXCEEDED_SILENCE': logger.error( 'silent time is too long, the audio won\'t be recognized' ) self._callback.on_error( 'silent time is too long, the audio won\'t be recognized' ) self._status = 'error' elif event == 'VOICE_END': logger.warn( 'detect voice end, the rest won\'t be recognized') self._status = 'end' elif result_type == 'RESULT': self._callback.on_response(result) elif result_type == 'END': self._status = 'end' self._callback.on_end(trace_id) else: logger.error('%s don\'t belong to any type' % result_type) def _on_close(ws): logger.info('websocket close') self._status = 'close' self._callback.on_close() def _on_error(ws, error): logger.error(error) self._status = 'error' self._callback.on_error(error) # 重试机制 headers = {'X-Auth-Token': self._token} sslopt = {"cert_reqs": ssl.CERT_NONE} retry_count = 5 for i in range(retry_count): self._status = 'pre_start' self._ws = websocket.WebSocketApp(url, headers, on_open=_on_open, on_close=_on_close, on_message=_on_message, on_error=_on_error) self._thread = threading.Thread( target=self._ws.run_forever, args=(None, sslopt, self._config.get_connect_lost_timeout() + 1, self._config.get_connect_lost_timeout())) self._thread.daemon = True self._thread.start() connect_count = int(self._config.get_connect_timeout() / connect_sleep_time) for j in range(connect_count): if self._status != 'pre_start': break else: time.sleep(connect_sleep_time) if self._status == 'start': break else: logger.error( 'connect meets error, it will retry %d times, now it is %d' % (retry_count, i + 1)) time.sleep(self._retry_sleep_time) if self._status == 'pre_start' or self._status == 'close' or self._status == 'error' or self._status == 'end': logger.error('websocket connect failed, url is %s' % url) raise ClientException('websocket connect failed, url is %s' % url)