昨日内容回顾

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

内容如下:

<!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,引入图片,将图片设置为圆的

<!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

<!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&amp;quality=80&amp;size=b9999_10000&amp;sec=1537023056923&amp;di=e50b27f1a9d34e586e421b50ff5cc0b0&amp;imgtype=0&amp;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参数

点击图文列表时,就调用次方法。

<!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&amp;quality=80&amp;size=b9999_10000&amp;sec=1537023056923&amp;di=e50b27f1a9d34e586e421b50ff5cc0b0&amp;imgtype=0&amp;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

<!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的一个全局变量!

<!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

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

<!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方法创建。

方法:

参考链接:

http://www.html5plus.org/doc/zh_cn/audio.html#plus.audio.AudioPlayer

由于音频在后端,前端播放时,获取的是一个远程音频。能播放吗?

先测试一下本地播放

测试本地播放

使用HBuilder 新建一个文件夹audiotest,

在此目录,放一个音频文件123.mp3

修改player.html,增加一个button按钮。设置点击事件,播放123.mp3

<!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,设置为远程地址

<!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个方法。

<!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

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,将之前的单聊的页面拿过来

<!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页面

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控制台就会输出:

{'': <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,判断类型,保存文件

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

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,增加按钮,发送给玩具

<!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,判断登录状态,建立连接

<!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传值

<!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:"" // 发给玩具id为12345
})
}); })
</script> </html>

修改index.html,监听send_music事件

<!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连接

{'5b9bb768e1253281608e96eb': <geventwebsocket.websocket.WebSocket object at 0x0000027DC1FE1F50>, '': <geventwebsocket.websocket.WebSocket object at 0x0000027DC20001E8>}

使用模拟器访问首页,随便点击一首歌。它会自动播放,点击暂停,再点击发送给玩具

此时Pycharm输出一条信息:

{"music_name":"a6d680fe-fa80-4a54-82b8-b203f5a9c7b4.mp3","toy_id":""}

歌曲名,获取到了。这里面的123456,是有player.html发送给index.html的!

网页播放音乐

这个时候,需要后端接收消息并处理了

修改im_serv.py

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字段

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,增加头像

<!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,增加一行,用来管理玩具。并增加点击事件

<!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,用来保存添加按钮的图片

从网络上面,搜索一个添加按钮图片,放到此目录!

此时,目录结构如下:

./
└── 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

<!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="data: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>

重新登录,效果如下:

总结:

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

python 全栈开发,Day127(app端内容播放,web端的玩具,app通过websocket远程遥控玩具播放内容,玩具管理页面)的更多相关文章

  1. 老男孩最新Python全栈开发视频教程(92天全)重点内容梳理笔记 看完就是全栈开发工程师

    为什么要写这个系列博客呢? 说来讽刺,91年生人的我,同龄人大多有一份事业,或者有一个家庭了.而我,念了次985大学,年少轻狂,在大学期间迷信创业,觉得大学里的许多课程如同吃翔一样学了几乎一辈子都用不 ...

  2. python全栈开发学习_内容目录及链接

    python全栈开发学习_day1_计算机五大组成部分及操作系统 python全栈开发学习_day2_语言种类及变量 python全栈开发_day3_数据类型,输入输出及运算符 python全栈开发_ ...

  3. python 全栈开发,Day99(作业讲解,DRF版本,DRF分页,DRF序列化进阶)

    昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确. - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery ...

  4. Python全栈开发【面向对象进阶】

    Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...

  5. Python全栈开发【面向对象】

    Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...

  6. Python全栈开发【模块】

    Python全栈开发[模块] 本节内容: 模块介绍 time random os sys json & picle shelve XML hashlib ConfigParser loggin ...

  7. Python全栈开发【基础四】

    Python全栈开发[基础四] 本节内容: 匿名函数(lambda) 函数式编程(map,filter,reduce) 文件处理 迭代器 三元表达式 列表解析与生成器表达式 生成器 匿名函数 lamb ...

  8. Python全栈开发【基础三】

    Python全栈开发[基础三]  本节内容: 函数(全局与局部变量) 递归 内置函数 函数 一.定义和使用 函数最重要的是减少代码的重用性和增强代码可读性 def 函数名(参数): ... 函数体 . ...

  9. Python全栈开发【基础二】

    Python全栈开发[基础二] 本节内容: Python 运算符(算术运算.比较运算.赋值运算.逻辑运算.成员运算) 基本数据类型(数字.布尔值.字符串.列表.元组.字典) 其他(编码,range,f ...

随机推荐

  1. JAVA记录-POST与GET方式区别

    1.get是从服务器上获取数据的一种请求,post是向服务器提交数据的一种请求. 2.get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到 ...

  2. Hive记录-impala常用命令

    1.impala是什么 Impala是Cloudera公司主导开发的新型查询系统,它提供SQL语义,能查询存储在Hadoop的HDFS和HBase中的PB级大数据.已有的Hive系统虽然也提供了SQL ...

  3. HDU - 3002 King of Destruction(最小割)

    http://acm.hdu.edu.cn/showproblem.php?pid=3002   最小割模板 #include<iostream> #include<cmath> ...

  4. Ant基础知识1

    1.Ant简介 Apache Ant是一个将软件编译/测试/部署等步骤联系在一起加以优化的一个构建工具,常用于java环境中的软件开发.Ant的默认配置文件是build.xml. 对java语言的支持 ...

  5. webuploader

    https://www.cnblogs.com/study-fanzeng/p/8930939.html http://fex.baidu.com/webuploader/doc/index.html ...

  6. Javaweb中提到的反射浅析(附源码)

    反射:一个jdk5.0的新特性,高级运用.在后期的框架中,这个是一大重点,现在估计我们都不会太多的接触他的.但是为了后面的铺垫,我想还是先了解一下: 先构造一个类,然后我们用反射来获取,调用里面的方法 ...

  7. string字符串js操作

    String 对象方法 方法 描述 anchor() 创建 HTML 锚. big() 用大号字体显示字符串. blink() 显示闪动字符串. bold() 使用粗体显示字符串. charAt() ...

  8. 第19月第8天 斯坦福大学公开课机器学习 (吴恩达 Andrew Ng)

    1.斯坦福大学公开课机器学习 (吴恩达 Andrew Ng) http://open.163.com/special/opencourse/machinelearning.html 笔记 http:/ ...

  9. 第16月第23天 atos

    1. grep --after-context=2 "Binary Images:" *crash xcrun atos -o zhiniao_adhoc_stg1.app.dSY ...

  10. cdh部署

    supermicro安装环境 本次安装基于无因特网的环境,共安装2个节点(一个master节点,一个data及节点),所用系统为centos7.x,所有安装过程均使用root用户.具体的节点信息如下: ...