zoukankan      html  css  js  c++  java
  • 音乐播放APP初步开发(一)websocket间的通信

    项目app页面的开发采用mui框架来实现,支持模拟器和移动端的运行,真机运行会提示安装hbuilder基座调试程序。似乎开发情况下只能再同一个网段下测试运行,跨网段就无法连接到资源数据。

    如果是将项目部署到真机上去调试可以在如下路径去找项目文件:

    内存>android>data>io.dcloud.HBuilder>apps>www

    也可以自定义部署资源的路径

    :_doc/audio/   就是指:内存>android>data>io.dcloud.HBuilder>apps>doc>audio>文件资源名

    窗口页面采用HTML5来显示。

    index.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.min.css"/>
    </head>
    <body>
        
        <!--底部选项卡-->
        <nav class="mui-bar mui-bar-tab">
            <a class="mui-tab-item mui-active">
                <span class="mui-icon mui-icon-home"></span>
                <span class="mui-tab-label">首页</span>
            </a>
            <a class="mui-tab-item">
                <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">
                <span class="mui-icon mui-icon-gear"></span>
                <span class="mui-tab-label">设置</span>
            </a>
        </nav>
        
        <script src="js/mui.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
        mui.init({
            subpages: [{
            url: "main.html",
            id: "main.html",
            styles: {
                top:'0px', //mui标题栏默认高度为45px;
                bottom:'50px' //默认为0px,可不定义;
                }
            }]
        });
            
            
        var ws=new WebSocket("ws://192.168.1.114:3721/app/app01");
        document.addEventListener("send_music",function (data) {
            var send_str=data.detail    //{to_user:"toy123",music:"sdata.album_name"}
            ws.send(JSON.stringify(send_str));
        })
    
        </script>
    </body>
    </html>
    index.html代码

    main.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.min.css"/>
        <script src="js/mui.min.js" type="text/javascript" charset="utf-8"></script>
    </head>
    <body>
        <!--标题-->
        <header class="mui-bar mui-bar-nav">
            <h1 class="mui-title">音乐</h1>
        </header>
        <!--图片轮播-->
        <div class="mui-content">
            <div id="slider" class="mui-slider" >
              <div class="mui-slider-group mui-slider-loop" style="height:300px;  auto;">
                <!-- 额外增加的一个节点(循环轮播:第一个节点是最后一张轮播) -->
                <div class="mui-slider-item mui-slider-item-duplicate">
                  <a href="#">
                    <img src="http://192.168.1.114:9527/get_lb/1.jpg">
                  </a>
                </div>
                <!-- 第一张 -->
                <div class="mui-slider-item">
                  <a href="#">
                    <img src="http://192.168.1.114:9527/get_lb/1.jpg">
                  </a>
                </div>
                <!-- 第二张 -->
                <div class="mui-slider-item">
                  <a href="#">
                    <img src="http://192.168.1.114:9527/get_lb/2.jpg">
                  </a>
                </div>
                <!-- 第三张 -->
                <div class="mui-slider-item">
                  <a href="#">
                    <img src="http://192.168.1.114:9527/get_lb/3.jpg">
                  </a>
                </div>
                <!-- 第四张 -->
                <div class="mui-slider-item">
                  <a href="#">
                    <img src="http://192.168.1.114:9527/get_lb/4.jpg">
                  </a>
                </div>
                <!-- 额外增加的一个节点(循环轮播:最后一个节点是第一张轮播) -->
                <div class="mui-slider-item mui-slider-item-duplicate">
                  <a href="#">
                    <img src="http://192.168.1.114:9527/get_lb/1.jpg">
                  </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>
        </div>
        
         <!--图文列表-->
        <ul class="mui-table-view" id="con_list">
            
        </ul>
        
        
        <script type="text/javascript">
        mui.init()
         mui.plusReady(function () {
            mui.post(window.serv+'/get_list',{
                    
                },function(data){
                    for (var i = 0; i < data.data.length; i++) {
                        creat_item(data.data[i])
                    }
                },'json'
            );
        })
         
        function creat_item (content) {
            var li=document.createElement("li")
            li.className="mui-table-view-cell mui-media";
            var a=document.createElement("a")
            a.onclick=function () {
                mui.openWindow({
                    url:"player.html",
                    id:"player.html",
                    extras:content
                })
            }
            var img=document.createElement("img")
            img.className="mui-media-object mui-pull-left";
            img.src=window.serv_img+content.music_img
            
            var div=document.createElement("div")
            div.className="mui-media-body"
            div.innerText=content.album_name
            var p=document.createElement("p")          
            p.className="mui-ellipsis"
            p.innerText=content.author_name
            
            li.appendChild(a)
            a.appendChild(img)
            a.appendChild(div)
            div.appendChild(p)
            
            document.getElementById("con_list").appendChild(li)
        }
        </script>
    </body>
    </html>
    main.html代码

    player.html歌曲的播放页面,并实现websocket连接远程控制播放曲目:

    <!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.min.css"/>
        <style type="text/css">
            @-webkit-keyframes rotation {
            from {
                -webkit-transform: rotate(0deg);
            }
            to {
                -webkit-transform: rotate(360deg);
            }
        }
        
        .an {
            -webkit-transform: rotate(360deg);
            animation: rotation 3s linear infinite;
            -moz-animation: rotation 3s linear infinite;
            -webkit-animation: rotation 3s linear infinite;
            -o-animation: rotation 3s linear infinite;
        }
        </style>
    </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">正在播放</h1>
        </header>
        <div class="mui-content" style="text-align:center; margin-top: 20px;">
            <img class="an" src="" id="cover"/ style="height:150px; 150px; border-radius:50%;">
        </div>
        
           <button type="button" id="zt" class="mui-btn mui-btn-yellow mui-btn-block">暂停</button>
        <button type="button" id="bf" class="mui-btn mui-btn-green mui-btn-block">播放</button>
        <button type="button" id="tz" class="mui-btn mui-btn-red mui-btn-block">停止</button>
        <button type="button" id="fs" class="mui-btn mui-btn-blue mui-btn-block">发送</button>
        
        
        <script src="js/mui.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
        mui.init()
        var sdata=null;
        var myplayer=null
        mui.plusReady(function () {
            sdata=plus.webview.currentWebview()
            document.getElementById("title").innerText="正在播放 - "+sdata.album_name
            document.getElementById("cover").src=window.serv_img+sdata.music_img
            myplayer=plus.audio.createPlayer(window.serv_music+sdata.play_url)
            myplayer.play()
        })
        
        document.getElementById('zt').addEventListener('tap',function () {
            myplayer.pause()    
        })
        document.getElementById('bf').addEventListener('tap',function () {
                myplayer.resume()
        })
        document.getElementById('tz').addEventListener('tap',function () {
                myplayer.stop()
        })
        document.getElementById('fs').addEventListener('tap',function () {
                var index=plus.webview.getWebviewById("HBuilder");
                mui.fire(index,'send_music',{to_user:"toy123",music:sdata.play_url});
        })
        
        </script>
    </body>
    </html>
    player.html代码

    为了避免hbuilder中的js频繁的给后端发送AJAX,更换ip,可以将ip设置为全部变量,在引用文件的js文件添加代码如下:

    window.serv="http://192.168.1.114:9527";
    window.serv_img=window.serv+"/get_photo/";
    window.serv_music=window.serv+"/get_music/";

    H5页面实现首页加载子页面main.html :

    mui.init({
            subpages: [{
            url: "main.html",
            id: "main.html",
            styles: {
                top:'0px', //mui标题栏默认高度为45px;
                bottom:'50px' //默认为0px,可不定义;为了不遮住index底部选项卡
                }
            }]
        });

    图文列表的数据展示:

    在不确定记录条数的情况下,会通过查询数据库,对数据迭代,去显示数据列表,所以在body主体里面只会有一个空列表元素:

    <!--图文列表-->
        <ul class="mui-table-view" id="con_list">
            
        </ul>

    通过JavaScript给后端发送post请求,去获取记录数,并迭代生成列表元素:

    mui.init()
         mui.plusReady(function () {
            mui.post(window.serv+'/get_list',{
                    
                },function(data){
                    for (var i = 0; i < data.data.length; i++) {
                        creat_item(data.data[i])
                    }
                },'json'
            );
        })      #window.serv指向js文件中配置好的ip地址

    返回的数据以字典形式推送给前端JavaScript ,迭代数据传递给creat_item去生成列表元素。

    function creat_item (content) {
            var li=document.createElement("li")
            li.className="mui-table-view-cell mui-media";
            var a=document.createElement("a")
            a.onclick=function () {
                mui.openWindow({
                    url:"player.html",
                    id:"player.html",
                    extras:content
                })
            }
            var img=document.createElement("img")
            img.className="mui-media-object mui-pull-left";
            img.src=window.serv_img+content.music_img        #去后台请求对应图片信息数据
            
            var div=document.createElement("div")
            div.className="mui-media-body"
            div.innerText=content.album_name
            var p=document.createElement("p")          
            p.className="mui-ellipsis"
            p.innerText=content.author_name
            
            li.appendChild(a)
            a.appendChild(img)
            a.appendChild(div)
            div.appendChild(p)
            
            document.getElementById("con_list").appendChild(li)
        }

    后端会有路由去处理这个请求:

    在实际项目中,为了便于扩展,通常会采用蓝图注册到app的方式去处理请求。处理请求文件放到serv目录下。

    获取数据列表:

    from flask import Blueprint,jsonify
    from setting import mongoDB,RET
    
    content=Blueprint("content",__name__)
    
    @content.route('/get_list',methods=["POST",])
    def get_list():
        music=list(mongoDB.music.find({}))
        for index,item in enumerate(music):
            music[index]["_id"]=str(item.get("_id"))
        RET["code"]=0
        RET["msg"]="查询列表数据"
        RET["data"]=music
        return jsonify(RET)

    列表数据显示完成,点击列表,实现跳转播放页面player.html :

    a.onclick=function () {
                mui.openWindow({
                    url:"player.html",
                    id:"player.html",
                    extras:content
                })
            }

    将字典数据传递给新页面player.html,在新页面获取传递过来的数据字典:

    并获取播放曲目图片和歌曲名,创建一个播放对象来播放歌曲

     mui.init()
        var sdata=null;
        var myplayer=null
        mui.plusReady(function () {
            sdata=plus.webview.currentWebview()
            document.getElementById("title").innerText="正在播放 - "+sdata.album_name
            document.getElementById("cover").src=window.serv_img+sdata.music_img
            myplayer=plus.audio.createPlayer(window.serv_music+sdata.play_url)        #去后台请求音乐数据
            myplayer.play()
        })

    至此已经能完成图文列表正常显示数据,点击列表,打开播放页面,播放歌曲。效果如图所示:

    关于图片的旋转可以通过设置img的样式来实现:

    <div class="mui-content" style="text-align:center; margin-top: 20px;">
            <img class="an" src="" id="cover"/ style="height:150px; 150px; border-radius:50%;">
        </div>

    style样式设置:

    <style type="text/css">
            @-webkit-keyframes rotation {
            from {
                -webkit-transform: rotate(0deg);
            }
            to {
                -webkit-transform: rotate(360deg);
            }
        }
        
        .an {
            -webkit-transform: rotate(360deg);
            animation: rotation 3s linear infinite;
            -moz-animation: rotation 3s linear infinite;
            -webkit-animation: rotation 3s linear infinite;
            -o-animation: rotation 3s linear infinite;
        }
        </style>

     进一步实现通过websocket远程通信,实现指定客户端播放app中指定的音乐:

    即:点击app中的发送按钮,远程的客户端(玩具)会切换到当前播放歌曲。来播放新的音乐

    监听“发送按钮”事件,将数据传递至index页面,并创建websocket对象,通过websocket对象将数据发送给后端:

    <button type="button" id="fs" class="mui-btn mui-btn-blue mui-btn-block">发送</button>

    在JavaScript中监听:

    document.getElementById('fs').addEventListener('tap',function () {
                var index=plus.webview.getWebviewById("HBuilder");
                mui.fire(index,'send_music',{to_user:"toy123",music:sdata.play_url});
        })

    在index中去监听send_music事件:并获取携带的数据

    var ws=new WebSocket("ws://192.168.1.114:3721/app/app01");
        document.addEventListener("send_music",function (data) {
            var send_str=data.detail    //{to_user:"toy123",music:"sdata.album_name"}
            ws.send(JSON.stringify(send_str));
        })

    运行程序就创建app的websocket对象,此时,后端会通过路由去获取app_id和app的websocket对象,保存在空字典里,准备后续的使用。

    后端websocket的代码实现:

    from flask import Flask,request
    from geventwebsocket.handler import WebSocketHandler
    from gevent.pywsgi import WSGIServer
    from geventwebsocket.websocket import WebSocket
    import json
    
    ws_app=Flask(__name__)
    
    user_socket_dict={}
    
    @ws_app.route("/app/<app_id>")
    def app(app_id):
        user_socket= request.environ.get("wsgi.websocket")  #type:WebSocket
        if user_socket:
            user_socket_dict[app_id]=user_socket  # {'app01': <geventwebsocket.websocket.WebSocket object at 0x0000022989215250>}
        print(user_socket_dict)
    
        while 1:
            user_msg=user_socket.receive()      # {"to_user":"toy123","music":"8155d294-0552-460d-b5f5-57afddbfdfd4.mp3"} <class 'str'>
    
            msg_dict=json.loads(user_msg)
    
            toy_socket=user_socket_dict.get(msg_dict.get("to_user"))    #获取当前客户端的websocket对象
            toy_socket.send(user_msg)                      #给对象发送之前的数据,必须是str类型 否则报错 
    
    @ws_app.route("/toy/<toy_id>")
    def toy(toy_id):
        user_socket=request.environ.get("wsgi.websocket")   # type: WebSocket
        if user_socket:
    
            user_socket_dict[toy_id]=user_socket  # 'toy123': <geventwebsocket.websocket.WebSocket object at 0x00000229892152B8>
        print(user_socket_dict)
    
        while 1:
            user_msg=user_socket.receive()
            print(user_msg)
    
    if __name__ == '__main__':
        http_serv=WSGIServer(("0.0.0.0",3721),ws_app,handler_class=WebSocketHandler)
        http_serv.serve_forever()

    app的websocket逻辑代码写完后,还需要创建一个客户端toy的websocket对象,同样会存储到字典里去

    toy页面代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>玩具</title>
    </head>
    <body>
    <audio autoplay="autoplay" controls id="player"></audio>
    
    <script type="application/javascript">
        var addr="192.168.1.114";
        var ws=new WebSocket("ws://"+addr+":3721/toy/toy123");
        ws.onmessage=function (data) {
            console.log(data.data);
            var msg=JSON.parse(data.data);
            document.getElementById("player").src="http://"+addr+":9527/get_music/"+msg.music;
    
        }
    </script>
    </body>
    </html>

    添加访问toy客户端的路由地址到player.py flask框架

    @app.route("/")
    def toy():
        return render_template("toy.html")

    基本完成该阶段的逻辑代码,项目运行流程:

    1.启动player.py 运行flask框架,启动服务器。

    2.运行脚本文件play_ws.py 启动websocket服务

    3.访问toy客户端页面,将toy的toy_id和websocket对象传递给后端

    4.启用app项目,将app_id和app的websocket对象传递给后端。

    5.后端的websocket中会将数据保存在字典中

    {'toy123': <geventwebsocket.websocket.WebSocket object at 0x00000293E4655250>, 'app01': <geventwebsocket.websocket.WebSocket object at 0x00000293E46552B8>}

    6.点开图文列表,音乐开始播放,点击发送,会将当前音乐的歌名和toy_id以字典方式发送给index.html页面

    7.index.html页面获取数据,并将字典对象转化为str,发送给后端app路由去处理

    8.后端收到数据,反序列化,得到内容,按照收件人的id去字典中获取到这个websocket对象,给这个对象发送收到的数据 未反序列化的str字典

    9.toy获取数据字典中music的值,并读取到audio中播放该曲目

    toy收到app通过websocket发过来的代码:

  • 相关阅读:
    122. 买卖股票的最佳时机 II-leetcode
    SQL优化
    《C++ Primer Plus》读书笔记之十二—C++中的代码重用
    《C++ Primer Plus》读书笔记之十一—类继承
    《C++ Primer Plus》读书笔记之十—类和动态内存分配
    《C++ Primer Plus》读书笔记之九—使用类
    《C++ Primer Plus》读书笔记之八—对象和类
    一道算法题-换钱
    《C++ Primer Plus》读书笔记之七—内存模型和名称空间
    《C++ Primer Plus》读书笔记之六—函数探幽
  • 原文地址:https://www.cnblogs.com/wen-kang/p/10778812.html
Copyright © 2011-2022 走看看