zoukankan      html  css  js  c++  java
  • 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 onclick="start_toy()">玩具开机键</button>
    <br>
    <button onclick="start_reco()">开始废话</button>
    <br>
    <button onclick="stop_reco()">发送语音</button>
    </body>
    <script src="/static/recorder.js"></script>
    <script src="/static/jquery.min.js"></script>
    <script type="application/javascript">
        var 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 onclick="start_toy()">玩具开机键</button>
    <br>
    <button onclick="start_reco()">开始废话</button>
    <br>
    <button onclick="stop_reco()">发送语音</button>
    </body>
    <script src="/static/recorder.js"></script>
    <script src="/static/jquery.min.js"></script>
    <script type="application/javascript">
        var 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 onclick="start_toy()">玩具开机键</button>
    <br>
    <button onclick="start_reco()">开始废话</button>
    <br>
    <button onclick="stop_reco()">发送语音</button>
    </body>
    <script src="/static/recorder.js"></script>
    <script src="/static/jquery.min.js"></script>
    <script type="application/javascript">
        var 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

    未完待续...

  • 相关阅读:
    avalon如何用年月日的方式输出..
    做一个倒计时的功能,天,时,分 /时,分,秒
    avalon用background-image不起作用,怎么来选取前几个的图片进行渲染
    获取地址栏的参数的两种方法?
    mac屏幕录制
    数据可视化
    vscode 插件
    git 命令 总结
    jest
    react admin
  • 原文地址:https://www.cnblogs.com/xiao987334176/p/9682628.html
Copyright © 2011-2022 走看看