玩具开机提示语,为多个玩具发送点播,聊天界面,app录音,app与服务器端文件传输,简单的对话
一、玩具开机提示语
先下载github代码,下面的操作,都是基于这个版本来的!
https://github.com/987334176/Intelligent_toy/archive/v1.2.zip
注意:由于涉及到版权问题,此附件没有图片和音乐。请参考链接,手动采集一下!
请参考链接:
https://www.cnblogs.com/xiao987334176/p/9647993.html#autoid-3-4-0
判断设备id
每一个玩具,都有设备id。如果在设备表中,提示找小主人。否则提示 联系厂家。
如果在玩具表中,提示开机!
进入flask项目,将jquery.min.js下载到static目录,下载链接如下:
https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js
使用jquery的原因,是因为要发送ajax的POST请求。使用$.post{}
修改 templates-->index.html,增加开机按钮
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <audio src="" autoplay="autoplay" controls id="player"></audio> <br> <input type="text" id="device_id"> <button onclick="start_toy()">玩具开机键</button> <br> <button onclick="start_reco()">开始废话</button> <br> <button onclick="stop_reco()">发送语音</button> </body> <script src="/static/recorder.js"></script> <script src="/static/jquery.min.js"></script> <script type="application/javascript"> // 获取音频文件 var get_file = "http://192.168.11.24:9527/get_audio/"; // 创建 WebSocket 对象 var ws = new WebSocket("ws://192.168.11.24:9528/toy/123456"); var reco = null; // 创建AudioContext对象 var audio_context = new AudioContext(); //要获取音频和视频 navigator.getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia); // 拿到媒体对象,允许音频对象 navigator.getUserMedia({audio: true}, create_stream, function (err) { console.log(err) }); //创建媒体流容器 function create_stream(user_media) { var stream_input = audio_context.createMediaStreamSource(user_media); // 给Recoder 创建一个空间,麦克风说的话,都可以录入。是一个流 reco = new Recorder(stream_input); } function start_reco() { //开始录音 reco.record(); //往里面写流 } function stop_reco() { //停止录音 reco.stop(); //停止写入流 get_audio(); //调用自定义方法 reco.clear(); //清空容器 } function get_audio() { // 获取音频 reco.exportWAV(function (wav_file) { ws.send(wav_file); //使用websocket连接发送数据给后端 }) } ws.onmessage = function (data) { // 客户端接收服务端数据时触发 // console.log(get_file + data.data); var content = JSON.parse(data.data); console.log(content); // 修改id为player的src属性,实现自动播放 document.getElementById("player").src = get_file + content.data; console.log(content.from_user + "给你点了一首歌"); }; function start_toy() { // 玩具开机 // 获取输入的设备id var device_id = document.getElementById("device_id").value; // 发送post请求 $.post( // 这里的地址必须是127.0.0.1,否则会有跨域问题 "http://127.0.0.1:9527/device_toy_id", // 发送设备id {device_id: device_id}, function (data) { console.log(data); }, "json" // 规定预期的服务器响应的数据类型为json ); } </script> </html>
修改 serv-->toys.py,增加视图函数device_toy_id
from flask import Blueprint, request, jsonify from setting import MONGO_DB from setting import RET from bson import ObjectId toy = Blueprint("toy", __name__) @toy.route("/toy_list", methods=["POST"]) def toy_list(): # 玩具列表 user_id = request.form.get("user_id") # 用户id # 查看用户信息 user_info = MONGO_DB.users.find_one({"_id": ObjectId(user_id)}) bind_toy = user_info.get("bind_toy") # 获取绑定的玩具 bind_toy_id = [] # 玩具列表 for toy_id in bind_toy: # 获取玩具列表中的所有玩具id bind_toy_id.append(ObjectId(toy_id)) # 一次性查询多个玩具 toys_list = list(MONGO_DB.toys.find({"_id": {"$in": bind_toy_id}})) for index,item in enumerate(toys_list): # 将_id转换为字符串 toys_list[index]["_id"] = str(item.get("_id")) RET["code"] = 0 RET["msg"] = "" RET["data"] = toys_list return jsonify(RET) @toy.route("/device_toy_id", methods=["POST"]) def device_toy_id(): device_id = request.form.get("device_id") # 获取设备id # 判断设备id是否在设备表中 if MONGO_DB.devices.find_one({"device_id": device_id}): # 查询设备id是否在玩具表中 toy_info = MONGO_DB.toys.find_one({"device_id": device_id}) if toy_info: return jsonify("开机") else: # 已授权的设备,但是没有绑定主人 return jsonify("找小主人") else: # 不在设备表中,说明是未授权,或者是冒牌的! return jsonify("联系玩具厂")
重启 manager.py,访问首页
输入一段数字,点击玩具开机键,效果如下:
打开 MongoDB客户端,复制一个不在玩具表(toys)中的设备id。效果如下:
复制一个,在玩具表中的设备id,效果如下:
提示语
后端逻辑判断,大致搞定了。下面来录制提示语,这里使用百度ai的接口。
在项目根目录,新建目录utils,在此目录下新建baidu_ai.py
此时,目录结构如下:
./
├── audio
├── audio_img
├── device_code
├── im_serv.py
├── manager.py
├── QRcode.py
├── serv
│ ├── content.py
│ ├── devices.py
│ ├── get_file.py
│ └── toys.py
├── setting.py
├── static
│ ├── jquery.min.js
│ └── recorder.js
├── templates
│ └── index.html
├── utils
│ └── baidu_ai.py
└── xiaopapa.py
修改 setting.py,增加百度AI的秘钥。注意:后5位被我修改了,请改为自己的!
import pymongo client = pymongo.MongoClient(host="127.0.0.1", port=27017) MONGO_DB = client["bananabase"] RET = { # 0: false 2: True "code": 0, "msg": "", # 提示信息 "data": {} } XMLY_URL = "http://m.ximalaya.com/tracks/" # 喜马拉雅链接 CREATE_QR_URL = "http://qr.liantu.com/api.php?text=" # 生成二维码API # 文件目录 import os AUDIO_FILE = os.path.join(os.path.dirname(__file__), "audio") # 音频 AUDIO_IMG_FILE = os.path.join(os.path.dirname(__file__), "audio_img") # 音频图片 DEVICE_CODE_PATH = os.path.join(os.path.dirname(__file__), "device_code") # 二维码 # 百度AI配置 APP_ID = "11712345" API_KEY = "3v3igzCkVFUDwFByNEE12345" SECRET_KEY = "jRnwLE7kzC1aRi2FD10OQY3y9Og12345" SPEECH = { "spd": 4, 'vol': 5, "pit": 8, "per": 4 }
修改 baidu_ai.py,录制开机语音
from aip import AipSpeech import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 项目根目录 import sys sys.path.append(BASE_DIR) # 加入到系统环境变量中 import setting # 导入setting client = AipSpeech(setting.APP_ID,setting.API_KEY,setting.SECRET_KEY) res = client.synthesis("欢迎来到嘉禾智能亲子互动乐园","zh",1,setting.SPEECH) with open(os.path.join(setting.AUDIO_FILE,"success.mp3"),"wb") as f: f.write(res)
执行 baidu_ai.py,会在audio目录生成 success.mp3 文件。试听一下,感觉萌萌哒!
注意:语言文件的保存路径是audio。为什么呢?因为前端会调用get_audio接口。它是从audio目录读取的!
修改 baidu_ai.py,录制没有小主人语音
from aip import AipSpeech import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 项目根目录 import sys sys.path.append(BASE_DIR) # 加入到系统环境变量中 import setting # 导入setting client = AipSpeech(setting.APP_ID,setting.API_KEY,setting.SECRET_KEY) res = client.synthesis("亲,我还没有小主人,快帮我找一个吧","zh",1,setting.SPEECH) with open(os.path.join(setting.AUDIO_FILE,"Nobind.mp3"),"wb") as f: f.write(res)
执行 baidu_ai.py,会在audio目录生成 Nobind.mp3 文件。试听一下吧
修改 baidu_ai.py,录制联系玩具厂商语音
from aip import AipSpeech import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 项目根目录 import sys sys.path.append(BASE_DIR) # 加入到系统环境变量中 import setting # 导入setting client = AipSpeech(setting.APP_ID,setting.API_KEY,setting.SECRET_KEY) res = client.synthesis("硬件设备不符,请联系玩具厂商","zh",1,setting.SPEECH) with open(os.path.join(setting.AUDIO_FILE,"Nodevice.mp3"),"wb") as f: f.write(res)
执行 baidu_ai.py,会在audio目录生成 Nodevice.mp3 文件。试听一下吧
修改 serv-->toys.py,返回语音文件名
from flask import Blueprint, request, jsonify from setting import MONGO_DB from setting import RET from bson import ObjectId toy = Blueprint("toy", __name__) @toy.route("/toy_list", methods=["POST"]) def toy_list(): # 玩具列表 user_id = request.form.get("user_id") # 用户id # 查看用户信息 user_info = MONGO_DB.users.find_one({"_id": ObjectId(user_id)}) bind_toy = user_info.get("bind_toy") # 获取绑定的玩具 bind_toy_id = [] # 玩具列表 for toy_id in bind_toy: # 获取玩具列表中的所有玩具id bind_toy_id.append(ObjectId(toy_id)) # 一次性查询多个玩具 toys_list = list(MONGO_DB.toys.find({"_id": {"$in": bind_toy_id}})) for index,item in enumerate(toys_list): # 将_id转换为字符串 toys_list[index]["_id"] = str(item.get("_id")) RET["code"] = 0 RET["msg"] = "" RET["data"] = toys_list return jsonify(RET) @toy.route("/device_toy_id", methods=["POST"]) def device_toy_id(): # 验证设备id RET["code"] = 0 RET["msg"] = "开机成功" RET["data"] = {} device_id = request.form.get("device_id") # 获取设备id # 判断设备id是否在设备表中 if MONGO_DB.devices.find_one({"device_id": device_id}): # 查询设备id是否在玩具表中 toy_info = MONGO_DB.toys.find_one({"device_id": device_id}) if toy_info: # RET添加键值,获取玩具id RET["data"]["toy_id"] = str(toy_info.get("_id")) # 音频文件 RET["data"]["audio"] = "success.mp3" return jsonify(RET) else: # 已授权的设备,但是没有绑定主人 RET["msg"] = "找小主人" RET["data"]["audio"] = "Nobind.mp3" return jsonify(RET) else: # 不在设备表中,说明是未授权,或者是冒牌的! RET["msg"] = "联系玩具厂" RET["data"]["audio"] = "Nodevice.mp3" return jsonify(RET)
修改 index.html,POST请求成功后,修改audio标签的文件路径。将ws.onmessage代码移植到下面!
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <audio src="" autoplay="autoplay" controls id="player"></audio> <br> <input type="text" id="device_id"> <button onclick="start_toy()">玩具开机键</button> <br> <button onclick="start_reco()">开始废话</button> <br> <button onclick="stop_reco()">发送语音</button> </body> <script src="/static/recorder.js"></script> <script src="/static/jquery.min.js"></script> <script type="application/javascript"> // 获取音频文件 var get_file = "http://192.168.11.24:9527/get_audio/"; var ws = null; // WebSocket 对象 var reco = null; // 创建AudioContext对象 var audio_context = new AudioContext(); //要获取音频和视频 navigator.getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia); // 拿到媒体对象,允许音频对象 navigator.getUserMedia({audio: true}, create_stream, function (err) { console.log(err) }); //创建媒体流容器 function create_stream(user_media) { var stream_input = audio_context.createMediaStreamSource(user_media); // 给Recoder 创建一个空间,麦克风说的话,都可以录入。是一个流 reco = new Recorder(stream_input); } function start_reco() { //开始录音 reco.record(); //往里面写流 } function stop_reco() { //停止录音 reco.stop(); //停止写入流 get_audio(); //调用自定义方法 reco.clear(); //清空容器 } function get_audio() { // 获取音频 reco.exportWAV(function (wav_file) { ws.send(wav_file); //使用websocket连接发送数据给后端 }) } function start_toy() { // 玩具开机 // 获取输入的设备id var device_id = document.getElementById("device_id").value; // 发送post请求 $.post( // 这里的地址必须是127.0.0.1,否则会有跨域问题 "http://127.0.0.1:9527/device_toy_id", // 发送设备id {device_id: device_id}, function (data) { console.log(data); toy_id = data.data.toy_id; // 玩具id // 修改audio标签的src属性 document.getElementById("player").src = get_file + data.data.audio; if (toy_id) { // 判断玩具id存在时 ws = new WebSocket("ws://192.168.11.24:9528/toy/" + toy_id); ws.onmessage = function (data) { // console.log(get_file + data.data); var content = JSON.parse(data.data); //反序列化数据 document.getElementById("player").src = get_file + content.data; console.log(content.from_user + "给你点了一首歌"); } } }, "json" // 规定预期的服务器响应的数据类型为json ); } </script> </html>
重启 manager.py,访问首页,输入正确的设备id,效果如下:
这个功能,还可以扩展。比如判断今天是否为小主人的生日。说:生日快乐!
或者阳历节日,也可以提醒!
二、为多个玩具发送点播
用户有一个玩具,或者多个玩具时。
如果点击这个按钮,需要用户选择,指定发送给哪一个玩具。
目前数据库中,只有一个用户。昨天已经添加了一个,具体操作,请从参考昨天的链接:
https://www.cnblogs.com/xiao987334176/p/9670063.html
现在再来添加一个!
绑定成功后,查看玩具表。有2条记录了
查看用户表,查看好友字段,会有2个!
建立连接
开2个页面,表示2个玩具。让2个玩具开机,需要2个合格的设备id。
打开玩具表,复制2个设备id
打开2个网页,左边输入 嘻嘻 的设备id
右边输入 小可爱 的设备id
在Console中,输入ws,回车。会出现一个websocket链接
注意:只要玩具开机了,就会建立 websocket连接!
查看Pycharm控制台输出:此时应该有2个websocket连接:
{'5ba21c84e1253229c4acbd12': <geventwebsocket.websocket.WebSocket object at 0x000002DB8812BE18>, '5ba0f1f2e12532418089bf88': <geventwebsocket.websocket.WebSocket object at 0x000002DB88172590>}
那么APP页面,如何选择多个玩具呢?需要用到 弹出菜单
弹出菜单
mui框架内置了弹出菜单插件,弹出菜单显示内容不限,但必须包裹在一个含.mui-popover
类的div中,如下即为一个弹出菜单内容:
<div id="popover" class="mui-popover"> <ul class="mui-table-view"> <li class="mui-table-view-cell"><a href="#">Item1</a></li> <li class="mui-table-view-cell"><a href="#">Item2</a></li> <li class="mui-table-view-cell"><a href="#">Item3</a></li> <li class="mui-table-view-cell"><a href="#">Item4</a></li> <li class="mui-table-view-cell"><a href="#">Item5</a></li> </ul> </div>
要显示、隐藏如上菜单,mui推荐使用锚点方式,例如:
<a href="#popover" id="openPopover" class="mui-btn mui-btn-primary mui-btn-block">打开弹出菜单</a>
点击如上定义的按钮,即可显示弹出菜单,再次点击弹出菜单之外的其他区域,均可关闭弹出菜单;这种使用方式最为简洁。
若希望通过js的方式控制弹出菜单,则通过如下一个方法即可:
mui('.bottomPopover').popover(status[,anchor]);
status
-
'show'显示popover
-
'hide'隐藏popover
-
'toggle'自动识别处理显示隐藏状态
mui('.bottomPopover').popover('toggle');//show hide toggle
本文参考链接:
http://dev.dcloud.net.cn/mui/ui/#ui_popover
修改 player.html,只修改html代码部分,js代码不用动!
<!doctype html> <html> <head> <meta charset="UTF-8"> <title></title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <link href="css/mui.min.css" rel="stylesheet" /> </head> <body> <header class="mui-bar mui-bar-nav"> <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <h1 class="mui-title" id="title_text">正在播放</h1> </header> <div class="mui-content"> <div class="mui-row" style="text-align: center;margin-top: 5px;"> <img src="avatar/girl.jpg" style="width: 250px;height: 250px; border-radius: 50%;" id="avatar" /> </div> <button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="play">播放</button> <button type="button" class="mui-btn mui-btn-yellow mui-btn-block" id="pause">暂停</button> <button type="button" class="mui-btn mui-btn-green mui-btn-block" id="resume">继续</button> <button type="button" class="mui-btn mui-btn-red mui-btn-block" id="stop">停止</button> <style type="text/css"> #popover { height: 150px; width: 300px; } </style> <div id="popover" class="mui-popover"> <div class="mui-scroll-wrapper"> <div class="mui-scroll"> <ul class="mui-table-view" id="toy_list" style="text-align: center;"> <li class="mui-table-view-cell"><a href="#">Item1</a></li> <li class="mui-table-view-cell"><a href="#">Item2</a></li> </ul> </div> </div> </div> <!--<button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="send2toy">发送给玩具</button>--> <a href="#popover" id="openPopover" class="mui-btn mui-btn-primary mui-btn-block">发送给玩具</a> </div> </body> <script src="js/mui.js"></script> <script type="text/javascript"> mui.init(); var Sdata = null; //当前web页面 var music_name = null; //歌曲名 var player = null; //播放对象 mui.plusReady(function() { Sdata = plus.webview.currentWebview(); // 当前web页面 mui.toast(Sdata.content_id); // 弹窗显示由main.html传递的content_id //发送post请求 mui.post( window.serv + "/content_one", { // 参数为content_id content_id: Sdata.content_id }, function(data) { // 打印响应数据 console.log(JSON.stringify(data)); // 修改标题 document.getElementById("title_text").innerText = "正在播放 : " + data.data.title; // 修改图片地址 document.getElementById("avatar").src = window.serv_imge + data.data.avatar; // 调用自定义方法,播放音频 // data是后端返回的数据,data.audio是音频文件名 music_name = data.data.audio; // 歌曲名 play_anything(music_name); //播放歌曲 } ); function play_anything(content) { //播放音频 // 创建播放对象,拼接路径 player = plus.audio.createPlayer(window.serv_audio + content); console.log(window.serv_audio + content); //打印路径 // http://192.168.11.86:9527/get_audio/a6d680fe-fa80-4a54-82b8-b203f5a9c7b4.mp3 player.play(); // 播放音频 } document.getElementById("play").addEventListener("tap", function() { player.play(); }); document.getElementById("pause").addEventListener("tap", function() { player.pause(); //暂停 }); document.getElementById("resume").addEventListener("tap", function() { player.resume(); //继续 }); document.getElementById("stop").addEventListener("tap", function() { player.stop(); // 停止,直接清空player中的对象 }); //发送给玩具 document.getElementById("send2toy").addEventListener("tap", function() { var index = plus.webview.getWebviewById("HBuilder"); //index.html页面 mui.fire(index, "send_music", { //发送音乐 music_name: music_name, //歌曲名 toy_id:"123456" // 发给玩具id为12345 }) }); }) </script> </html>
使用模拟器访问,点击 发送给玩具,效果如下:
上面的item固定死了,需要展示为当前用户的 玩具名。需要访问后端接口,查询当前用户的所有玩具
修改 player.html
<!doctype html> <html> <head> <meta charset="UTF-8"> <title></title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <link href="css/mui.min.css" rel="stylesheet" /> </head> <body> <header class="mui-bar mui-bar-nav"> <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <h1 class="mui-title" id="title_text">正在播放</h1> </header> <div class="mui-content"> <div class="mui-row" style="text-align: center;margin-top: 5px;"> <img src="avatar/girl.jpg" style="width: 250px;height: 250px; border-radius: 50%;" id="avatar" /> </div> <button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="play">播放</button> <button type="button" class="mui-btn mui-btn-yellow mui-btn-block" id="pause">暂停</button> <button type="button" class="mui-btn mui-btn-green mui-btn-block" id="resume">继续</button> <button type="button" class="mui-btn mui-btn-red mui-btn-block" id="stop">停止</button> <style type="text/css"> #popover { height: 150px; width: 300px; } </style> <div id="popover" class="mui-popover"> <div class="mui-scroll-wrapper"> <div class="mui-scroll"> <ul class="mui-table-view" id="toy_list" style="text-align: center;"> </ul> </div> </div> </div> <!--<button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="send2toy">发送给玩具</button>--> <a href="#popover" id="openPopover" class="mui-btn mui-btn-primary mui-btn-block">发送给玩具</a> </div> </body> <script src="js/mui.js"></script> <script type="text/javascript"> mui.init(); var Sdata = null; //当前web页面 var music_name = null; //歌曲名 var player = null; //播放对象 mui.plusReady(function() { Sdata = plus.webview.currentWebview(); // 当前web页面 mui.toast(Sdata.content_id); // 弹窗显示由main.html传递的content_id //发送post请求 mui.post( window.serv + "/content_one", { // 参数为content_id content_id: Sdata.content_id }, function(data) { // 打印响应数据 console.log(JSON.stringify(data)); // 修改标题 document.getElementById("title_text").innerText = "正在播放 : " + data.data.title; // 修改图片地址 document.getElementById("avatar").src = window.serv_imge + data.data.avatar; // 调用自定义方法,播放音频 // data是后端返回的数据,data.audio是音频文件名 music_name = data.data.audio; // 歌曲名 play_anything(music_name); //播放歌曲 } ); mui.post( //查询当前用户的玩具列表 window.serv + "/toy_list", { user_id: plus.storage.getItem("user") }, function(data) { console.log(JSON.stringify(data)); for(var i = 0; i < data.data.length; i++) { // 执行定义方法create_toy,增加li标签 create_toy(data.data[i]); } } ); function play_anything(content) { //播放音频 // 创建播放对象,拼接路径 player = plus.audio.createPlayer(window.serv_audio + content); console.log(window.serv_audio + content); //打印路径 // http://192.168.11.86:9527/get_audio/a6d680fe-fa80-4a54-82b8-b203f5a9c7b4.mp3 player.play(); // 播放音频 } document.getElementById("play").addEventListener("tap", function() { player.play(); }); document.getElementById("pause").addEventListener("tap", function() { player.pause(); //暂停 }); document.getElementById("resume").addEventListener("tap", function() { player.resume(); //继续 }); document.getElementById("stop").addEventListener("tap", function() { player.stop(); // 停止,直接清空player中的对象 }); //发送给玩具 document.getElementById("send2toy").addEventListener("tap", function() { var index = plus.webview.getWebviewById("HBuilder"); //index.html页面 mui.fire(index, "send_music", { //发送音乐 music_name: music_name, //歌曲名 toy_id: "123456" // 发给玩具id为12345 }) }); function create_toy(toy_info) { // 创建玩具 // 构造下面的标签 // <li class="mui-table-view-cell"> // <a href="#">Item1</a> // </li> var litag = document.createElement("li"); litag.className = "mui-table-view-cell" var atag = document.createElement("a"); atag.id = toy_info._id; atag.innerText = toy_info.baby_name; litag.appendChild(atag); document.getElementById("toy_list").appendChild(litag); } }) </script> </html>
重新访问一次,效果如下:
点击 小豆芽 是没有效果的!需要增加点击事件。由于它是a标签,使用onclick
需要使用websocket发送数据。由于index.html建立了websocket连接,使用fire事件将数据发给index.html。
由index.html来发送数据!
修改player.html
<!doctype html> <html> <head> <meta charset="UTF-8"> <title></title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <link href="css/mui.min.css" rel="stylesheet" /> </head> <body> <header class="mui-bar mui-bar-nav"> <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <h1 class="mui-title" id="title_text">正在播放</h1> </header> <div class="mui-content"> <div class="mui-row" style="text-align: center;margin-top: 5px;"> <img src="avatar/girl.jpg" style="width: 250px;height: 250px; border-radius: 50%;" id="avatar" /> </div> <button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="play">播放</button> <button type="button" class="mui-btn mui-btn-yellow mui-btn-block" id="pause">暂停</button> <button type="button" class="mui-btn mui-btn-green mui-btn-block" id="resume">继续</button> <button type="button" class="mui-btn mui-btn-red mui-btn-block" id="stop">停止</button> <!--弹窗样式--> <style type="text/css"> #popover { height: 150px; width: 300px; } </style> <div id="popover" class="mui-popover"> <div class="mui-scroll-wrapper"> <div class="mui-scroll"> <ul class="mui-table-view" id="toy_list" style="text-align: center;"> </ul> </div> </div> </div> <!--<button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="send2toy">发送给玩具</button>--> <a href="#popover" id="openPopover" class="mui-btn mui-btn-primary mui-btn-block">发送给玩具</a> </div> </body> <script src="js/mui.js"></script> <script type="text/javascript"> mui.init(); var Sdata = null; //当前web页面 var music_name = null; //歌曲名 var player = null; //播放对象 mui.plusReady(function() { Sdata = plus.webview.currentWebview(); // 当前web页面 mui.toast(Sdata.content_id); // 弹窗显示由main.html传递的content_id //发送post请求 mui.post( window.serv + "/content_one", { // 参数为content_id content_id: Sdata.content_id }, function(data) { // 打印响应数据 console.log(JSON.stringify(data)); // 修改标题 document.getElementById("title_text").innerText = "正在播放 : " + data.data.title; // 修改图片地址 document.getElementById("avatar").src = window.serv_imge + data.data.avatar; // 调用自定义方法,播放音频 // data是后端返回的数据,data.audio是音频文件名 music_name = data.data.audio; // 歌曲名 play_anything(music_name); //播放歌曲 } ); mui.post( //查询当前用户的玩具列表 window.serv + "/toy_list", { user_id: plus.storage.getItem("user") }, function(data) { console.log(JSON.stringify(data)); for(var i = 0; i < data.data.length; i++) { // 执行定义方法create_toy,增加li标签 create_toy(data.data[i]); } } ); function play_anything(content) { //播放音频 // 创建播放对象,拼接路径 player = plus.audio.createPlayer(window.serv_audio + content); console.log(window.serv_audio + content); //打印路径 // http://192.168.11.86:9527/get_audio/a6d680fe-fa80-4a54-82b8-b203f5a9c7b4.mp3 player.play(); // 播放音频 } document.getElementById("play").addEventListener("tap", function() { player.play(); }); document.getElementById("pause").addEventListener("tap", function() { player.pause(); //暂停 }); document.getElementById("resume").addEventListener("tap", function() { player.resume(); //继续 }); document.getElementById("stop").addEventListener("tap", function() { player.stop(); // 停止,直接清空player中的对象 }); //发送给玩具 document.getElementById("send2toy").addEventListener("tap", function() { var index = plus.webview.getWebviewById("HBuilder"); //index.html页面 mui.fire(index, "send_music", { //发送音乐 music_name: music_name, //歌曲名 toy_id: "123456" // 发给玩具id为12345 }) }); function create_toy(toy_info) { // 创建玩具 // 构造下面的标签 // <li class="mui-table-view-cell"> // <a href="#">Item1</a> // </li> var litag = document.createElement("li"); litag.className = "mui-table-view-cell" var atag = document.createElement("a"); atag.id = toy_info._id; // 玩具id atag.innerText = toy_info.baby_name; atag.onclick = function() { // 点击事件 // 获取index页面 var index = plus.webview.getWebviewById("HBuilder") mui.fire(index, "send_music", { // 使用fire发送数据给index music_name: music_name, //歌曲名 //玩具id,注意:这里必须是this.id。它表示你点击的是哪个玩具 toy_id: this.id }) } litag.appendChild(atag); document.getElementById("toy_list").appendChild(litag); } }) </script> </html>
index.html页面,就不需要修改了。因为它已经监听了 send_music 事件。
点击 小甜甜
此时,页面的第二个窗口,会自动播放歌曲。
那么点歌功能,就完成了!
三、聊天页面
之前我们写的phone.html,好像没咋用过。将phone.html重命名为 message.html
好友列表,来源于 用户表(users) 的friend_list字段!
修改index.html,将底部选项卡 中的 电话 改为 消息
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <title></title> <script src="js/mui.js"></script> <link href="css/mui.min.css" rel="stylesheet" /> </head> <body> <!--底部选项卡--> <nav class="mui-bar mui-bar-tab"> <a class="mui-tab-item mui-active" id="index"> <span class="mui-icon mui-icon-home"></span> <span class="mui-tab-label">首页</span> </a> <a class="mui-tab-item" id="message"> <span class="mui-icon mui-icon-chatbubble"></span> <span class="mui-tab-label">消息</span> </a> <a class="mui-tab-item"> <span class="mui-icon mui-icon-email"></span> <span class="mui-tab-label">邮件</span> </a> <a class="mui-tab-item" id="login"> <span class="mui-icon mui-icon-gear"></span> <span class="mui-tab-label">设置</span> </a> </nav> </body> <script type="text/javascript" charset="utf-8"> var ws = null; // websocket对象 mui.init({ subpages: [{ url: "main.html", id: "main.html", styles: window.styles }] }); mui.plusReady(function() { // console.log(JSON.stringify(plus.webview.currentWebview())) if(plus.storage.getItem("user")) { // 判断是否登录 console.log('已结登录了!'); //连接websocket连接 ws = new WebSocket("ws://"+window.ws_serv+"/app/"+plus.storage.getItem("user")) // 客户端接收服务端数据时触发 ws.onmessage = function() {}; } }); // 消息 document.getElementById("message").addEventListener("tap", function() { mui.openWindow({ url: "message.html", id: "message.html", styles: window.styles, extras: { // 传输用户id,给message.html user_id: plus.storage.getItem("user") } }) }); document.getElementById("index").addEventListener("tap", function() { mui.openWindow({ url: "main.html", id: "main.html", styles: window.styles }) }) document.getElementById("login").addEventListener("tap", function() { mui.openWindow({ url: "login.html", id: "login.html", styles: window.styles }) }) document.addEventListener("login",function(data){ // fire事件接收消息,使用data.detail // index是为做显示区分 mui.toast("index"+data.detail.msg) }); document.addEventListener("send_music", function(data) { //监听send_music事件 var music_name = data.detail.music_name; //获取player.html使用fire发送的music_name值 var toy_id = data.detail.toy_id; //获取发送的玩具id send_str = { //构造数据 music_name:music_name, toy_id:toy_id } // 发送数据给后端,注意要json序列化 ws.send(JSON.stringify(send_str)); }); </script> </html>
底部选项卡,效果如下:
修改 message.html,发送post,请求好友列表
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <link rel="stylesheet" type="text/css" href="css/mui.css" /> </head> <body> <header class="mui-bar mui-bar-nav"> <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <h1 class="mui-title">我的好友</h1> </header> <div class="mui-content"> <ul class="mui-table-view" id="friend_list"> </ul> </div> </body> <script src="js/mui.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> mui.init() var Sdata = null; mui.back = function(){}; // 加载HTML5Puls mui.plusReady(function() { Sdata = plus.webview.currentWebview(); // post请求 mui.post( // 好友列表 window.serv + "/friend_list", {user_id:Sdata.user_id}, function(data){ console.log(JSON.stringify(data)); } ) }); </script> </html>
进入 flask项目,进入serv目录,新建文件friend.py
from flask import Blueprint, request, jsonify from setting import MONGO_DB from setting import RET from bson import ObjectId fri = Blueprint("fri", __name__) @fri.route("/friend_list", methods=["POST"]) def friend_list(): # 好友列表 user_id = request.form.get("user_id") # 查询用户id信息 res = MONGO_DB.users.find_one({"_id": ObjectId(user_id)}) friend_list = res.get("friend_list") # 获取好友列表 RET["code"] = 0 RET["msg"] = "" RET["data"] = friend_list return jsonify(RET)
修改 manager.py,注册蓝图
from flask import Flask, request,jsonify,render_template from setting import MONGO_DB from setting import RET from bson import ObjectId from serv import get_file from serv import content from serv import devices from serv import toys from serv import friend app = Flask(__name__) app.register_blueprint(get_file.getfile) app.register_blueprint(content.cont) app.register_blueprint(devices.devs) app.register_blueprint(toys.toy) app.register_blueprint(friend.fri) @app.route('/') def hello_world(): return render_template("index.html") @app.route('/login',methods=["POST"]) def login(): """ 登陆验证 :return: settings -> RET """ try: RET["code"] = 1 RET["msg"] = "用户名或密码错误" RET["data"] = {} username = request.form.get("username") password = request.form.get("password") user = MONGO_DB.users.find_one({"username": username, "password": password}) if user: # 由于user中的_id是ObjectId对象,需要转化为字符串 user["_id"] = str(user.get("_id")) RET["code"] = 0 RET["msg"] = "欢迎登陆" RET["data"] = {"user_id": user.get("_id")} except Exception as e: RET["code"] = 1 RET["msg"] = "登陆失败" return jsonify(RET) @app.route('/reg',methods=["POST"]) def reg(): """ 注册 :return: {"code":0,"msg":"","data":""} """ try: username = request.form.get("username") password = request.form.get("password") age = request.form.get("age") nickname = request.form.get("nickname") gender = request.form.get("gender") phone = request.form.get("phone") user_info = { "username": username, "password": password, "age": age, "nickname": nickname, # 判断gender==2,成立时为girl.jpg,否则为boy.jpg "avatar": "girl.jpg" if gender == 2 else "boy.jpg", "gender": gender, "phone": phone } res = MONGO_DB.users.insert_one(user_info) user_id = str(res.inserted_id) RET["code"] = 0 RET["msg"] = "注册成功" RET["data"] = user_id except Exception as e: RET["code"] = 1 RET["msg"] = "注册失败" return jsonify(RET) @app.route('/user_info', methods=["POST"]) def user_info(): user_id = request.form.get("user_id") # "password": 0 表示忽略密码字段 res = MONGO_DB.users.find_one({"_id": ObjectId(user_id)}, {"password": 0}) if res: res["_id"] = str(res.get("_id")) RET["code"] = 0 RET["msg"] = "" RET["data"] = res return jsonify(res) if __name__ == '__main__': app.run("0.0.0.0", 9527, debug=True)
重启 manager.py
使用模拟器访问 消息 ,效果如下:
这个页面还是空的。查看HBuilder控制台输出:
{"code":0,"data":[{"friend_avatar":"girl.jpg","friend_chat":"5ba0f1f2e12532418089bf87","friend_id":"5ba0f1f2e12532418089bf88","friend_name":"小可爱","friend_remark":"小甜甜"},{"friend_avatar":"girl.jpg","friend_chat":"5ba21c84e1253229c4acbd11","friend_id":"5ba21c84e1253229c4acbd12","friend_name":"嘻嘻","friend_remark":"小豆芽"}],"msg":""} at message.html:37
已经得到了数据,下面开始渲染页面!
修改 message.html,渲染页面
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <link rel="stylesheet" type="text/css" href="css/mui.css" /> </head> <body> <header class="mui-bar mui-bar-nav"> <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <h1 class="mui-title">我的好友</h1> </header> <div class="mui-content"> <ul class="mui-table-view" id="friend_list"> </ul> </div> </body> <script src="js/mui.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> mui.init() var Sdata = null; mui.back = function(){}; // 加载HTML5Puls mui.plusReady(function() { Sdata = plus.webview.currentWebview(); // post请求 mui.post( // 好友列表 window.serv + "/friend_list", {user_id:Sdata.user_id}, function(data){ console.log(JSON.stringify(data)); // 循环好友列表 for (var i = 0; i < data.data.length; i++) { // 执行自定义方法,渲染页面 create_content(data.data[i]); } } ) }); function create_content(content){ // <li class="mui-table-view-cell mui-media"> // <a href="javascript:;"> // <img class="mui-media-object mui-pull-left" src="../images/shuijiao.jpg"> // <div class="mui-media-body"> // 幸福 // <p class='mui-ellipsis'>能和心爱的人一起睡觉,是件幸福的事情;可是,打呼噜怎么办?</p> // </div> // </a> // </li> var litag = document.createElement("li"); litag.className = "mui-table-view-cell mui-media"; var atag = document.createElement("a"); atag.id = content.friend_id; // 点击事件 atag.onclick = function(){ console.log(this.id); // open_chat(this.id); } var imgtag = document.createElement("img"); imgtag.className = "mui-media-object mui-pull-left"; imgtag.src = "avatar/" + content.friend_avatar; var divtag = document.createElement("div"); divtag.className = "mui-media-body"; divtag.innerText = content.friend_remark; var ptag = document.createElement("p"); ptag.className = "mui-ellipsis"; ptag.innerText = content.friend_name; litag.appendChild(atag); atag.appendChild(imgtag); atag.appendChild(divtag); divtag.appendChild(ptag); document.getElementById("friend_list").appendChild(litag); } </script> </html>
重新访问,效果如下:
点击小甜甜,查看HBuilder控制台输出:
5ba0f1f2e12532418089bf88 at message.html:63
它会打印出,好友id。那么下面就可以开始聊天了!
新建css文件
chat.css
div.speech { float: left; margin: 0, 0; padding: 6px; table-layout: fixed; word-break: break-all; position: relative; background: -webkit-gradient( linear, 50% 0%, 50% 100%, from(#ffffff), color-stop(0.1, #ececec), color-stop(0.5, #dbdbdb), color-stop(0.9, #dcdcdc), to(#8c8c8c)); border: 1px solid #989898; border-radius: 8px; } div.speech:before { content: ''; position: absolute; width: 0; height: 0; left: 15px; top: -20px; border: 10px solid; border-color: transparent transparent #989898 transparent; } div.speech:after { content: ''; position: absolute; width: 0; height: 0; left: 17px; top: -16px; border: 8px solid; border-color: transparent transparent #ffffff transparent; } div.speech.right { display: inline-block; box-shadow: -2px 2px 5px #CCC; margin-right: 10px; max-width: 75%; float: right; background: -webkit-gradient( linear, 50% 0%, 50% 100%, from(#e4ffa7), color-stop(0.1, #bced50), color-stop(0.4, #aed943), color-stop(0.8, #a7d143), to(#99BF40)); } div.speech.right:before { content: ''; position: absolute; width: 0; height: 0; top: 9px; bottom: auto; left: auto; right: -10px; border-width: 9px 0 9px 10px; border-color: transparent #989898; } div.speech.right:after { content: ''; position: absolute; width: 0; height: 0; top: 10px; bottom: auto; left: auto; right: -8px; border-width: 8px 0 8px 9px; border-color: transparent #bced50; } div.left { display: inline-block; box-shadow: 2px 2px 2px #CCCCCC; margin-left: 10px; max-width: 75%; position: relative; background: -webkit-gradient( linear, 50% 0%, 50% 100%, from(#ffffff), color-stop(0.1, #eae8e8), color-stop(0.4, #E3E3E3), color-stop(0.8, #DFDFDF), to(#D9D9D9)); } div.left:before { content: ''; position: absolute; width: 0; height: 0; top: 9px; bottom: auto; left: -10px; border-width: 9px 10px 9px 0; border-color: transparent #989898; } div.left:after { content: ''; position: absolute; width: 0; height: 0; top: 10px; bottom: auto; left: -8px; border-width: 8px 9px 8px 0; border-color: transparent #eae8e8; } .leftimg { float: left; margin-top: 10px; } .rightimg { float: right; margin-top: 10px; } .leftd { clear: both; float: left; margin-left: 10px; margin-top: 15px; } .rightd { clear: both; float: right; margin-top: 15px; margin-right: 10px; } .leftd_h { width: 45px; height: 35px; border-radius: 100%; display: block; float: left; overflow: hidden; } .leftd_h img { display: block; width: 100%; height: auto; } .rightd_h { width: 45px; height: 35px; border-radius: 100%; display: block; float: right; overflow: hidden; } .rightd_h img { display: block; width: 100%; height: auto; } .chat-other { background-color: red; margin-top: 10px; margin-left: 20px; } .chat-other-span { background-color: aquamarine; /*border-radius: 10%;*/ height: 18px; } .chat-mine { margin-top: 10px; margin-right: 20px; text-align: right; } .chat-avatar { border-radius: 100%; width: 25px; height: 25px; }
新建文件chat.html
手势事件
在开发移动端的应用时,会用到很多的手势操作,比如滑动、长按等,为了方便开放者快速集成这些手势,mui内置了常用的手势事件,目前支持的手势事件见如下列表:
参考链接:
http://dev.dcloud.net.cn/mui/event/#gesture
这里, 只用到了 长按里面的 hold和release
修改 chat.html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <link rel="stylesheet" type="text/css" href="css/mui.css" /> <link rel="stylesheet" type="text/css" href="css/chat.css" /> </head> <body> <header class="mui-bar mui-bar-nav"> <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <h1 class="mui-title">与xxx进行对话</h1> </header> <div class="mui-content" id="chat_list"> <div class="leftd"> <img src="avatar/girl.jpg" class="leftd_h" /> <div class="speech left">点击播放</div> </div> <div class="rightd"> <img src="avatar/girl.jpg" class="rightd_h" /> <div class="speech right">点击播放</div> </div> </div> <nav class="mui-bar mui-bar-tab"> <a class="mui-tab-item mui-active" id="talk"> <span class="mui-icon mui-icon-speech"></span> <span class="mui-tab-label">按住说话</span> </a> </nav> </body> <script src="js/mui.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> mui.init({ //手势事件配置 gestureConfig: { tap: true, //默认为true doubletap: true, //默认为false longtap: false, //默认为false swipe: true, //默认为true drag: true, //默认为true hold: true, //默认为false,不监听 release: true //默认为false,不监听 } }); var index = null; var Sdata=null; mui.plusReady(function() { index = plus.webview.getWebviewById("HBuilder"); Sdata = plus.webview.currentWebview(); }) var rec = null; document.getElementById("talk").addEventListener("hold", function() { mui.toast("按住了"); }) document.getElementById("talk").addEventListener("release", function() { mui.toast("松开了"); }) function create_chat(who, p) { // 构建div,一次说话,就是一个div // <div class="leftd"> // <img src="avatar/girl.jpg" class="leftd_h" /> // <div class="speech left">点击播放</div> // </div> // 默认显示在左边 var div1class = "leftd"; var imgclass = "leftd_h"; var div2class = "speech left"; // 左右列表排序效果只是class不一样而已! // 这里做一个判断,当为self,class改为right if(who == "self") { div1class = "rightd"; imgclass = "rightd_h"; div2class = "speech right"; } var div1tag = document.createElement("div"); div1tag.className = div1class; var imgtag = document.createElement("img"); imgtag.className = imgclass; imgtag.src = "avatar/girl.jpg" var div2tag = document.createElement("div"); div2tag.className = div2class; div2tag.innerText = "点击播放"; div1tag.appendChild(imgtag); div1tag.appendChild(div2tag); document.getElementById("chat_list").appendChild(div1tag); } // 生成几个div。一个div就是一次说话 create_chat("self"); // self表示我 create_chat("w"); // 这个可以随便写,表示其他 create_chat("self"); create_chat("2"); create_chat("2"); </script> </html>
修改 message.html,增加点击事件。点击时,跳转到chat.html页面
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <link rel="stylesheet" type="text/css" href="css/mui.css" /> </head> <body> <header class="mui-bar mui-bar-nav"> <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <h1 class="mui-title">我的好友</h1> </header> <div class="mui-content"> <ul class="mui-table-view" id="friend_list"> </ul> </div> </body> <script src="js/mui.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> mui.init() var Sdata = null; mui.back = function(){}; // 加载HTML5Puls mui.plusReady(function() { Sdata = plus.webview.currentWebview(); // post请求 mui.post( // 好友列表 window.serv + "/friend_list", {user_id:Sdata.user_id}, function(data){ console.log(JSON.stringify(data)); // 循环好友列表 for (var i = 0; i < data.data.length; i++) { // 执行自定义方法,渲染页面 create_content(data.data[i]); } } ) }); function create_content(content){ // <li class="mui-table-view-cell mui-media"> // <a href="javascript:;"> // <img class="mui-media-object mui-pull-left" src="../images/shuijiao.jpg"> // <div class="mui-media-body"> // 幸福 // <p class='mui-ellipsis'>能和心爱的人一起睡觉,是件幸福的事情;可是,打呼噜怎么办?</p> // </div> // </a> // </li> var litag = document.createElement("li"); litag.className = "mui-table-view-cell mui-media"; var atag = document.createElement("a"); atag.id = content.friend_id; // 点击事件 atag.onclick = function(){ console.log(this.id); open_chat(this.id); //执行自定义方法open_chat } var imgtag = document.createElement("img"); imgtag.className = "mui-media-object mui-pull-left"; imgtag.src = "avatar/" + content.friend_avatar; var divtag = document.createElement("div"); divtag.className = "mui-media-body"; divtag.innerText = content.friend_remark; var ptag = document.createElement("p"); ptag.className = "mui-ellipsis"; ptag.innerText = content.friend_name; litag.appendChild(atag); atag.appendChild(imgtag); atag.appendChild(divtag); divtag.appendChild(ptag); document.getElementById("friend_list").appendChild(litag); } function open_chat(friend_id){ // 打开chat.html mui.openWindow({ url:"chat.html", id:"chat.html", extras:{ // 传参给chat.html friend_id:friend_id } }) } </script> </html>
使用模拟器访问,效果如下:
四、app录音
由于时间关系,详细步骤略...
五、app与服务器端文件传输
由于时间关系,详细步骤略...
六、简单的对话
由于时间关系,详细步骤略...
今日总结:
1.玩具开机提示语 刚刚开机的时候: 1.授权问题(MD5授权码)提示语 : 请联系玩具厂商 2.绑定问题 提示语 : 快给我找一个小主人 3.成功 提示语:欢迎使用 2.为多个玩具发送点播: mpop 弹出菜单 3.聊天界面: <div class="leftd"> <img src="avatar/girl.jpg" class="leftd_h" /> <div class="speech left">点击播放</div> </div> <div class="rightd"> <img src="avatar/girl.jpg" class="rightd_h" /> <div class="speech right">点击播放</div> </div> 按住录音: hold: 按住事件 开始录音(回调函数) release: 松开事件 结束录音 执行录音中的回调函数 4.app录音: var rec = plus.audio.getRcorder() rec.record( {filename:"_doc/audio/",format:"amr"}, function(success){ success //录音文件保存路径 }, function(error){} ) rec.stop() 5.app与服务器端文件传输(ws传输): 1.app使用dataURL方式打开录音文件 : base64 文件 2.通过某个函数 将 Base64 格式的文件 转为 Blob 用于 websocket传输 3.将Blob对象使用Ws发送至服务端 4.服务端保存文件(amr) 5.将amr 转换为 mp3 使用 ffmpeg -i xxx.amr xxx.mp3 6.简单的对话(app向玩具(web)发起): app: 1.发起两次 ws.send({to_user:}) 告诉服务端我要发给谁消息 2. ws.send(blob) app与服务器端文件传输 websocket服务: 0.创建两个变量,用于接收to_user 和 blob对象 1.收到用户的JSON字符串,to_user 获取对方的Websocket,用户send 2.收到用户的Blob对象,语音文件 保存成amr文件,转换成mp3 注意保存文件的路径 3.将转换完成的文件发送给 to_user 4.两个变量置空
由于时间关系,详细步骤略...,主要修改了3个文件。
MyApp: chat.html,index.html
banana: im_serv.py
最终效果,使用APP给 小甜甜 说一段话:
第二个网页,也就是小甜甜的,会自动播放声音
完整代码,请参考github:
https://github.com/987334176/Intelligent_toy/archive/v1.3.zip
未完待续。。。