Flask与Html5+的项目:音乐播放器

发布时间 2023-07-08 10:36:20作者: f_carey

Flask与Html5+的项目:音乐播放器

1 需求

  1. App:
    1. 支持移动设备:Android / iOS
    2. Mui + Html5+
  2. 通讯协议:
    1. WebSocket
  3. HTTP FlaskWeb框架逻辑:
    1. 轻量化
    2. 快速实现 HTTP rest-API 接口
    3. Flask
  4. MongoDB数据存储:
    1. JSON存储,便于后期数据分析、用户画像
    2. 数据格式灵活
    3. 存储读取速度很快
  5. 业务需求:
    1. 用户:
      1. 登录注册
      2. 绑定APP
    2. 内容展示
      1. 文字
      2. 图片
      3. 音频
    3. 仿微信通讯
    4. 用户控制APP播放内容:基于websocket 实现内容推送
  6. FlaskWeb框架 后台:
    1. 用户相关功能
      • 登录
      • 注册
      • 头像
      • 控制App通讯录
    2. 文件功能相关
      • 文件内容传输
      • 文件上传
    3. 通讯服务:
      • 幼教内容推送至App
      • 可以和App进行通讯
      • 玩具和App进行通讯

2 实现

2.1 内容采集

import math
import os.path
import time

import requests
from pro01 import setting
from uuid import uuid4

# 1. 根据专辑ID访问专辑页面,https://www.example.com/revision/album/v1/getTracksList?albumId=4436043&pageNum=2&sort=0&pageSize=30
# 2. 得到列表中对应的ID名称:content.get('data').get('tracks')[0].get('trackId')
# 3. 构建访问歌曲url:https://www.example.com/revision/play/v1/audio?id=31719610&ptype=1

wanna_get_dict = {
    "儿歌": ["20880781", "4436043"],
    "故事": ["6233693", "260744"],
    "古诗": ["11106118", "52208331"]
}


def get_content(content_dict: dict):
    for tag, album_list in content_dict.items():
        for album in album_list:
            # 统计当前专集共有多少页
            page = 1
            content = requests.get(url=setting.CONTENT_LIST_URL % (album, page), headers=setting.HEADERS).json()
            total_page = math.ceil(int(content.get('data').get('trackTotalCount')) / 30)
            print(total_page)
            while page <= total_page:
                if page != 1:
                    content = requests.get(url=setting.CONTENT_LIST_URL % (album, page), headers=setting.HEADERS).json()
                content_list = content.get('data').get('tracks')
                for sound in content_list:
                    music_name = sound.get('title')
                    music_file = uuid4()
                    music_path = os.path.join(setting.MUSIC_PATH, f'{music_file}.mp3')
                    cover_path = os.path.join(setting.COVER_PATH, f'{music_file}.jpg')
                    music_author = sound.get('anchorName')
                    music_create_time = sound.get('createDateFormat')
                    music_tag = sound.get('tag')
                    music_id = sound.get('trackId')

                    cover_url = setting.IMG_HOST + sound.get('albumCoverPath')
                    cover = requests.get(url=cover_url, headers=setting.HEADERS).content
                    with open(cover_path, 'wb') as f:
                        f.write(cover)

                    music_content = requests.get(url=setting.SOUND_URL % music_id, headers=setting.HEADERS).json()
                    music_url = music_content.get('data').get('src')
                    music = requests.get(url=music_url, headers=setting.HEADERS).content
                    with open(music_path, 'wb') as f:
                        f.write(music)
                    music_info = {
                        'title': music_name,
                        'music_file': f'{music_file}.mp3',
                        'cover': f'{music_file}.jpg',
                        'author': music_author,
                        'create_time': music_create_time,
                        'tag': music_tag,
                    }
                    setting.MongoDB.music_info.insert_one(music_info)
                    print(music_info)
                    time.sleep(5)
                    page += 1


get_content(content_dict=wanna_get_dict)

2.2 创建App项目

  1. MUI、HTML5+
  2. 使用Get方式获取内容列表

2.2.1 前端

2.2.1.1 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.css"/>
</head>
<body>
	<header class="mui-bar mui-bar-nav">
		<h1 class="mui-title">首页</h1>
	</header>
	<div class="mui-content">
		it's 首页.
	</div>
	<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='login'>
			<span class="mui-icon mui-icon-phone"></span>
			<span class="mui-tab-label">登录</span>
		</a>
		<a class="mui-tab-item" id="player">
			<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.js" type="text/javascript" charset="utf-8"></script>
	<script type="text/javascript">
	mui.init()
	document.getElementById('index').addEventListener('tap',function () {
	        mui.openWindow('index.html','index.html',{})
	});
	document.getElementById('login').addEventListener('tap',function () {
	        mui.openWindow({
				url: 'login.html', 
				id:'login.html',
				styles:{
					   top:'0px',//新页面顶部位置
					   bottom:'50px',//新页面底部位置
					 },
				createnew:true,
	        });
	});
	document.getElementById('player').addEventListener('tap',function () {
	        mui.openWindow({
	        	url: 'playerlist.html', 
	        	id:'playerlist.html',
	        	styles:{
	        		   top:'0px',//新页面顶部位置
	        		   bottom:'50px',//新页面底部位置
	        		 },
	        });
	});
	</script>
</body>
</html>
2.2.1.2 playlist.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 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">儿歌</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">故事</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">诗词</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='data_list'>
			<li class="mui-table-view-cell mui-media">
				<a href="javascript:;">
					<img class="mui-media-object mui-pull-left" src="">
					<div class="mui-media-body">
						幸福
						<p class="mui-ellipsis">能和心爱的人一起睡觉,是件幸福的事情;可是,打呼噜怎么办?</p>
					</div>
				</a>
			</li>
		</ul>
	</div>
	<script src="js/mui.js" type="text/javascript" charset="utf-8"></script>
	<script type="text/javascript">
	mui.init()
	mui.ready(function () {
		mui.get(window.serv + '/get_list',{
			},function(data){
				for (var i=0; i<data.data.length; i++) {
					get_data(data.data[i])
				}
			},'json'
		);
		function get_data(data) {
			var li = document.createElement('li');
			li.className = "mui-table-view-cell mui-media";
			var a = document.createElement('a');
			a.addEventListener('tap',function() {
				mui.openWindow(
				'player.html',
				'player.html',
				{
					styles:{
					top:'0px',//子页面顶部位置
					bottom:'50px',//子页面底部位置
					},
				  extras:data,
				  createnew:true,
				  })
			});
			var img = document.createElement('img');
			img.className = "mui-media-object mui-pull-left"
			img.src = window.serv_cover + '/' + data.music_file.split('.')[0] + '.jpg';
			var div = document.createElement('div');
			div.className = "mui-media-body"
			div.innerText = data.title;
			var p = document.createElement('p');
			p.className = "mui-ellipsis"
			p.innerText = data.title;
			
			// 创建Dom层级结构
			li.appendChild(a);
			a.appendChild(img);
			a.appendChild(div);
			div.appendChild(p);
			
			// 添加data_list中的Dom对象
			document.getElementById('data_list').appendChild(li)
		};
		});
	</script>
</body>
</html>
2.2.1.3 player.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" id="title"></h1>
	</header>
	<div class="mui-content">
		<div class="mui-row" style="text-align: center; margin-top: 50px;">
			<img id="cover" style="width: 250px; height: 250px; border-radius: 50%;" />
		</div>
		<div class="mui-row" style="text-align: center; margin-top: 50px;">
			<audio autoplay controls id="player"></audio>
		</div>
		<div class="mui-row" style="text-align: center; margin-top: 15px;">
			<button type="button" class="mui-btn mui-btn-blue" id="play">播放</button>
			<button type="button" class="mui-btn mui-btn-red" id="pause">暂停</button>
			<button type="button" class="mui-btn mui-btn-blue" id='resume'>继续</button>
		</div>
		<div class="mui-row" style="text-align: center;margin-top: 25px;" >
				<button type="button" class="mui-btn mui-btn-purple mui-btn-block" id="send_music">发送歌曲</button>
				</div>
	</div>
	<script src="js/mui.js" type="text/javascript" charset="utf-8"></script>
	<script type="text/javascript">
	mui.init()
	var play = null;
	mui.plusReady(function () {
	    var player_wv = plus.webview.currentWebview();
		console.log(JSON.stringify(player_wv))
		document.getElementById('title').innerText = player_wv.title + '--正常播放';
	    document.getElementById('cover').src = window.serv_cover + '/'+player_wv.music_file.split('.')[0]+'.jpg';
	    palyer = document.getElementById("player");
	    player.src = window.serv_music + '/' + player_wv.music_file;
	})

	document.getElementById('play').addEventListener('tap',function () {
	        player.play();
	});
	document.getElementById('pause').addEventListener('tap',function () {
	        player.pause();
	});
	document.getElementById('resume').addEventListener('tap',function () {
	        player.resume();
		});
	</script>
</body>
</html>

2.2.2 后端

2.2.2.1 setting.py
HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"
}

CONTENT_LIST_URL = "https://www.example.com/revision/album/v1/getTracksList?albumId=%s&pageNum=%s&sort=0&pageSize=30"
SOUND_URL = 'https://www.example.com/revision/play/v1/audio?id=%s&ptype=1'
IMG_HOST = 'http://imagev2.example.com/'
from pymongo import MongoClient

MC = MongoClient(host='127.0.0.1', port=27017)
MongoDB = MC['erge']

import os

COVER_PATH = os.path.join('data', 'cover')
MUSIC_PATH = os.path.join('data', 'music')

RET = {
    "code": 0,
    "msg": "",
    "data": {}
}

2.2.2.2 bp/erge_display.py
from flask import Flask
from bp.users import users_bp
from bp.erge_display import display_bp

app = Flask(__name__)
app.config['DEBUG'] = True
app.register_blueprint(users_bp)
app.register_blueprint(display_bp)

if __name__ == '__main__':
    app.run('0.0.0.0', 80)

2.2.2.3 bp/users.py
from flask import Blueprint, request, jsonify, render_template
from pro01.setting import MongoDB, RET

users_bp = Blueprint('users_bp', __name__)


@users_bp.route('/signin', methods=['POST'])
def register():
    # 获取FormData中的数据
    user_info = request.form.to_dict()
    # 将数据转换成字典 直接存放在MongoDB中
    res = MongoDB.users.insert_one(user_info)
    if res.inserted_id:
        RET["code"] = 0
        RET["msg"] = "注册成功"
        RET["data"] = {}
        return jsonify(RET)
    else:
        RET["code"] = 1
        RET["msg"] = "注册失败"
        RET["data"] = {}
        return jsonify(RET)


@users_bp.route("/login", methods=["POST"])
def login():
    login_info = request.form.to_dict()
    user_info = MongoDB.users.find_one(login_info)
    print(login_info)
    if not user_info:
        # 登录失败
        RET["code"] = 1
        RET["msg"] = "用户名密码错误"
        RET["data"] = {}

        return jsonify(RET)
    else:
        user_info["_id"] = str(user_info.get("_id"))
        user_info.pop("password")  # 删除敏感字段
        RET["code"] = 0
        RET["msg"] = "登录成功"
        RET["data"] = user_info

        return jsonify(RET)

2.2.2.4 app.py
from flask import Flask
from bp.users import users_bp
from bp.erge_display import display_bp

app = Flask(__name__)
app.config['DEBUG'] = True
app.register_blueprint(users_bp)
app.register_blueprint(display_bp)

if __name__ == '__main__':
    app.run('0.0.0.0', 80)