python 全栈开发,Day130(多玩具端的遥控功能, 简单的双向聊天,聊天记录存放数据库,消息提醒,玩具主动发起消息,玩具主动发起点播)

 

先下载github代码,下面的操作,都是基于这个版本来的!

https://github.com/987334176/Intelligent_toy/archive/v1.3.zip

注意:由于涉及到版权问题,此附件没有图片和音乐。请参考链接,手动采集一下!

请参考链接:

https://www.cnblogs.com/xiao987334176/p/9647993.html#autoid-3-4-0

 

一、多玩具端的遥控功能

地址统一管理

进入flask项目,修改 templates-->index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

</head>
<body>
<audio src="" autoplay="autoplay" controls id="player"></audio>
<br>
<input type="text" id="device_id">
<button οnclick="start_toy()">玩具开机键</button>
<br>
<button οnclick="start_reco()">开始废话</button>
<br>
<button οnclick="stop_reco()">发送语音</button>
</body>
<script src="/static/recorder.js"></script>
<script src="/static/jquery.min.js"></script>
<script type="application/javascript">
    var serv = "http://192.168.11.40:9527";
    var ws_serv = "ws://192.168.11.40:9528";

    // 获取音频文件
    var get_music = serv + "/get_audio/";

    var ws = null;  // WebSocket 对象
    var reco = null;
    // 创建AudioContext对象
    var audio_context = new AudioContext();
    //要获取音频和视频
    navigator.getUserMedia = (navigator.getUserMedia ||
        navigator.webkitGetUserMedia ||
        navigator.mozGetUserMedia ||
        navigator.msGetUserMedia);

    // 拿到媒体对象,允许音频对象
    navigator.getUserMedia({audio: true}, create_stream, function (err) {
        console.log(err)
    });

    //创建媒体流容器
    function create_stream(user_media) {
        var stream_input = audio_context.createMediaStreamSource(user_media);
        // 给Recoder 创建一个空间,麦克风说的话,都可以录入。是一个流
        reco = new Recorder(stream_input);

    }

    function start_reco() {  //开始录音
        reco.record();  //往里面写流
    }

    function stop_reco() {  //停止录音
        reco.stop();  //停止写入流
        get_audio();  //调用自定义方法
        reco.clear();  //清空容器
    }

    function get_audio() {  // 获取音频
        reco.exportWAV(function (wav_file) {
            ws.send(wav_file);  //使用websocket连接发送数据给后端
        })
    }

    function start_toy() {  // 玩具开机
        // 获取输入的设备id
        var device_id = document.getElementById("device_id").value;
        // 发送post请求
        $.post(
            // 这里的地址必须是127.0.0.1,否则会有跨域问题
            "http://127.0.0.1:9527/device_toy_id",
            // 发送设备id
            {device_id: device_id},
            function (data) {
                console.log(data);
                toy_id = data.data.toy_id;  // 玩具id
                // 修改audio标签的src属性
                document.getElementById("player").src = get_music + data.data.audio;
                if (toy_id) {  // 判断玩具id存在时
                    ws = new WebSocket(ws_serv + "/toy/" + toy_id);
                    ws.onmessage = function (data) {
                        // console.log(get_music + data.data);
                        var content = JSON.parse(data.data);  //反序列化数据
                        document.getElementById("player").src = get_music + content.data;
                        console.log(content.from_user + "给你点了一首歌");
                    }
                }
            }, "json"
            // 规定预期的服务器响应的数据类型为json
        );
    }


</script>
</html>
View Code

 

进入 HBuilder项目MyApp,查看mui.js。

...
window.ws_serv = "192.168.11.40:9528";
window.serv = "http://192.168.11.40:9527";
window.serv_imge = window.serv+"/get_image/";
window.serv_audio = window.serv+"/get_audio/";
...

这里面,已经是统一化管理了

 

昨天内容,已经实现了简单的对话。但是点歌功能被删掉了,今天需要开启一下。

进入flask项目,修改im_serv.py,增加msg_type。用来做消息类型判断!

from flask import Flask, request
from geventwebsocket.websocket import WebSocket
from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer
import json, os
from uuid import uuid4
from setting import AUDIO_FILE,CHAT_FILE
from serv import content

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)

    file_name = ""
    to_user = ""

    while True:  # 手机听歌 把歌曲发送给 玩具 1.将文件直接发送给玩具 2.将当前听的歌曲名称或ID发送到玩具
        msg = user_socket.receive()
        if type(msg) == bytearray:  # 判断类型为bytearray
            file_name = f"{uuid4()}.amr"  # 文件后缀为amr,安卓和ios通用
            file_path = os.path.join(CHAT_FILE, file_name)  # 存放在chat目录
            print(msg)
            with open(file_path, "wb") as f:
                f.write(msg)  # 写入文件

            # 将amr转换为mp3,因为html中的audio不支持amr
            os.system(f"ffmpeg -i {file_path} {file_path}.mp3")

        else:
            msg_dict = json.loads(msg)
            to_user = msg_dict.get("to_user")  # 获取目标用户

            if msg_dict.get("msg_type") == "music":
                other_user_socket = user_socket_dict.get(to_user)

                send_str = {
                    "code": 0,
                    "from_user": uid,
                    "msg_type": "music",
                    "data": msg_dict.get("data")
                }
                other_user_socket.send(json.dumps(send_str))

            # res = content._content_one(content_id)
        if file_name and to_user:  # 如果文件名和发送用户同上存在时
            # 获取websocket对象
            other_user_socket = user_socket_dict.get(to_user)
            # 构造数据
            send_str = {
                "code": 0,
                "from_user": uid,
                "msg_type": "chat", # 聊天类型
                # 后缀必须是mp3的
                "data": f"{file_name}.mp3"
            }
            # 发送数据给前端页面
            other_user_socket.send(json.dumps(send_str))
            # 最后一定要清空这2个变量,否则造成混乱
            file_name = ""
            to_user = ""


if __name__ == '__main__':
    # 创建一个WebSocket服务器
    http_serv = WSGIServer(("0.0.0.0", 9528), app, handler_class=WebSocketHandler)
    # 开始监听HTTP请求
    http_serv.serve_forever()


'''
{
    "code": 0,
    "from_user": uid,  # APP用户id
    "data": music_name  # 歌曲名
}
'''
View Code

这里最重要的是msg_ytpe

 

修改 templates-->index.html,做msg_type判断

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

</head>
<body>
<audio src="" autoplay="autoplay" controls id="player"></audio>
<br>
<input type="text" id="device_id">
<button οnclick="start_toy()">玩具开机键</button>
<br>
<button οnclick="start_reco()">开始废话</button>
<br>
<button οnclick="stop_reco()">发送语音</button>
</body>
<script src="/static/recorder.js"></script>
<script src="/static/jquery.min.js"></script>
<script type="application/javascript">
    var serv = "http://192.168.11.40:9527";
    var ws_serv = "ws://192.168.11.40:9528";

    // 获取音频文件
    var get_music = serv + "/get_audio/";

    var ws = null;  // WebSocket 对象
    var reco = null;
    // 创建AudioContext对象
    var audio_context = new AudioContext();
    //要获取音频和视频
    navigator.getUserMedia = (navigator.getUserMedia ||
        navigator.webkitGetUserMedia ||
        navigator.mozGetUserMedia ||
        navigator.msGetUserMedia);

    // 拿到媒体对象,允许音频对象
    navigator.getUserMedia({audio: true}, create_stream, function (err) {
        console.log(err)
    });

    //创建媒体流容器
    function create_stream(user_media) {
        var stream_input = audio_context.createMediaStreamSource(user_media);
        // 给Recoder 创建一个空间,麦克风说的话,都可以录入。是一个流
        reco = new Recorder(stream_input);

    }

    function start_reco() {  //开始录音
        reco.record();  //往里面写流
    }

    function stop_reco() {  //停止录音
        reco.stop();  //停止写入流
        get_audio();  //调用自定义方法
        reco.clear();  //清空容器
    }

    function get_audio() {  // 获取音频
        reco.exportWAV(function (wav_file) {
            ws.send(wav_file);  //使用websocket连接发送数据给后端
        })
    }

    function start_toy() {  // 玩具开机
        // 获取输入的设备id
        var device_id = document.getElementById("device_id").value;
        // 发送post请求
        $.post(
            // 这里的地址必须是127.0.0.1,否则会有跨域问题
            "http://127.0.0.1:9527/device_toy_id",
            // 发送设备id
            {device_id: device_id},
            function (data) {
                console.log(data);
                toy_id = data.data.toy_id;  // 玩具id
                // 修改audio标签的src属性
                document.getElementById("player").src = get_music + data.data.audio;
                if (toy_id) {  // 判断玩具id存在时
                    ws = new WebSocket(ws_serv + "/toy/" + toy_id);
                    ws.onmessage = function (data) {
                        // console.log(get_music + data.data);
                        var content = JSON.parse(data.data);  //反序列化数据
                        // 判断消息类型
                        if (content.msg_type == "chat") {
                            document.getElementById("player").src = get_chat + content.data;
                            document.getElementById("to_user").innerText = content.from_user;
                            console.log(content.from_user + "给你发送了一条消息");
                        }
                        if (content.msg_type == "music") {
                            document.getElementById("player").src = get_music + content.data;
                            console.log(content.from_user + "给你点播了歌儿");
                        }
                    }
                }
            }, "json"
            // 规定预期的服务器响应的数据类型为json
        );
    }


</script>
</html>
View Code

 

进入 HBuilder项目,修改index.html,修改send_music监听事件。统一格式,发送to_user

<!DOCTYPE html>
<html>

    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
        <title></title>
        <script src="js/mui.js"></script>
        <link href="css/mui.min.css" rel="stylesheet" />
    </head>

    <body>
        <!--底部选项卡-->
        <nav class="mui-bar mui-bar-tab">
            <a class="mui-tab-item mui-active" id="index">
                <span class="mui-icon mui-icon-home"></span>
                <span class="mui-tab-label">首页</span>
            </a>
            <a class="mui-tab-item" id="message">
                <span class="mui-icon mui-icon-chatbubble"></span>
                <span class="mui-tab-label">消息</span>
            </a>
            <a class="mui-tab-item">
                <span class="mui-icon mui-icon-email"></span>
                <span class="mui-tab-label">邮件</span>
            </a>
            <a class="mui-tab-item" id="login">
                <span class="mui-icon mui-icon-gear"></span>
                <span class="mui-tab-label">设置</span>
            </a>
        </nav>
    </body>
    <script type="text/javascript" charset="utf-8">
        var ws = null; // websocket对象
        mui.init({
            subpages: [{
                url: "main.html",
                id: "main.html",
                styles: window.styles
            }]
        });
        mui.plusReady(function() {
            //            console.log(JSON.stringify(plus.webview.currentWebview()))
            if(plus.storage.getItem("user")) { // 判断是否登录
                console.log('已结登录了!');
                //连接websocket连接
                ws = new WebSocket("ws://" + window.ws_serv + "/app/" + plus.storage.getItem("user"))
                // 客户端接收服务端数据时触发
                ws.onmessage = function() {};
            }
        });

        // 消息
        document.getElementById("message").addEventListener("tap", function() {
            mui.openWindow({
                url: "message.html",
                id: "message.html",
                styles: window.styles,
                extras: {
                    // 传输用户id,给message.html
                    user_id: plus.storage.getItem("user")
                }
            })
        });

        document.getElementById("index").addEventListener("tap", function() {
            mui.openWindow({
                url: "main.html",
                id: "main.html",
                styles: window.styles
            })
        })

        document.getElementById("login").addEventListener("tap", function() {
            // 自动登录,判断storage中的user存在,就跳转到user_info,否则跳转login
            if(plus.storage.getItem("user")) {
                mui.openWindow({
                    url: "user_info.html",
                    id: "user_info.html",
                    styles: window.styles,
                    extras: {
                        user_id: plus.storage.getItem("user")
                    }
                })
            } else {
                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 = { //构造数据
                data: music_name,
                to_user: toy_id,  // 目标用户,这里统一格式
                msg_type:"music",  // 类型为音乐
            }
            // 发送数据给后端,注意要json序列化
            ws.send(JSON.stringify(send_str));
        });

        document.addEventListener("send_msg", function(data) {  //发送消息
            var filename = data.detail.filename
            var to_user = data.detail.to_user
            send_str = {
                to_user: to_user
            }
            ws.send(JSON.stringify(send_str))
            plus.io.resolveLocalFileSystemURL(filename, function(entry) {
                // 可通过entry对象操作test.html文件 
                entry.file(function(file) {
                    // FileReader文件系统中的读取文件对象,用于获取文件的内容
                    var fileReader = new plus.io.FileReader();

                    //                        alert("getFile:" + JSON.stringify(file));
                    // readAsDataURL: 以URL编码格式读取文件数据内容
                    fileReader.readAsDataURL(file, 'utf-8');
                    // onloadend: 文件读取操作完成时的回调函数
                    fileReader.onloadend = function(evt) {
                        console.log(evt.target.result);
                        var b = dataURLtoBlob(evt.target.result);
                        ws.send(b);  // 发送blob数据

                    }
                    //                        alert(file.size + '--' + file.name)
                });
            });

        })

        function dataURLtoBlob(dataurl) {  // 数据转换为Blob
            // 逻辑很复杂,这里不解释了。直接用就可以了!
            var arr = dataurl.split(','),
                mime = arr[0].match(/:(.*?);/)[1],
                bstr = atob(arr[1]),
                n = bstr.length,
                u8arr = new Uint8Array(n);
            while(n--) {
                u8arr[n] = bstr.charCodeAt(n);
            }
            var a = new Blob([u8arr], {
                type: mime
            });
            return a
        }
    </script>

</html>
View Code

 

进入flask项目,根目录下新建文件夹 chat

此时项目结构如下:

./
├── audio
├── audio_img
├── chat
├── device_code
├── im_serv.py
├── manager.py
├── QRcode.py
├── serv
│   ├── content.py
│   ├── devices.py
│   ├── friend.py
│   ├── get_file.py
│   └── toys.py
├── setting.py
├── static
│   ├── jquery.min.js
│   └── recorder.js
├── templates
│   └── index.html
├── utils
│   └── baidu_ai.py
└── xiaopapa.py
View Code

 

进入目录 audio,将里面所有 amr和amr.mp3文件全部删掉!

这些文件,会自动放到chat目录

 

修改 settings.py,增加 CHAT_FILE变量

import pymongo

client = pymongo.MongoClient(host="127.0.0.1", port=27017)
MONGO_DB = client["bananabase"]

RET = {
    # 0: false 2: True
    "code": 0,
    "msg": "",  # 提示信息
    "data": {}
}

XMLY_URL = "http://m.ximalaya.com/tracks/"  # 喜马拉雅链接
CREATE_QR_URL = "http://qr.liantu.com/api.php?text="  # 生成二维码API

# 文件目录
import os

AUDIO_FILE = os.path.join(os.path.dirname(__file__), "audio")  # 音频
AUDIO_IMG_FILE = os.path.join(os.path.dirname(__file__), "audio_img")  # 音频图片

DEVICE_CODE_PATH = os.path.join(os.path.dirname(__file__), "device_code")  # 二维码
CHAT_FILE = os.path.join(os.path.dirname(__file__), "chat")  # 聊天

# 百度AI配置
APP_ID = "11712345"
API_KEY = "3v3igzCkVFUDwFByNE12345"
SECRET_KEY = "jRnwLE7kzC1aRi2FD10OQY3y9O12345"
SPEECH = {
    "spd": 4,
    'vol': 5,
    "pit": 8,
    "per": 4
}
View Code

 

修改 get_file.py,增加get_chat视图函数

from flask import Blueprint, send_file
from setting import AUDIO_FILE
from setting import AUDIO_IMG_FILE
from setting import CHAT_FILE
import os

getfile = Blueprint("getfile", __name__)

@getfile.route("/get_audio/<filename>")
def get_audio(filename):  # 获取音频
    sendfile = os.path.join(AUDIO_FILE, filename)
    return send_file(sendfile)

@getfile.route("/get_image/<filename>")
def get_image(filename):  # 获取图片
    sendfile = os.path.join(AUDIO_IMG_FILE, filename)
    return send_file(sendfile)

@getfile.route("/get_chat/<filename>")
def get_chat(filename):  # 获取聊天文件
    sendfile = os.path.join(CHAT_FILE, filename)
    return send_file(sendfile)
View Code

 

访问网页,让2个玩具页面开机!

 

使用模拟器访问页面,点击一首歌曲,点击发送给 小甜甜

 

此时浏览器第二个页面,就是小甜甜。会自动播放音乐!

 

注意:必须要web玩具和手机APP,都连接上websocket才行。否则会导致点歌不成功!

 

自动重连websocket

为了避免后续操作,因为websocket连接而导致的问题。修改相关代码,可以自动重连!

进入 HBuilder项目MyApp,修改index.html,增加ws.onclose

<!DOCTYPE html>
<html>

    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
        <title></title>
        <script src="js/mui.js"></script>
        <link href="css/mui.min.css" rel="stylesheet" />
    </head>

    <body>
        <!--底部选项卡-->
        <nav class="mui-bar mui-bar-tab">
            <a class="mui-tab-item mui-active" id="index">
                <span class="mui-icon mui-icon-home"></span>
                <span class="mui-tab-label">首页</span>
            </a>
            <a class="mui-tab-item" id="message">
                <span class="mui-icon mui-icon-chatbubble"></span>
                <span class="mui-tab-label">消息</span>
            </a>
            <a class="mui-tab-item">
                <span class="mui-icon mui-icon-email"></span>
                <span class="mui-tab-label">邮件</span>
            </a>
            <a class="mui-tab-item" id="login">
                <span class="mui-icon mui-icon-gear"></span>
                <span class="mui-tab-label">设置</span>
            </a>
        </nav>
    </body>
    <script type="text/javascript" charset="utf-8">
        var ws = null; // websocket对象
        mui.init({
            subpages: [{
                url: "main.html",
                id: "main.html",
                styles: window.styles
            }]
        });
        mui.plusReady(function() {
            //            console.log(JSON.stringify(plus.webview.currentWebview()))
            if(plus.storage.getItem("user")) { // 判断是否登录
                console.log('已结登录了!');
                //连接websocket连接
                ws = new WebSocket("ws://" + window.ws_serv + "/app/" + plus.storage.getItem("user"))
                // 客户端接收服务端数据时触发
                ws.onmessage = function() {};
            }
            // 自动重连
            ws.onclose = function() {
                window.location.reload();
            }
        });

        // 消息
        document.getElementById("message").addEventListener("tap", function() {
            mui.openWindow({
                url: "message.html",
                id: "message.html",
                styles: window.styles,
                extras: {
                    // 传输用户id,给message.html
                    user_id: plus.storage.getItem("user")
                }
            })
        });

        document.getElementById("index").addEventListener("tap", function() {
            mui.openWindow({
                url: "main.html",
                id: "main.html",
                styles: window.styles
            })
        })

        document.getElementById("login").addEventListener("tap", function() {
            // 自动登录,判断storage中的user存在,就跳转到user_info,否则跳转login
            if(plus.storage.getItem("user")) {
                mui.openWindow({
                    url: "user_info.html",
                    id: "user_info.html",
                    styles: window.styles,
                    extras: {
                        user_id: plus.storage.getItem("user")
                    }
                })
            } else {
                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 = { //构造数据
                data: music_name,
                to_user: toy_id, // 目标用户,这里统一格式
                msg_type: "music", // 类型为音乐
            }
            // 发送数据给后端,注意要json序列化
            ws.send(JSON.stringify(send_str));
        });

        document.addEventListener("send_msg", function(data) { //发送消息
            var filename = data.detail.filename
            var to_user = data.detail.to_user
            send_str = {
                to_user: to_user
            }
            ws.send(JSON.stringify(send_str))
            plus.io.resolveLocalFileSystemURL(filename, function(entry) {
                // 可通过entry对象操作test.html文件 
                entry.file(function(file) {
                    // FileReader文件系统中的读取文件对象,用于获取文件的内容
                    var fileReader = new plus.io.FileReader();

                    //                        alert("getFile:" + JSON.stringify(file));
                    // readAsDataURL: 以URL编码格式读取文件数据内容
                    fileReader.readAsDataURL(file, 'utf-8');
                    // onloadend: 文件读取操作完成时的回调函数
                    fileReader.onloadend = function(evt) {
                        console.log(evt.target.result);
                        var b = dataURLtoBlob(evt.target.result);
                        ws.send(b); // 发送blob数据

                    }
                    //                        alert(file.size + '--' + file.name)
                });
            });

        })

        function dataURLtoBlob(dataurl) { // 数据转换为Blob
            // 逻辑很复杂,这里不解释了。直接用就可以了!
            var arr = dataurl.split(','),
                mime = arr[0].match(/:(.*?);/)[1],
                bstr = atob(arr[1]),
                n = bstr.length,
                u8arr = new Uint8Array(n);
            while(n--) {
                u8arr[n] = bstr.charCodeAt(n);
            }
            var a = new Blob([u8arr], {
                type: mime
            });
            return a
        }
    </script>

</html>
View Code

 

进入 flask项目,修改 templates-->index.html,增加ws.onclose

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

</head>
<body>
<audio src="" autoplay="autoplay" controls id="player"></audio>
<br>
<input type="text" id="device_id">
<button οnclick="start_toy()">玩具开机键</button>
<br>
<button οnclick="start_reco()">开始废话</button>
<br>
<button οnclick="stop_reco()">发送语音</button>
</body>
<script src="/static/recorder.js"></script>
<script src="/static/jquery.min.js"></script>
<script type="application/javascript">
    var serv = "http://192.168.11.40:9527";
    var ws_serv = "ws://192.168.11.40:9528";

    // 获取音频文件
    var get_music = serv + "/get_audio/";

    var ws = null;  // WebSocket 对象
    var reco = null;
    // 创建AudioContext对象
    var audio_context = new AudioContext();
    //要获取音频和视频
    navigator.getUserMedia = (navigator.getUserMedia ||
        navigator.webkitGetUserMedia ||
        navigator.mozGetUserMedia ||
        navigator.msGetUserMedia);

    // 拿到媒体对象,允许音频对象
    navigator.getUserMedia({audio: true}, create_stream, function (err) {
        console.log(err)
    });

    //创建媒体流容器
    function create_stream(user_media) {
        var stream_input = audio_context.createMediaStreamSource(user_media);
        // 给Recoder 创建一个空间,麦克风说的话,都可以录入。是一个流
        reco = new Recorder(stream_input);

    }

    function start_reco() {  //开始录音
        reco.record();  //往里面写流
    }

    function stop_reco() {  //停止录音
        reco.stop();  //停止写入流
        get_audio();  //调用自定义方法
        reco.clear();  //清空容器
    }

    function get_audio() {  // 获取音频
        reco.exportWAV(function (wav_file) {
            ws.send(wav_file);  //使用websocket连接发送数据给后端
        })
    }

    function start_toy() {  // 玩具开机
        // 获取输入的设备id
        var device_id = document.getElementById("device_id").value;
        // 发送post请求
        $.post(
            // 这里的地址必须是127.0.0.1,否则会有跨域问题
            "http://127.0.0.1:9527/device_toy_id",
            // 发送设备id
            {device_id: device_id},
            function (data) {
                console.log(data);
                toy_id = data.data.toy_id;  // 玩具id
                // 修改audio标签的src属性
                document.getElementById("player").src = get_music + data.data.audio;
                if (toy_id) {  // 判断玩具id存在时
                    ws = new WebSocket(ws_serv + "/toy/" + toy_id);
                    ws.onmessage = function (data) {
                        // console.log(get_music + data.data);
                        var content = JSON.parse(data.data);  //反序列化数据
                        // 判断消息类型
                        if (content.msg_type == "chat") {
                            document.getElementById("player").src = get_chat + content.data;
                            document.getElementById("to_user").innerText = content.from_user;
                            console.log(content.from_user + "给你发送了一条消息");
                        }
                        if (content.msg_type == "music") {
                            document.getElementById("player").src = get_music + content.data;
                            console.log(content.from_user + "给你点播了歌儿");
                        }
                    };
                    ws.onclose = function () {
                        window.location.reload();
                    }
                }
            }, "json"
            // 规定预期的服务器响应的数据类型为json
        );
    }


</script>
</html>
View Code

重启 im_serv.py,就可以了!

 

二、简单的双向聊天

由于时间关系,详细步骤略...

 

三、聊天记录存放数据库

由于时间关系,详细步骤略...

 

四、消息提醒

由于时间关系,详细步骤略...

 

五、玩具主动发起消息

由于时间关系,详细步骤略...

 

六、玩具主动发起点播

由于时间关系,详细步骤略...

 

可以点击歌曲,比如我要听 新年恰恰

 

今日总结:

0.地址统一化管理:
    serv = "domain_name"

1.多玩具端的遥控功能(必做)
    发送消息的时候,把遥控功能 开启
     send_str = {
            "code": 0,
            "from_user": uid,
            <加粗>"msg_type": "chat",</加粗>
            "data": f"{file_name}.mp3"
        }
    "msg_type": "chat" 判断是发消息 还是遥控点播
    服务端:
        1.收到一次 send 所以就要在收到字符串send 的时候判断"msg_type"=="music"
        2.other_user_socket = user_socket_dict.get(to_user)
            send_str = {
                "code": 0,
                "from_user": uid,
                <加粗>"msg_type": "music",</加粗>
                "data": msg_dict.get("data")
            }
            other_user_socket.send(json.dumps(send_str))
    玩具端:
        玩具段收到消息之后判断 msg_type 是getchat 还是getmusic
        if (content.msg_type == "chat") {
            document.getElementById("player").src = get_chat + content.data;
            console.log(content.from_user + "给你发送了一条消息");
        }

2.简单的单向聊天 升级 双向聊天
    form_user : 寄件人
    to_user : 收件人
    
    收件人和寄件人 颠倒 就是回复消息
    
    app 在 index中给 chat 页面发送 fire事件用于创建消息条目
    

3.聊天的记录 存放数据库
    chat add {sender :发件人 ,msg:语音文件名 ,updated_at : 时间}
    
    app 聊天页面:
        每一次打开聊天窗口的时候发起post请求:
        获取chat表中的聊天记录,依次生成聊天信息,打印在窗口中
            
            
4.消息提醒
    1.查询to_user用户的好友列表,当前自己在好友列表中的备注
    2.将备注合成语音
    3.将语音发送至玩具端
    4.玩具端收到消息后,按收取消息键,收取最后一条消息
    
    坑:未读,只能收取最后一条
        离线消息

5.玩具主动发起消息
    1.将语音发送至服务端
    2.将语音转换成文本
    3.把文本进行 字符串儿 匹配 “发消息” in q -》 friend_name in q -> friend
    4.模拟friend给自己发了一条假消息
        {
            from_user : friend_id
        }
    5.玩具端收到 from_user 之后,可以开启对friend的消息发送了
    
6.玩具主动发起点播:
    同 玩具主动发起消息 
View Code

 

由于时间关系,无法演示效果...

完整代码,参考github:

https://github.com/987334176/Intelligent_toy/archive/v1.4.zip

 

未完待续...

 

posted @ 2018-09-20 18:42 肖祥 阅读( ...) 评论( ...) 编辑 收藏
Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐