最近、WeChat のパスワード赤い封筒機能が導入されようとしていて、上司は、今、ウール パーティーが蔓延しているので、注意しないと 100,000 のプロモーション用赤い封筒が 2 つで使い果たされてしまうのではないかと心配になりました。それなら、同じ人が赤い封筒を受け取っているかどうかを確認する機能を作ることができますか? 彼は頭を撫でながら、「トム、私たちのために声紋認識をしてください!」と続けました。
具体的なプロセスは次のとおりです。
声紋登録者(最終イメージ)
声紋ログイン (最終レンダリング)
アップロードファイルの識別:
pm2スレッド
声紋認識サービスプロバイダーは、クライアントを使用して直接通話することができず、音声もサポートしていないため、接続するには独自のサーバーを開発する必要があります。
テクノロジースタック koa + co-wecaht-api + mysql + ffmpeg + pm2 + knex
注: サービス プロバイダーは WeChat amr ファイルをサポートしていないため、ffmpeg を使用して WeChat オーディオ amr ファイルを wav にトランスコードする必要があります。
以下は関連するコードの一部です (開いたまま)。 。
WeChat jssdk 開発 WeChat API にすでに精通している場合は、次のセクションに進んでください。
WeChatトークンを取得する
var api = await new WechatAPI (
config . appid ,
config . appsecret ,
async ( ) => {
// 传入一个获取全局token的方法
var txt = await fs . readFile ( "./token/access_token.txt" , "utf8" ) ;
return JSON . parse ( txt ) ;
} ,
async token => {
// 请将token存储到全局,跨进程、跨机器级别的全局,比如写到数据库、redis等
// 这样才能在cluster模式及多机情况下使用,以下为写入到文件的示例
await fs . writeFile ( "./token/access_token.txt" , JSON . stringify ( token ) ) ;
}
) ;
注: トークン ファイルを読み取れない場合は、対応するディレクトリに新しいテキスト ファイル (access_token.txt など) を手動で作成します。
WeChatの署名を取得する
var jsapi_ticket = await api . getLatestTicket ( ) ;
let nonce_str = 'abcdefg' ; // 密钥,字符串任意,可以随机生成
let timestamp = parseInt ( new Date ( ) . getTime ( ) / 1000 ) + '' ; // 时间戳
let url = ctx . request . body . url ; // 使用接口的url链接,不包含#后的内容
let str = 'jsapi_ticket=' + jsapi_ticket . ticket + '&noncestr=' + nonce_str + '×tamp=' + timestamp + '&url=' + url ;
let signature = sha1 ( str ) ;
ctx . body = {
appId : config . appid ,
timestamp : timestamp ,
nonceStr : nonce_str ,
signature : signature
}
クロスドメインリクエスト
const Koa = require ( "koa" ) ;
const app = new Koa ( ) ;
const cors = require ( "koa-cors" ) ;
... . .
app . use (
cors ( {
origin : "http://www.xxxx.com" ,
maxAge : 5 ,
credentials : true ,
allowMethods : [ "OPTIONS" , "GET" , "POST" , "DELETE" ] ,
allowHeaders : [ 'Content-Type' , 'Accept' ]
} )
) ;
ffmpegトランスコーディング
const ffmpeg = require ( 'fluent-ffmpeg' ) ;
... .
var command = ffmpeg ( _delPath . amr )
. audioBitrate ( '16k' ) //16k音频采样率
. audioFrequency ( 16000 ) //16比特音频信号
. audioQuality ( 10 ) //音频质量
. on ( 'end' , function ( ) {
console . log ( 'file has been converted succesfully' ) ;
resolve ( ) ;
} )
. on ( 'error' , function ( err ) {
reject ( err . message )
console . log ( 'an error happened: ' + err . message ) ;
} )
. save ( _delPath . fix ) ;
声紋サーバーを送信する
const rp = require ( "request-promise" ) ;
... . .
var vprData = {
method : "POST" ,
url : "http://www.xxxx.com" ,
headers : {
"cache-control" : "no-cache" ,
"x-udid" : "xxxxxx" ,
"x-session-key" : "xxxx" ,
"x-task-config" : "xxxxxx" ,
"x-request-date" : "xxxxxx" ,
"x-sdk-version" : "5.1" ,
"x-app-key" : "xxxxxxx"
} ,
formData : {
// Like <input type="file" name="file">
file : {
value : fs . createReadStream ( soundData . path ) ,
options : {
filename : soundData . name ,
contentType : soundData . type //mp3 = audio/mpeg, wav = audio/wav
}
}
}
} ;
var xml = await rp ( vprData ) ;
//xml to json
var resJson = { } ;
var parseString = require ( 'xml2js' ) . parseString ;
await new Promise ( ( resolve , reject ) => {
parseString ( xml . toString ( ) , async ( err , result ) => {
resJson = result . ResponseInfo ;
//do something
resolve ( ) ;
} ) ;
} ) ;
テクノロジースタック vue + vue-router + axios。
WeChatの長押しポップアップコピーボタンを削除
mounted ( ) {
document . oncontextmenu = function ( e ) {
e . preventDefault ( ) ;
} ;
//初始化 微信jssdk
vm . wx_init ( ) ;
}
WeChatの署名を取得してイベントを登録する
wx . config ( {
debug : false , // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId : res . appId , // 必填,公众号的唯一标识
timestamp : res . timestamp , // 必填,生成签名的时间戳
nonceStr : res . nonceStr , // 必填,生成签名的随机串
signature : res . signature , // 必填,签名,见附录1
jsApiList : [
"onMenuShareTimeline" ,
"onMenuShareAppMessage" ,
"uploadVoice" ,
"startRecord" ,
"playVoice" ,
"stopRecord" ,
"onVoicePlayEnd"
] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
} ) ;
記録が正式に開始されると同時に許可を求められることを避けるために、事前にユーザーに記録機能の許可を求めてください。現時点では、記録機能のステータスは制御不能です。
if ( ! localStorage . rainAllowRecord || localStorage . rainAllowRecord !== "true" ) {
wx . startRecord ( {
success : function ( ) {
localStorage . rainAllowRecord = "true" ;
wx . stopRecord ( ) ;
} ,
cancel : function ( ) {
alert ( "用户拒绝授权录音" ) ;
}
} ) ;
}
さて、話は簡単です、コードを見せてください。
使用方法
git clone https: //github.com/ssttm169/tom-vpr.git
cd server
npm i / yarn
npm run dev //本地开发
npm start //服务器跑
//或者
cd client
npm i / yarn
npm run dev //本地开发
npm start //服务上跑客户端