# node_music **Repository Path**: xjiabin/node_music ## Basic Information - **Project Name**: node_music - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2019-05-21 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # node_music ### 项目功能 1. 登录 2. 注册 3. 添加音乐 4. 播放音乐 5. 编辑音乐 6. 删除音乐 > 接口文档 | 同异步 | 名称 | 请求方式 | URL | 参数1 | 参数2 | 参数3 | 参数4 | 参数5 | 返回类型 | | | ------ | ------------------ | -------- | -------------------- | --------------------------------------- | -------------------------- | :----------------------------: | -------------------------- | ----------- | ----------------------------------------- | ------------------------------ | | 同步 | 注册页面 | GET | /user/register | 无 | | | | | 页面 | | | 同步 | 登录页面 | GET | /user/login | 无 | | | | | 页面 | | | 同步 | 音乐列表 | GET | /music/index | 无 | | | | | 页面 | | | 同步 | 添加音乐 | GET | /music/add-music | 无 | | | | | 页面 | | | 同步 | 编辑音乐 | GET | /music/edit-music | 无 | | | | | 页面 | | | 异步 | 验证用户名是否存在 | POST | /user/check-username | username,必填,string,用户名 | | | | | JSON对象{code:'001',msg:'可以注册'} | 001:成功,002失败,msg描述信息 | | 异步 | 注册 | POST | /user/do-register | username,必填,string,用户名 | password,必填, string,密码 | email,必填,string,邮箱 | v_code,必填, string,验证码 | | JSON对象{code:'002',msg:'用户名已经存在'} | 001:成功,002失败,msg描述信息 | | 异步 | 登录 | POST | /user/do-login | username,必填,string,用户名 | password,必填,string,密码 | | | | JSON对象 | 001:成功,002失败,msg描述信息 | | 异步 | 退出 | GET | /user/logout | 无 | | | | | | | | 异步 | 添加音乐 | POST | /music/add-music | title标题 | singer歌手 | time时长 | file歌曲 | filelrc歌词 | JSON对象 | 001:成功,002失败,msg描述信息 | | 异步 | 更新音乐 | PUT | /api/update-music | id/title/singer/time/file+filelrc(选填) | JSON对象 | 001:成功,002失败,msg描述信息 | | | | | | 异步 | 删除音乐 | DELETE | /api/del-music | id音乐ID必填 | 查询字符串 | | | | | | | 异步 | 获取验证码图片 | GET | /user/get-pic | | | | | | | | ### 滚动歌词 #### 1. 获取歌词 根据歌曲 id 获取歌词: ```h [00:00.98]告白气球 [00:04.19]词:方文山 [00:06.29]曲:周杰伦 [00:07.78]演唱:周杰伦 [00:17.78] [00:23.65]塞纳河畔 左岸的咖啡 [00:26.45]我手一杯 品尝你的美 ... [03:13.16]亲爱的别任性 你的眼睛 [03:20.01] [03:21.37]在说我愿意 ``` #### 2. 解析歌词 将歌词解析为 `{ "时间(秒数)": "歌词" }` 的格式: ```js { "0": "告白气球", // => [00:00.98]告白气球 "4": "词:方文山", // => [00:04.19]词:方文山 "6": "曲:周杰伦", // => [00:06.29]曲:周杰伦 // ... } ``` **解析歌词** ```js function parseLrc(lrc) { var tempObj = {}; // [00:00.98]告白气球 // 1. 以换行符切割字符串 var lines = lrc.split('\n'); // [00:00.98]告白气球 => `/\[(\d{2})\:(\d{2})\.(\d{2})\](.*)/` var regex = /\[(\d{2})\:(\d{2})\.(\d{2})\](.*)/; // 2. 遍历该数组,以正则来匹配数据,获取时间和歌词 for(var i = 0; i < lines.length; i++) { var line = lines[i]; var result = regex.exec(line); if (!result) continue; // 不匹配的跳过 var minute = result[1]; // 分 var second = result[2]; // 秒 // var millisecond = result[3]; // 毫秒 var content = result[4]; // 歌词 // 3. 计算时间作为 key,将其与歌词为对象添加属性 var key = (minute * 60) + (second - 0); tempObj[key] = content; } // 4. 返回这个对象 return tempObj; } ``` #### 3. 生成 dom 结构 根据解析完的歌词格式生成 dom 结构 歌词容器 ```html
``` 将歌词放在 p 标签中, 添加一个自定义属性 time; 以 时间(key) 为值, 以歌词(value) 为innerText => `

歌词

` ```html

告白气球

词:方文山

曲:周杰伦

``` 将生成的 p 标签加入 `div#lrc` 容器中 #### 4. 滚动 滚动是根据歌曲播放进度进行的,如果歌曲暂停了,歌词就不滚动; 播放时间为 0 秒,则滚动到 `time=0` 的位置 播放时间为 4 秒,则滚动到 `time=4` 的位置 播放时间为 6 秒,则滚动到 `time=6` 的位置 .... 滚动的距离 = ( `div#lrc` 的 top 值) - ( `p` 的 top 值) 如: ```html

告白气球

词:方文山

曲:周杰伦

``` 滚动距离 = (`id=lrc` 的top值为 20) - (`time=0` 的top值为 50) 随着歌曲播放,歌词的 key(秒数) 就越大,p 标签的位置就越来越下,top 值就越来越大,可以让 lrc 盒子向上移动的距离就越来越多 #### jQuery 上传文件 ```js $('form').on('submit', function(e) { e.preventDefault(); // 获取所有的 input var inputs = document.querySelectorAll('input'); var formData = new FormData(); formData.append('title', inputs[0].value); formData.append('singer', inputs[1].value); formData.append('time', inputs[2].value); // 传递文件的方式: 文件对象 坑:原生 DOM 的属性, jQ上没有该属性 formData.append('file', inputs[3].files[0]); formData.append('filelrc', inputs[4].files[0]); $.ajax({ url: '/music/add-music', type: 'post', data: formData, dataType: 'json', // jq 上传文件的坑: // 1. 默认请求头是 application/x-www-form-urlencoded (键值对) // 2. 默认会将数据序列化,转化为 key=value // 解决: contentType: false, // 1. processData: false, // 2. success: function(data) { if (data.code === '001') { if (confirm('添加成功,去听歌?')) { window.location = '/music/index'; } } else { alert(data.msg); } } }) }) ``` ### 部署 ------ - NodeJs是基于V8解析引擎并执行的,所以不同于其他后端语言,需要先编译后才能在服务器运行 - NodeJs自身又包含服务器,所以当启动了NodeJs的程序,就会在使用当前机器的公网IP并监听端口运行服务器 #### PM2 - forever(没有pm2好用) - 保障服务器不会挂掉 - 1. ```npm i -g pm2``` 2. ``` pm2 start ./xxx.js --name projectname``` - 其他命令 - ```js pm2 examples // 查看常用示例 pm2 [start||restart||stop||delete] all||name||id // 重启 pm2 show name||id // 查看信息 pm2 list // 查看项目 pm2 flush // 清空日志 pm2 log name||id // 查看日志 ``` #### nginx负载均衡 - 配置1: - ```python upstream localhost { ip_hash; # nginx内置的处理 server localhost:8000 weight=1; server localhost:8001 weight=1; } location / { proxy_pass http://localhost } ``` #### 查看环境变量 - art-template中有NODE_ENV==="production" 环境变量的配置 - __linux:__ /etc/profile文件中 export 环境变量=值 - 执行坏境变量更新命令 ```source /etc/profile``` #### session共享 - ip_hash 是解决通过IP地址来分配不同服务器的,对不同服务器以绑定的方式分解压力。 - 同时又能实现登录后,还访问其他服务器导致登录状态不一的解决方案 - 但是其并不是真正的 session 共享,真正的共享还是在数据库存储