python 全栈开发,Day127(app端内容播放,web端的玩具,app通过websocket远程遥控玩具播放内容,玩具管理页面)
昨日内容回顾
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1. 小爬爬 内容采集 XMLY 的 儿童频道 requests 2. 登陆 注册 自动登陆 退出 mui.post("请求地址",{数据},function(){}) plus.storage.setItem(key,value) plus.storage.getItem(key) plus.storage.removeItem(key) 3. app 首页内容部分 document.createElement("div")
一、app端内容播放
下载代码
https://github.com/987334176/Intelligent_toy/archive/v1.0.zip
注意:由于涉及到版权问题,此附件没有图片和音乐。请参考昨天的代码,手动采集一下!
请参考链接:
https://www.cnblogs.com/xiao987334176/p/9647993.html#autoid-3-4-0
播放页面
点击首页的图文列表,需要打开播放页面!
新建一个player.html
内容如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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"> </div> </div> </body> <script src="js/mui.min.js"></script> <script type="text/javascript"> mui.init() </script> </html>
新建一个目录avatar,用来存放头像。从网上下载2个头像,放到此目录。
MyApp目录结构如下:
./
├── avatar
│ ├── boy.jpg
│ └── girl.jpg
├── css
│ ├── mui.css
│ └── mui.min.css
├── fonts
│ └── mui.ttf
├── index.html
├── js
│ ├── md5.min.js
│ ├── mui.js
│ └── mui.min.js
├── login.html
├── main.html
├── manifest.json
├── phone.html
├── player.html
├── reg.html
├── unpackage
└── user_info.html
修改player.html,引入图片,将图片设置为圆的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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> </div> </body> <script src="js/mui.min.js"></script> <script type="text/javascript"> mui.init() </script> </html>
使用HBuilder访问页面,效果如下:
这个图片应该要设置转圈的,有兴趣的人,可以用cs3语法弄一下!
获取列表id
这个图片,应该是音频的图片。昨天我们已经把音频相关的图片下载下来了!
那么首页点击的时候,应该要把当前内容的id传递给player.html
修改main.html,增加点击事件,使用onclick,打印id
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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"> <h1 class="mui-title">首页</h1> </header> <div class="mui-scroll-wrapper"> <div class="mui-scroll"> <div class="mui-content"> <div id="slider" class="mui-slider"> <div class="mui-slider-group mui-slider-loop"> <!-- 额外增加的一个节点(循环轮播:第一个节点是最后一张轮播) --> <div class="mui-slider-item mui-slider-item-duplicate"> <a href="#"> <img src="http://placehold.it/400x300"> </a> </div> <!-- 第一张 --> <div class="mui-slider-item"> <a href="#"> <img src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1537023056923&di=e50b27f1a9d34e586e421b50ff5cc0b0&imgtype=0&src=http%3A%2F%2Fatt.bbs.duowan.com%2Fforum%2F201508%2F18%2F173910p7045xys71x4zfyh.jpg" width="300px" height="400px"> </a> </div> <!-- 第二张 --> <div class="mui-slider-item"> <a href="#"> <img src="http://placehold.it/400x300"> </a> </div> <!-- 第三张 --> <div class="mui-slider-item"> <a href="#"> <img src="http://placehold.it/400x300"> </a> </div> <!-- 第四张 --> <div class="mui-slider-item"> <a href="#"> <img src="http://placehold.it/400x300"> </a> </div> <!-- 额外增加的一个节点(循环轮播:最后一个节点是第一张轮播) --> <div class="mui-slider-item mui-slider-item-duplicate"> <a href="#"> <img src="http://placehold.it/400x300"> </a> </div> </div> <div class="mui-slider-indicator"> <div class="mui-indicator mui-active"></div> <div class="mui-indicator"></div> <div class="mui-indicator"></div> <div class="mui-indicator"></div> </div> </div> <ul class="mui-table-view mui-grid-view mui-grid-9"> <li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3"> <a href="#"> <span class="mui-icon mui-icon-home"></span> <div class="mui-media-body">Home</div> </a> </li> <li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3"> <a href="#"> <span class="mui-icon mui-icon-email"><span class="mui-badge mui-badge-red">5</span></span> <div class="mui-media-body">Email</div> </a> </li> <li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3"> <a href="#"> <span class="mui-icon mui-icon-chatbubble"></span> <div class="mui-media-body">Chat</div> </a> </li> <li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3"> <a href="#"> <span class="mui-icon mui-icon-location"></span> <div class="mui-media-body">Location</div> </a> </li> <li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3"> <a href="#"> <span class="mui-icon mui-icon-search"></span> <div class="mui-media-body">Search</div> </a> </li> <li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3"> <a href="#"> <span class="mui-icon mui-icon-phone"></span> <div class="mui-media-body">Phone</div> </a> </li> </ul> <ul class="mui-table-view" id="content_list"> </ul> </div> </div> </div> </body> <script src="js/mui.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> mui.init() mui('.mui-scroll-wrapper').scroll() mui.back = function() { mui.toast("不让你点") }; mui.plusReady(function(){ mui.post( window.serv+"/content_list", {}, function(data){ console.log(JSON.stringify(data)); for (var i = 0; i < data.data.length; i++) { create_content(data.data[i]) } } ) }) document.addEventListener("talk",function(data){ mui.toast(data.detail.talk); }) function create_content(content){ //创建图文列表 var litag = document.createElement("li"); litag.className = "mui-table-view-cell mui-media"; var atag = document.createElement("a"); atag.id = content._id; // 点击事件 atag.onclick = function(){ console.log(this.id); } var imgtag = document.createElement("img"); imgtag.className = "mui-media-object mui-pull-left"; imgtag.src = window.serv_imge + content.avatar; // console.log(window.serv_imge + content.avatar); var divtag = document.createElement("div"); divtag.className = "mui-media-body"; divtag.innerText = content.title; var ptag = document.createElement("p"); ptag.className = "mui-ellipsis"; ptag.innerText = content.intro; litag.appendChild(atag); atag.appendChild(imgtag); atag.appendChild(divtag); divtag.appendChild(ptag); document.getElementById("content_list").appendChild(litag); } </script> </html>
使用模拟器访问,点击图文列表,HBuilder控制台就会输出id:
这些id是从MongoDB获取,并渲染的!
修改main.html,新增一个方法openPlayer,用来打开播放页面。传递id参数
点击图文列表时,就调用次方法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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"> <h1 class="mui-title">首页</h1> </header> <div class="mui-scroll-wrapper"> <div class="mui-scroll"> <div class="mui-content"> <div id="slider" class="mui-slider"> <div class="mui-slider-group mui-slider-loop"> <!-- 额外增加的一个节点(循环轮播:第一个节点是最后一张轮播) --> <div class="mui-slider-item mui-slider-item-duplicate"> <a href="#"> <img src="http://placehold.it/400x300"> </a> </div> <!-- 第一张 --> <div class="mui-slider-item"> <a href="#"> <img src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1537023056923&di=e50b27f1a9d34e586e421b50ff5cc0b0&imgtype=0&src=http%3A%2F%2Fatt.bbs.duowan.com%2Fforum%2F201508%2F18%2F173910p7045xys71x4zfyh.jpg" width="300px" height="400px"> </a> </div> <!-- 第二张 --> <div class="mui-slider-item"> <a href="#"> <img src="http://placehold.it/400x300"> </a> </div> <!-- 第三张 --> <div class="mui-slider-item"> <a href="#"> <img src="http://placehold.it/400x300"> </a> </div> <!-- 第四张 --> <div class="mui-slider-item"> <a href="#"> <img src="http://placehold.it/400x300"> </a> </div> <!-- 额外增加的一个节点(循环轮播:最后一个节点是第一张轮播) --> <div class="mui-slider-item mui-slider-item-duplicate"> <a href="#"> <img src="http://placehold.it/400x300"> </a> </div> </div> <div class="mui-slider-indicator"> <div class="mui-indicator mui-active"></div> <div class="mui-indicator"></div> <div class="mui-indicator"></div> <div class="mui-indicator"></div> </div> </div> <ul class="mui-table-view mui-grid-view mui-grid-9"> <li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3"> <a href="#"> <span class="mui-icon mui-icon-home"></span> <div class="mui-media-body">Home</div> </a> </li> <li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3"> <a href="#"> <span class="mui-icon mui-icon-email"><span class="mui-badge mui-badge-red">5</span></span> <div class="mui-media-body">Email</div> </a> </li> <li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3"> <a href="#"> <span class="mui-icon mui-icon-chatbubble"></span> <div class="mui-media-body">Chat</div> </a> </li> <li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3"> <a href="#"> <span class="mui-icon mui-icon-location"></span> <div class="mui-media-body">Location</div> </a> </li> <li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3"> <a href="#"> <span class="mui-icon mui-icon-search"></span> <div class="mui-media-body">Search</div> </a> </li> <li class="mui-table-view-cell mui-media mui-col-xs-4 mui-col-sm-3"> <a href="#"> <span class="mui-icon mui-icon-phone"></span> <div class="mui-media-body">Phone</div> </a> </li> </ul> <ul class="mui-table-view" id="content_list"> </ul> </div> </div> </div> </body> <script src="js/mui.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> mui.init() mui('.mui-scroll-wrapper').scroll() mui.back = function() { mui.toast("不让你点") }; mui.plusReady(function(){ mui.post( window.serv+"/content_list", {}, function(data){ console.log(JSON.stringify(data)); for (var i = 0; i < data.data.length; i++) { create_content(data.data[i]) } } ) }) document.addEventListener("talk",function(data){ mui.toast(data.detail.talk); }) function create_content(content){ //创建图文列表 var litag = document.createElement("li"); litag.className = "mui-table-view-cell mui-media"; var atag = document.createElement("a"); atag.id = content._id; // 点击事件 atag.onclick = function(){ console.log(this.id); openPlayer(this.id); //调用openPlayer方法 } var imgtag = document.createElement("img"); imgtag.className = "mui-media-object mui-pull-left"; imgtag.src = window.serv_imge + content.avatar; // console.log(window.serv_imge + content.avatar); var divtag = document.createElement("div"); divtag.className = "mui-media-body"; divtag.innerText = content.title; var ptag = document.createElement("p"); ptag.className = "mui-ellipsis"; ptag.innerText = content.intro; litag.appendChild(atag); atag.appendChild(imgtag); atag.appendChild(divtag); divtag.appendChild(ptag); document.getElementById("content_list").appendChild(litag); } function openPlayer(content_id){ //打开播放页面 mui.openWindow({ url:"player.html", id:"player.html", // 传递参数content_id extras:{content_id:content_id} }) } </script> </html>
修改player.html,接收id
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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> </div> </body> <script src="js/mui.js"></script> <script type="text/javascript"> mui.init(); var Sdata = null; mui.plusReady(function() { Sdata = plus.webview.currentWebview(); // 当前web页面 mui.toast(Sdata.content_id); // 弹窗显示由main.html传递的content_id }) </script> </html>
使用模拟器访问,底部会弹出id,效果如下:
发送POST请求
修改player.html,注意js代码部分,是mui.js。因为它依赖mui.js的一个全局变量!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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> </div> </body> <script src="js/mui.min.js"></script> <script type="text/javascript"> mui.init(); var Sdata = 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)); } ) }) </script> </html>
打开flask项目,修改 serv-->content.py,增加视图函数content_one
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Blueprint, jsonify,request from setting import MONGO_DB from setting import RET from bson import ObjectId cont = Blueprint("cont", __name__) @cont.route("/content_list", methods=["POST"]) def content_list(): # 内容列表 res_list = list(MONGO_DB.sources.find({})) # 字典转换列表 for index, item in enumerate(res_list): #返回 enumerate(枚举)对象 # 由于_id是ObjectId对象,转换为字符串 res_list[index]["_id"] = str(item.get("_id")) RET["code"] = 0 RET["msg"] = "" RET["data"] = res_list return jsonify(RET) # 返回json数据 @cont.route("/content_one", methods=["POST"]) def content_one(): """ 获取一条内容 :return: settings-->RET """ content_id = request.form.get("content_id") # 根据_id获取一条数据 res = MONGO_DB.sources.find_one({"_id":ObjectId(content_id)}) res["_id"] = str(res["_id"]) # 转换为str RET["code"] = 0 RET["msg"] = "" RET["data"] = res return jsonify(RET)
使用模拟器,重新点击一次,查看HBuilder控制台输出:
{"code":0,"data":{"_id":"5b9cd309e125320580da5b94","audio":"a6d680fe-fa80-4a54-82b8-b203f5a9c7b4.mp3","avatar":"a6d680fe-fa80-4a54-82b8-b203f5a9c7b4.jpg","category":"erge","intro":"【一千零一夜】经典儿歌","nickname":"一千零一夜频道","play_count":0,"title":"新年恰恰"},"msg":""} at player.html:39
既然数据有了,就可以渲染页面了!
修改player.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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> </div> </body> <script src="js/mui.js"></script> <script type="text/javascript"> mui.init(); var Sdata = 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; } ) }) </script> </html>
使用模拟器,重新点击。播放如下:
现在需要播放这首歌曲,如何实现呢?
听歌是一个新的接口,使用Audio模块
Audio模块
Audio模块用于提供音频的录制和播放功能,可调用系统的麦克风设备进行录音操作,也可调用系统的扬声器设备播放音频文件。通过plus.audio获取音频管理对象。
参考链接:
http://www.html5plus.org/doc/zh_cn/audio.html
AudioPlayer
音频播放对象
interface AudioPlayer {
function void play( successCB, errorCB );
function void pause();
function void resume();
function void stop();
function void seekTo( position );
function Number getDuration();
function Number getPosition();
function void setRoute( route );
}
说明:
音频播放对象,用于音频文件的播放。不能通过new方法直接创建,只能通过audio.createPlayer方法创建。
方法:
- play: 开始播放音频
- pause: 暂停播放音频
- resume: 恢复播放音频
- stop: 停止播放音频
- seekTo: 跳到指定位置播放音频
- getDuration: 获取音频流的总长度
- getPosition: 获取音频流当前播放的位置
- setRoute: 设置音频输出线路
参考链接:
http://www.html5plus.org/doc/zh_cn/audio.html#plus.audio.AudioPlayer
由于音频在后端,前端播放时,获取的是一个远程音频。能播放吗?
先测试一下本地播放
测试本地播放
使用HBuilder 新建一个文件夹audiotest,
在此目录,放一个音频文件123.mp3
修改player.html,增加一个button按钮。设置点击事件,播放123.mp3
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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> </div> </body> <script src="js/mui.js"></script> <script type="text/javascript"> mui.init(); var Sdata = 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; } ); function play_anything(content) { //播放音频 // 创建播放对象 player = plus.audio.createPlayer(content); player.play(); // 播放音频 } document.getElementById("play").addEventListener("tap", function() { // 播放指定音乐 play_anything("audiotest/123.mp3"); }); }) </script> </html>
打开模拟器,点击任意的图文列表。跳转到播放页面,点击播放,就会有声音了!
如果没有声音,打开音量合成器。将模拟器的音量设置和系统一样
参考链接:
https://www.yeshen.com/faqs/HyentzDUZ
如果还是没有声音,重启夜神模拟器。再次播放,就有声音了!
我就是这么解决的!
播放远程音频
既然本地的解决了,删除目录audiotest目录!
下面来测试一下播放远程音频。
修改mui.js,增加全局变量serv_audio
... window.serv = "http://192.168.11.86:9527" window.serv_imge = window.serv+"/get_image/"; window.serv_audio = window.serv+"/get_audio/"; ...
修改 player.html,设置为远程地址
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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> </div> </body> <script src="js/mui.js"></script> <script type="text/javascript"> mui.init(); var Sdata = 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是音频文件名 play_anything(data.data.audio); } ); 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() { // 播放指定音乐 // play_anything("audiotest/123.mp3"); }); }) </script> </html>
使用模拟器访问,点击一个列表,就会自动播放
如果要其他功能,比如暂停,继续,停止。
修改player.html,增加按钮,并增加3个方法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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> </div> </body> <script src="js/mui.js"></script> <script type="text/javascript"> mui.init(); var Sdata = null; //当前web页面 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是音频文件名 play_anything(data.data.audio); } ); 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中的对象 }); }) </script> </html>
使用模拟器访问,点击一首歌。会自动播放!
点击暂停-->继续,暂停-->继续,再点击停止。最后点击播放,就没有声音了!
注意:停止,是直接清空player中的对象。
二、web端的玩具
小故事
先来讲一个小故事:前期开发时,由于软件部需要测试。但是硬件部暂时无法拿出板子给软件部测试!
为了解决这个问题,硬件部使用了工程版的面板做了low版的样品,给软件部测试。第一天测试了几个小时,到了中午时间,同事都去吃饭了!
回来时,发现板子烧了!桌子烧一个大洞!键盘没了一半,显示器...
为啥呢?因为芯片程序,没有写任何保护措施。触发了一个死循环,导致板子过热!
为了避免重蹈覆辙,不允许使用工程版!
软件部为了测试,做了一个web版玩具页面!使用websocket
web版玩具页面
之前后端写的 manager.py,是用来提供资源服务的。
现在和玩具交互,使用websocket,它是通讯类服务。需要单独起一个进程,和资源进程分开!
录音
新建文件 im_serv.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Flask, request from geventwebsocket.websocket import WebSocket from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer import json app = Flask(__name__) user_socket_dict = {} # 空字典,用来存放用户名和发送消息 @app.route("/toy/<tid>") def toy(tid): # 获取请求的WebSocket对象 user_socket = request.environ.get("wsgi.websocket") # type:WebSocket if user_socket: # 设置键值对 user_socket_dict[tid] = user_socket print(user_socket_dict) # {'123456': <geventwebsocket.websocket.WebSocket object at 0x00000176ABD92E18>} # 循环,接收消息 while True: # 接收消息 msg = user_socket.receive() print(msg) # 打印 if __name__ == '__main__': # 创建一个WebSocket服务器 http_serv = WSGIServer(("0.0.0.0", 9528), app, handler_class=WebSocketHandler) # 开始监听HTTP请求 http_serv.serve_forever()
在templates 目录创建文件index.html,将之前的单聊的页面拿过来
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <audio src="" autoplay controls id="player"></audio> <br> <button onclick="start_reco()">开始废话</button> <br> <button onclick="stop_reco()">发送语音</button> </body> <script src="/static/recorder.js"></script> <script type="application/javascript"> // 获取音频文件 var get_file = "http://192.168.11.86:9527/get_audio/"; // 创建 WebSocket 对象 var ws = new WebSocket("ws://192.168.11.86: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); // 修改id为player的src属性,实现自动播放 document.getElementById("player").src = get_file + content.data; console.log(content.from_user + "给你点了一首歌"); } </script> </html>
将recorder.js,放到static目录。
github下载地址为:
https://github.com/mattdiamond/Recorderjs
修改 manager.py,渲染index.html页面
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 app = Flask(__name__) app.register_blueprint(get_file.getfile) app.register_blueprint(content.cont) @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": 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)
此时目录结构如下:
./
├── audio
├── audio_img
├── im_serv.py
├── manager.py
├── serv
│ ├── content.py
│ └── get_file.py
├── setting.py
├── static
│ └── recorder.js
├── templates
│ └── index.html
└── xiaopapa.py
访问首页:
http://127.0.0.1:9527/
注意:千万不要用IP访问,否则点击开始废话时,console会报错!
DOMException: Only secure origins are allowed (see: https://goo.gl/Y0ZkNV). (index):44 Uncaught TypeError: Cannot read property 'record' of null
提示 只有安全的来源是允许的。什么意思呢?就是说,这个链接,必须使用HTTPS连接。涉及HTTPS,必然就要买证书了!
当然了,证书又很贵!这里推荐bootcdn使用的免费证书,参考链接:
https://blog.bootcdn.cn/only-https/
访问首页之后,Pycharm控制台就会输出:
{'123456': <geventwebsocket.websocket.WebSocket object at 0x0000016DFC2BBE18>}
这个123456是从哪里来的呢?是用index.html来的,看这一行代码
var ws = new WebSocket("ws://192.168.11.86:9528/toy/123456");
这里,我指定的id为123456,后端接收后,以id为键。那么打印出一个字典!
点击页面的开始废话,再点击停止
查看Pycharm控制台输出:
bytearray(b'RIFF$@\x03\x00WAVEfmt...)
输出了bytearray数据类型,它可以直接保存为音频文件!
修改im_serv.py,判断类型,保存文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Flask, request from geventwebsocket.websocket import WebSocket from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer import json app = Flask(__name__) user_socket_dict = {} # 空字典,用来存放用户名和发送消息 @app.route("/toy/<tid>") def toy(tid): # 获取请求的WebSocket对象 user_socket = request.environ.get("wsgi.websocket") # type:WebSocket if user_socket: # 设置键值对 user_socket_dict[tid] = user_socket print(user_socket_dict) # {'123456': <geventwebsocket.websocket.WebSocket object at 0x00000176ABD92E18>} # 循环,接收消息 while True: # 接收消息 msg = user_socket.receive() print(msg) # 打印 if type(msg) == bytearray: with open('123.wav','wb') as f: f.write(msg) # 写入文件 if __name__ == '__main__': # 创建一个WebSocket服务器 http_serv = WSGIServer(("0.0.0.0", 9528), app, handler_class=WebSocketHandler) # 开始监听HTTP请求 http_serv.serve_forever()
重启im_serv.py
再次访问首页,录一段音。查看flask项目,会生成一个123.wav文件
直接播放这个文件,是有声音的!
注意:这个页面,就代指了玩具!
三、app通过websocket远程遥控玩具播放内容
现在手机APP,也需要通过websocket连接服务器。那么手机能不能也连接 ws://192.168.11.86:9528/toy/123456
呢?可以的!但是我不想让手机连接它。因为手机发送的数据,不一定是bytearray,也有可能是字符串。
需要重新写一个接口
修改im_serv.py,增加视图函数user_app
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Flask, request from geventwebsocket.websocket import WebSocket from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer import json app = Flask(__name__) user_socket_dict = {} # 空字典,用来存放用户名和发送消息 @app.route("/toy/<tid>") def toy(tid): # 玩具连接 # 获取请求的WebSocket对象 user_socket = request.environ.get("wsgi.websocket") # type:WebSocket if user_socket: # 设置键值对 user_socket_dict[tid] = user_socket print(user_socket_dict) # {'123456': <geventwebsocket.websocket.WebSocket object at 0x00000176ABD92E18>} # 循环,接收消息 while True: # 接收消息 msg = user_socket.receive() print(msg) # 打印 if type(msg) == bytearray: print(11) with open('123.wav','wb') as f: f.write(msg) # 写入文件 @app.route("/app/<uid>") def user_app(uid): # 手机app连接 user_socket = request.environ.get("wsgi.websocket") # type:WebSocket if user_socket: user_socket_dict[uid] = user_socket # { uid : websocket} print(user_socket_dict) while True: # 手机听歌 把歌曲发送给 玩具 1.将文件直接发送给玩具 2.将当前听的歌曲名称或ID发送到玩具 msg = user_socket.receive() print(msg) if __name__ == '__main__': # 创建一个WebSocket服务器 http_serv = WSGIServer(("0.0.0.0", 9528), app, handler_class=WebSocketHandler) # 开始监听HTTP请求 http_serv.serve_forever()
修改MyApp里面的player.html,增加按钮,发送给玩具
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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> <button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="send2toy">发送给玩具</button> </div> </body> <script src="js/mui.js"></script> <script type="text/javascript"> mui.init(); var Sdata = null; //当前web页面 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是音频文件名 play_anything(data.data.audio); } ); 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中的对象 }); }) </script> </html>
使用模拟器访问,效果如下:
建立连接
需要在APP中使用websocket,那么在什么时候,建立连接呢?
应该是在用户登录之后,点击首页时,建立websocket连接
修改mui.js,增加全局变量 ws_serv
... window.ws_serv = "192.168.11.86:9528"; window.serv = "http://192.168.11.86:9527"; window.serv_imge = window.serv+"/get_image/"; window.serv_audio = window.serv+"/get_audio/"; ...
修改index.html,判断登录状态,建立连接
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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="phone"> <span class="mui-icon mui-icon-phone"></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("phone").addEventListener("tap", function() { mui.toast("你點擊了電話按鈕"); mui.openWindow({ url: "phone.html", id: "phone.html", styles: window.styles, extras: { user_id: 123456 } }) }) 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) }) </script> </html>
注意:必须写在 mui.plusReady 里面
使用模拟器打开登录页面
登录成功之后,点击首页
查看Pycharm控制台输出
{'5b9bb768e1253281608e96eb': <geventwebsocket.websocket.WebSocket object at 0x0000027DC1F0BE18>}
这个时候,建立了一个连接!
前面的 5b9bb768e1253281608e96eb是MongoDB用户表记录的_id字段!
那么在后端flask里面的im_sevr.py中。user_socket_dict保存了所有的websocket连接!它们之间,就可以进行一对一通讯了!
一对一通讯
需求:播放页面的歌曲,通过websocket,将歌曲名发给web玩具进行播放!
修改player.html,需要将歌曲名发送给index.html页面。为什么呢?因为index.html建立了websocket连接。注意:index.html的webview的视图id为HBuilder
使用fire传值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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> <button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="send2toy">发送给玩具</button> </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>
修改index.html,监听send_music事件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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="phone"> <span class="mui-icon mui-icon-phone"></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("phone").addEventListener("tap", function() { mui.toast("你點擊了電話按鈕"); mui.openWindow({ url: "phone.html", id: "phone.html", styles: window.styles, extras: { user_id: 123456 } }) }) 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>
刷新web玩具页面,此时有2个websocket连接
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{'5b9bb768e1253281608e96eb': <geventwebsocket.websocket.WebSocket object at 0x0000027DC1FE1F50>, '123456': <geventwebsocket.websocket.WebSocket object at 0x0000027DC20001E8>}
使用模拟器访问首页,随便点击一首歌。它会自动播放,点击暂停,再点击发送给玩具
此时Pycharm输出一条信息:
{"music_name":"a6d680fe-fa80-4a54-82b8-b203f5a9c7b4.mp3","toy_id":"123456"}
歌曲名,获取到了。这里面的123456,是有player.html发送给index.html的!
网页播放音乐
这个时候,需要后端接收消息并处理了
修改im_serv.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Flask, request from geventwebsocket.websocket import WebSocket from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer import json app = Flask(__name__) user_socket_dict = {} # 空字典,用来存放用户名和发送消息 @app.route("/toy/<tid>") def toy(tid): # 玩具连接 # 获取请求的WebSocket对象 user_socket = request.environ.get("wsgi.websocket") # type:WebSocket if user_socket: # 设置键值对 user_socket_dict[tid] = user_socket print(user_socket_dict) # {'123456': <geventwebsocket.websocket.WebSocket object at 0x00000176ABD92E18>} # 循环,接收消息 while True: # 接收消息 msg = user_socket.receive() print(msg) # 打印 if type(msg) == bytearray: print(11) with open('123.wav','wb') as f: f.write(msg) # 写入文件 @app.route("/app/<uid>") def user_app(uid): # 手机app连接 user_socket = request.environ.get("wsgi.websocket") # type:WebSocket if user_socket: user_socket_dict[uid] = user_socket # { uid : websocket} print(user_socket_dict) while True: # 手机听歌 把歌曲发送给 玩具 1.将文件直接发送给玩具 2.将当前听的歌曲名称或ID发送到玩具 msg = user_socket.receive() msg_dict = json.loads(msg) # 接收值,由于APP发送的是json,需要反序列化 # {"music_name": "a6d680fe-fa80-4a54-82b8-b203f5a9c7b4.mp3", "toy_id": "123456"} user_id = msg_dict.get("toy_id") # 获取toy_id music_name = msg_dict.get("music_name") # 获取歌曲名 # 从user_socket_dict这个大字典中获取 other_user_socket = user_socket_dict.get(user_id) # 获取APP用户的websocket对象 # 构造数据 send_str = { "code": 0, "from_user": uid, # APP用户id "data": music_name # 歌曲名 } # 发送给web玩具页面,注意:不能是jsonify,它是针对于HTTP请求,会设置响应头 # 这里是websocket连接,只能用json.dumps other_user_socket.send(json.dumps(send_str)) if __name__ == '__main__': # 创建一个WebSocket服务器 http_serv = WSGIServer(("0.0.0.0", 9528), app, handler_class=WebSocketHandler) # 开始监听HTTP请求 http_serv.serve_forever()
重启 im_serv.py
重启之后,websocket连接就丢失了!
先访问web玩具页面
http://127.0.0.1:9527/
将模拟器中的HBuilder进程关闭,重启开启,并登陆。
点击一首歌曲,点击暂停,再点击一次发送给玩具!
此时,播放按钮,会播放音乐。
效果如下:
为什么,点击网页的播放按钮,就能播放指定的音乐呢?
查看flask中的templates-->index.html,这一段代码
ws.onmessage = function (data) { // 客户端接收服务端数据时触发 // console.log(get_file + data.data); var content = JSON.parse(data.data); // 修改id为player的src属性,实现自动播放 document.getElementById("player").src = get_file + content.data; console.log(content.from_user + "给你点了一首歌"); }
注意:ws.onmessage是接收到数据时,会自动触发!
那么它接收到什么数据了呢?接收了一个json数据。序列化之后,应该是这个样子
{code: 0, from_user: "5b9bb768e1253281608e96eb", data: "4ed490e8-aded-4f23-8a7c-c845e48ec778.mp3"}
那么它就会修改页面的src属性。那么页面点击播放,就可以播放音乐了!
四、玩具管理页面
默认登录之后,会跳转到用户信息页面,这里需要展示用户头像。
但是数据库少了头像字段。
使用客户端工具访问用户表
默认的ORM语句为:
db.users.find({})
目前只有一个用户
增加一个字段avatar,这个表示头像
db.users.updateMany({"username":"xiao"},{$set:{"avatar":"boy.jpg"}})
修改flask后端的 的注册函数,增加avatar字段
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 app = Flask(__name__) app.register_blueprint(get_file.getfile) app.register_blueprint(content.cont) @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)
进入HBuilder的项目MyApp,修改user_info.html,增加头像
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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"> <h1 class="mui-title">用户信息</h1> </header> <div class="mui-content"> <ul class="mui-table-view"> <div class="mui-row" style="text-align: center;"> <img src="" id="avatar" style="width: 250px;height: 250px; border-radius: 100%;"/> </div> <li class="mui-table-view-cell"><span>用户名</span><span id="username" class="mui-pull-right">111</span></li> <li class="mui-table-view-cell"><span>昵称</span><span id="nickname" class="mui-pull-right">22</span></li> </ul> <button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="logout">退出登陆</button> </div> </body> <script src="js/mui.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> mui.init() mui.back = function() {}; mui.plusReady(function() { var Sdata = plus.webview.currentWebview(); mui.post( window.serv + "/user_info", { user_id: Sdata.user_id }, function(data) { console.log(JSON.stringify(data)); console.log("wfdsfdsafdsafas"); document.getElementById("username").innerText = data.username; document.getElementById("nickname").innerText = data.nickname; // 修改头像地址 document.getElementById("avatar").src = "avatar/" + data.avatar; // console.log("avatar/" + data.avatar); } ) }) document.getElementById("logout").addEventListener("tap", function() { plus.storage.removeItem("user") mui.openWindow({ url: "login.html", id: "login.html", styles: window.styles }) }); </script> </html>
使用模拟器重新登录,效果如下:
修改user_info.html,增加一行,用来管理玩具。并增加点击事件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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"> <h1 class="mui-title">用户信息</h1> </header> <div class="mui-content"> <ul class="mui-table-view"> <div class="mui-row" style="text-align: center;"> <img src="" id="avatar" / style="width: 250px;height: 250px; border-radius: 100%;"> </div> <li class="mui-table-view-cell"><span>用户名</span><span id="username" class="mui-pull-right">111</span></li> <li class="mui-table-view-cell"><span>昵称</span><span id="nickname" class="mui-pull-right">22</span></li> </ul> <ul class="mui-table-view"> <li class="mui-table-view-cell" id="toy_manager"><i class="mui-icon mui-icon-qq"></i>管理我的玩具</li> </ul> <button type="button" class="mui-btn mui-btn-blue mui-btn-block" id="logout">退出登陆</button> </div> </body> <script src="js/mui.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> mui.init() mui.back = function() {}; mui.plusReady(function() { var Sdata = plus.webview.currentWebview(); mui.post( window.serv + "/user_info", { user_id: Sdata.user_id }, function(data) { console.log(JSON.stringify(data)); console.log("wfdsfdsafdsafas"); document.getElementById("username").innerText = data.username; document.getElementById("nickname").innerText = data.nickname; // 修改头像地址 document.getElementById("avatar").src = "avatar/" + data.avatar; // console.log("avatar/" + data.avatar); } ) }) document.getElementById("logout").addEventListener("tap", function() { plus.storage.removeItem("user") mui.openWindow({ url: "login.html", id: "login.html", styles: window.styles }) }); // 管理玩具页面 document.getElementById("toy_manager").addEventListener("tap", function() { mui.openWindow({ url: "toy_manager.html", id: "toy_manager.html", }) }); </script> </html>
新建文件 toy_manager.html
新建目录image,用来保存添加按钮的图片
从网络上面,搜索一个添加按钮图片,放到此目录!
此时,目录结构如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
./
└── MyApp
├── avatar
│ ├── boy.jpg
│ └── girl.jpg
├── css
│ ├── mui.css
│ └── mui.min.css
├── fonts
│ └── mui.ttf
├── images
│ └── add.png
├── index.html
├── js
│ ├── md5.min.js
│ ├── mui.js
│ └── mui.min.js
├── login.html
├── main.html
├── manifest.json
├── phone.html
├── player.html
├── reg.html
├── toy_manager.html
├── unpackage
└── user_info.html
修改 toy_manager.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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="toy_list"> <li class="mui-table-view-cell mui-media"> <a id="add_toy"> <img class="mui-media-object mui-pull-left" src="images/add.png"> <div class="mui-media-body"> 你还没有玩具 <p class="mui-ellipsis">点击此处扫描二维码添加玩具</p> </div> </a> </li> </ul> </div> </body> <script src="js/mui.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> mui.init() function create_content(content) { var litag = document.createElement("li"); litag.className = "mui-table-view-cell mui-media"; var atag = document.createElement("a"); atag.id = content._id; // 点击事件 周一把这儿写了 绑定事件 atag.onclick = function() { console.log(this.id); openPlayer(this.id); } var imgtag = document.createElement("img"); imgtag.className = "mui-media-object mui-pull-left"; imgtag.src = window.serv_imge + content.avatar; var divtag = document.createElement("div"); divtag.className = "mui-media-body"; divtag.innerText = content.title; var ptag = document.createElement("p"); ptag.className = "mui-ellipsis"; ptag.innerText = content.intro; litag.appendChild(atag); atag.appendChild(imgtag); atag.appendChild(divtag); divtag.appendChild(ptag); document.getElementById("toy_list").appendChild(litag); } document.getElementById("add_toy").addEventListener("tap", function() { mui.openWindow({ url: "扫描二维码.html", id: "扫描二维码.html", }) }) </script> </html>
重新登录,效果如下:
总结:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1. app端内容播放 听歌:plus.audio 创建:var player = plus.audio.createPlay(内容的URL路径) //苹果暂时不支持HTTP 开始播放 : player.play() 暂停播放 :player.pause() 继续播放 :player.resume() 停止播放 :player.stop() 清空player中对象 2. 手动写一个web端的玩具(小剧情) 硬件需要时间,咱们先写一个协议,然后开发一个初级硬件暂时先顶上,用了一周的事件做了一个LowB的硬件 事件: 中午吃饭忘了关闭硬件,结果芯片过热,造成芯片烧毁,烧毁了一个桌面,烫坏了一个显示器,键盘没了一半儿 老张呵斥硬件部:工程开发板不允许再次使用,所有压力全部在软件部,自己写一个Web模拟玩具收发消息 3. app通过websocket 事件远程遥控玩具播放内容 websocket 在整个app中只能存在一个 app 端 : mui.fire(ws页面,"事件",{data}) ws页面: document.addEventListener("事件",function(data){ data.detail }) 开启websocket服务 - 两个接口 app toy - 两个前端 连接 websocket - {user:websocket,toy:websocket} appuser 发送了一个字符串给服务端 {toy_id:123456,content_id:tyuiop123r234} 通过字符串中的toy_id 拿到 websocket_dict 中的 toy websocket 可以给 toy 中的websocket 发送消息了 toy.send(json.dumps({}))
完整代码,请参考github:
https://github.com/987334176/Intelligent_toy/archive/v1.1.zip