├── .babelrc ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── README.md ├── api ├── .gitignore ├── .npmignore ├── app.js ├── package.json ├── public │ └── index.html ├── router │ ├── album.js │ ├── artist_album.js │ ├── artists.js │ ├── artists_desc.js │ ├── artists_mv.js │ ├── banner.js │ ├── check_music.js │ ├── comment_album.js │ ├── comment_dj.js │ ├── comment_like.js │ ├── comment_music.js │ ├── comment_mv.js │ ├── comment_playlist.js │ ├── daily_signin.js │ ├── dj_catelist.js │ ├── dj_detail.js │ ├── dj_hot.js │ ├── dj_program.js │ ├── dj_program_detail.js │ ├── dj_recommend.js │ ├── dj_recommend_type.js │ ├── dj_sub.js │ ├── event.js │ ├── fm_trash.js │ ├── follow.js │ ├── like.js │ ├── likelist.js │ ├── logWeb.js │ ├── login.js │ ├── loginCellphone.js │ ├── login_refresh.js │ ├── lyric.js │ ├── musicUrl.js │ ├── mv.js │ ├── mv_first.js │ ├── mv_url.js │ ├── personal_fm.js │ ├── personalized.js │ ├── personalized_djprogram.js │ ├── personalized_mv.js │ ├── personalized_newsong.js │ ├── personalized_privatecontent.js │ ├── playlist_catlist.js │ ├── playlist_detail.js │ ├── playlist_hot.js │ ├── playlist_tracks.js │ ├── program_recommend.js │ ├── recommend_dislike.js │ ├── recommend_resource.js │ ├── recommend_songs.js │ ├── resource_like.js │ ├── search.js │ ├── search_hot.js │ ├── search_multimatch.js │ ├── search_suggest.js │ ├── simi_artists.js │ ├── simi_mv.js │ ├── simi_playlist.js │ ├── simi_song.js │ ├── simi_user.js │ ├── song_detail.js │ ├── top_album.js │ ├── top_artists.js │ ├── top_list.js │ ├── top_mv.js │ ├── top_playlist.js │ ├── top_playlist_highquality.js │ ├── top_songs.js │ ├── toplist.js │ ├── toplist_artist.js │ ├── toplist_detail.js │ ├── user_audio.js │ ├── user_cloud.js │ ├── user_cloud_search.js │ ├── user_detail.js │ ├── user_dj.js │ ├── user_event.js │ ├── user_followeds.js │ ├── user_follows.js │ ├── user_playlist.js │ ├── user_playrecord.js │ └── user_subcount.js ├── test │ ├── album.test.js │ ├── comment.test.js │ ├── login.test.js │ ├── lyric.test.js │ ├── musicUrl.test.js │ └── search.test.js └── util │ ├── crypto.js │ └── util.js ├── build └── icons │ ├── 256x256.png │ ├── icon.icns │ └── icon.ico ├── config ├── build.config.js ├── build.js ├── dev-client.js ├── dev-runner.js ├── webpack.main.config.js ├── webpack.renderer.config.js └── webpack.web.config.js ├── doc ├── 1588237435-599597952042d_articlex.jpeg ├── 3872766167-5999ac798b713_articlex.png └── 3913213729-5999ac98a8c6c_articlex.jpeg ├── package.json ├── src ├── index.ejs ├── main │ ├── command.js │ ├── index.dev.js │ └── index.js └── renderer │ ├── App.vue │ ├── api │ ├── api.js │ └── config.js │ ├── assets │ ├── scss │ │ ├── common.scss │ │ ├── core │ │ │ ├── _css3.scss │ │ │ ├── _grid.scss │ │ │ ├── _mixin.scss │ │ │ └── _setting.scss │ │ ├── forms.scss │ │ ├── function.scss │ │ └── var.scss │ └── style │ │ ├── app.scss │ │ ├── forms.scss │ │ ├── grid.scss │ │ ├── instructViews.scss │ │ └── table.scss │ ├── components │ ├── common │ │ ├── 404 │ │ │ ├── 404.scss │ │ │ ├── 404.vue │ │ │ └── assets │ │ │ │ └── 404.svg │ │ ├── banner │ │ │ ├── banner.scss │ │ │ ├── banner.vue │ │ │ └── index.js │ │ ├── barButton │ │ │ ├── barButton.scss │ │ │ └── barButton.vue │ │ ├── dropdown │ │ │ ├── dropdown.scss │ │ │ └── dropdown.vue │ │ ├── header │ │ │ ├── assets │ │ │ │ ├── dropdown-a.svg │ │ │ │ ├── dropdown.svg │ │ │ │ ├── i-left.svg │ │ │ │ ├── i-right.svg │ │ │ │ ├── icon-left.svg │ │ │ │ ├── icon-right.svg │ │ │ │ ├── logo.svg │ │ │ │ └── so.svg │ │ │ ├── header.scss │ │ │ └── header.vue │ │ ├── loading │ │ │ ├── index.js │ │ │ └── loading.vue │ │ ├── pagination │ │ │ ├── assets │ │ │ │ ├── i-left.svg │ │ │ │ └── i-right.svg │ │ │ ├── index.js │ │ │ ├── pagination.scss │ │ │ └── pagination.vue │ │ ├── playDetail │ │ │ ├── api.js │ │ │ ├── assets │ │ │ │ ├── close_1.svg │ │ │ │ ├── close_2.svg │ │ │ │ ├── close_3.svg │ │ │ │ ├── collect.svg │ │ │ │ ├── download.svg │ │ │ │ ├── like.svg │ │ │ │ ├── like_1.svg │ │ │ │ ├── like_2.svg │ │ │ │ ├── share_1.svg │ │ │ │ └── share_2.svg │ │ │ ├── playDetail.scss │ │ │ └── playDetail.vue │ │ ├── playList │ │ │ ├── assets │ │ │ │ ├── 1clone.svg │ │ │ │ └── clone.svg │ │ │ ├── playList.scss │ │ │ └── playList.vue │ │ ├── playerBar │ │ │ ├── assets │ │ │ │ ├── for.svg │ │ │ │ ├── list.svg │ │ │ │ ├── lyric.svg │ │ │ │ ├── next.svg │ │ │ │ ├── pause.svg │ │ │ │ ├── play.svg │ │ │ │ ├── prev.svg │ │ │ │ ├── voice-close.svg │ │ │ │ └── voice-open.svg │ │ │ ├── playerBar.scss │ │ │ └── playerBar.vue │ │ ├── sideBar │ │ │ ├── api.js │ │ │ ├── assets │ │ │ │ ├── download.svg │ │ │ │ ├── fm.svg │ │ │ │ ├── friend.svg │ │ │ │ ├── iTunes.svg │ │ │ │ ├── likes.svg │ │ │ │ ├── list.svg │ │ │ │ ├── music.svg │ │ │ │ ├── mv.svg │ │ │ │ └── shows.svg │ │ │ ├── sideBar.scss │ │ │ └── sideBar.vue │ │ ├── slider │ │ │ ├── slider.scss │ │ │ └── slider.vue │ │ ├── toast │ │ │ ├── index.js │ │ │ ├── toast.scss │ │ │ └── toast.vue │ │ └── windowTop │ │ │ └── windowTop.vue │ ├── instructViews.vue │ ├── views │ │ ├── details │ │ │ ├── api.js │ │ │ └── details.vue │ │ ├── download │ │ │ └── download.vue │ │ ├── fm │ │ │ └── fm.vue │ │ ├── friend │ │ │ └── friend.vue │ │ ├── home │ │ │ ├── api.js │ │ │ ├── assets │ │ │ │ └── emoji-20.svg │ │ │ ├── home.scss │ │ │ └── home.vue │ │ ├── iTunes │ │ │ ├── assets │ │ │ │ └── itunes.svg │ │ │ ├── iTunes.scss │ │ │ └── iTunes.vue │ │ ├── login │ │ │ ├── api.js │ │ │ ├── assets │ │ │ │ ├── bg.png │ │ │ │ ├── clone.svg │ │ │ │ ├── mobile.svg │ │ │ │ ├── password.svg │ │ │ │ ├── phone.svg │ │ │ │ └── yes.svg │ │ │ ├── login.scss │ │ │ └── login.vue │ │ ├── mv │ │ │ └── mv.vue │ │ └── search │ │ │ ├── api.js │ │ │ ├── component │ │ │ ├── album │ │ │ │ ├── album.scss │ │ │ │ ├── album.vue │ │ │ │ └── assets │ │ │ │ │ └── default.svg │ │ │ ├── lyric │ │ │ │ └── lyric.vue │ │ │ ├── mv │ │ │ │ ├── assets │ │ │ │ │ └── default.svg │ │ │ │ ├── mv.scss │ │ │ │ └── mv.vue │ │ │ ├── radio │ │ │ │ └── radio.vue │ │ │ ├── singer │ │ │ │ ├── assets │ │ │ │ │ └── default.svg │ │ │ │ ├── singer.scss │ │ │ │ └── singer.vue │ │ │ ├── single │ │ │ │ ├── assets │ │ │ │ │ ├── collect.svg │ │ │ │ │ ├── download.svg │ │ │ │ │ └── right.svg │ │ │ │ ├── single.scss │ │ │ │ └── single.vue │ │ │ ├── song │ │ │ │ ├── assets │ │ │ │ │ └── default.svg │ │ │ │ ├── song.scss │ │ │ │ └── song.vue │ │ │ └── user │ │ │ │ └── user.vue │ │ │ ├── search.scss │ │ │ └── search.vue │ └── windowViews.vue │ ├── main.js │ ├── router │ └── index.js │ ├── store │ ├── index.js │ ├── modules │ │ ├── index.js │ │ ├── play.js │ │ ├── routes.js │ │ ├── search.js │ │ ├── toggle.js │ │ └── user.js │ └── mutation-types.js │ └── utils │ ├── background.js │ ├── ipcRenderer.js │ ├── libs.js │ └── storage.js ├── static └── .gitkeep └── test ├── .eslintrc ├── e2e ├── index.js ├── specs │ └── Launch.spec.js └── utils.js └── unit ├── index.js ├── karma.conf.js └── specs └── LandingPage.spec.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "comments": false, 3 | "env": { 4 | "test": { 5 | "presets": [ 6 | ["env", { 7 | "targets": { "node": 7 } 8 | }], 9 | "stage-0" 10 | ], 11 | "plugins": ["istanbul"] 12 | }, 13 | "main": { 14 | "presets": [ 15 | ["env", { 16 | "targets": { "node": 7 } 17 | }], 18 | "stage-0" 19 | ] 20 | }, 21 | "renderer": { 22 | "presets": [ 23 | ["env", { 24 | "modules": false 25 | }], 26 | "stage-0" 27 | ] 28 | }, 29 | "web": { 30 | "presets": [ 31 | ["env", { 32 | "modules": false 33 | }], 34 | "stage-0" 35 | ] 36 | } 37 | }, 38 | "plugins": ["transform-runtime"] 39 | } 40 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | test/unit/coverage/** 2 | test/unit/*.js 3 | test/e2e/*.js 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | parserOptions: { 5 | sourceType: 'module' 6 | }, 7 | env: { 8 | browser: true, 9 | node: true 10 | }, 11 | extends: 'standard', 12 | globals: { 13 | __static: true 14 | }, 15 | plugins: [ 16 | 'html' 17 | ], 18 | 'rules': { 19 | // allow paren-less arrow functions 20 | 'arrow-parens': 0, 21 | // allow async-await 22 | 'generator-star-spacing': 0, 23 | // allow debugger during development 24 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, 25 | 'space-before-function-paren': 0, 26 | 'indent': ['warn', 2], 27 | 'no-tabs': 0, 28 | 'semi': 0 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=Vue 2 | *.css linguist-language=Vue 3 | *.html linguist-language=Vue 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | dist/electron/* 3 | dist/web/* 4 | build/* 5 | !build/icons 6 | coverage 7 | node_modules/ 8 | npm-debug.log 9 | npm-debug.log.* 10 | thumbs.db 11 | !.gitkeep 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-electron-music 2 | 3 | > nodejs、vue、electron制作的网易云音乐客户端(MacOs)版,目前没有在Windows运行过,所以样式可能有点差异,时间不足,有空再更新。 4 | 5 | [运行效果预览视频](http://v.youku.com/v_show/id_XMjk2MzYxODUyMA==.html?spm=a2hzp.8244740.0.0) 6 | 7 | ### 技术栈 8 | - vue全家桶(vue vue-router vuex) 9 | - electron(应用框架) 10 | - request(请求数据) 11 | - ES6 12 | - SCSS 13 | - 更多... 14 | 15 | ## 图片预览 16 | ![](https://github.com/eugeneCN/vue-electron-music/blob/master/doc/3872766167-5999ac798b713_articlex.png) 17 | ![](https://github.com/eugeneCN/vue-electron-music/blob/master/doc/3913213729-5999ac98a8c6c_articlex.jpeg) 18 | ![](https://github.com/eugeneCN/vue-electron-music/blob/master/doc/1588237435-599597952042d_articlex.jpeg) 19 | 20 | ## 安装运行(Build Setup) 21 | 22 | > 确保项目正常运行,请将你的node版本升级为7.0+以上,低版本node没有测试过。 23 | 24 | ``` bash 25 | 26 | # 克隆项目 27 | git clone https://github.com/eugeneCN/vue-electron-music.git 28 | 29 | # 安装依赖 30 | npm install 31 | 32 | # 启动api localhost:3000 33 | cd api/ 34 | npm install 35 | npm start 36 | 37 | # 启动本地服务 localhost:9080 38 | npm run dev 39 | 40 | # 打包成本地应用 41 | npm run build 42 | 43 | # 开启控制台 44 | Ctrl + Shift + i 45 | 46 | ``` 47 | 48 | ## 鸣谢 49 | 50 | 此应用提供的API: [https://github.com/Binaryify/NeteaseCloudMusicApi](https://github.com/Binaryify/NeteaseCloudMusicApi) ,此接口的说明请到这里[查看](https://binaryify.github.io/NeteaseCloudMusicApi/#/) 51 | -------------------------------------------------------------------------------- /api/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | *.log 4 | -------------------------------------------------------------------------------- /api/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NeteaseCloudMusicApi", 3 | "version": "2.5.9", 4 | "description": "网易云音乐 NodeJS 版 API", 5 | "scripts": { 6 | "start": "node app.js", 7 | "test": "mocha -r intelli-espower-loader -t 20000 test" 8 | }, 9 | "keywords": [ 10 | "网易云音乐", 11 | "网易云", 12 | "音乐", 13 | "网易云音乐nodejs" 14 | ], 15 | "author": "", 16 | "license": "MIT", 17 | "dependencies": { 18 | "big-integer": "^1.6.17", 19 | "express": "^4.15.2", 20 | "request": "^2.81.0" 21 | }, 22 | "devDependencies": { 23 | "intelli-espower-loader": "^1.0.1", 24 | "mocha": "^3.2.0", 25 | "power-assert": "^1.4.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /api/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 网易云音乐 API 8 | 9 | 10 |

网易云音乐 API

11 | 当你看到这个页面时,这个服务已经成功跑起来了~ 12 | 查看文档 13 |

例子:

14 | 19 | 47 | 48 | -------------------------------------------------------------------------------- /api/router/album.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "" 9 | } 10 | const id = req.query.id 11 | createWebAPIRequest( 12 | 'music.163.com', 13 | `/weapi/v1/album/${id}`, 14 | 'POST', 15 | data, 16 | cookie, 17 | music_req => { 18 | res.send(music_req) 19 | }, 20 | err => res.status(502).send('fetch error') 21 | ) 22 | }) 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/artist_album.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const id = req.query.id 8 | const data = { 9 | 'offset': req.query.offset || 0, 10 | 'total': true, 11 | 'limit': req.query.limit || 30, 12 | "csrf_token": "" 13 | } 14 | createWebAPIRequest( 15 | 'music.163.com', 16 | `/weapi/artist/albums/${id}`, 17 | 'POST', 18 | data, 19 | cookie, 20 | music_req => res.send(music_req), 21 | err => res.status(502).send('fetch error') 22 | ) 23 | }) 24 | 25 | 26 | module.exports = router -------------------------------------------------------------------------------- /api/router/artists.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "" 9 | } 10 | const id = req.query.id 11 | const offset = req.query.offset || 0 12 | const limit = req.query.limit || 50 13 | createWebAPIRequest( 14 | 'music.163.com', 15 | `/weapi/v1/artist/${id}?offset=${offset}&limit=${limit}`, 16 | 'POST', 17 | data, 18 | cookie, 19 | music_req => res.send(music_req), 20 | err => res.status(502).send('fetch error') 21 | ) 22 | }) 23 | 24 | 25 | module.exports = router -------------------------------------------------------------------------------- /api/router/artists_desc.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const id = req.query.id 8 | const data = { 9 | id, 10 | "csrf_token": "" 11 | } 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | `/weapi/artist/introduction`, 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => res.send(music_req), 19 | err => res.status(502).send('fetch error') 20 | ) 21 | }) 22 | 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/artists_mv.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const id = req.query.id 8 | const data = { 9 | artistId: id, 10 | "total": true, 11 | "offset": req.query.offset, 12 | "limit": req.query.limit, 13 | "csrf_token": "" 14 | } 15 | createWebAPIRequest( 16 | 'music.163.com', 17 | `/weapi/artist/mvs`, 18 | 'POST', 19 | data, 20 | cookie, 21 | music_req => res.send(music_req), 22 | err => res.status(502).send('fetch error') 23 | ) 24 | }) 25 | 26 | 27 | module.exports = router -------------------------------------------------------------------------------- /api/router/banner.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | createRequest('/api/v2/banner/get', 'GET', null) 7 | .then(result => { 8 | res.setHeader("Content-Type", "application/json") 9 | res.send(result) 10 | }) 11 | .catch(err => { 12 | res.status(502).send('fetch error') 13 | }) 14 | }) 15 | 16 | 17 | module.exports = router -------------------------------------------------------------------------------- /api/router/check_music.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const request = require("request") 4 | const { createWebAPIRequest } = require("../util/util") 5 | router.get("/", (req, res) => { 6 | const id = parseInt(req.query.id) 7 | const br = parseInt(req.query.br || 999000) 8 | const data = { 9 | "ids": [id], 10 | "br": br, 11 | "csrf_token": "" 12 | } 13 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 14 | 15 | createWebAPIRequest( 16 | 'music.163.com', 17 | '/weapi/song/enhance/player/url', 18 | 'POST', 19 | data, 20 | cookie, 21 | music_req => { 22 | if(JSON.parse(music_req).code==200){ 23 | return res.send({success: true, message: 'ok'}) 24 | } 25 | return res.send({success: false, message: '亲爱的,暂无版权'}); 26 | }, 27 | err => { 28 | res.status(502).send('fetch error') 29 | } 30 | ) 31 | }) 32 | 33 | 34 | module.exports = router -------------------------------------------------------------------------------- /api/router/comment_album.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const rid=req.query.id 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const data = { 9 | "offset": req.query.offset || 0, 10 | "rid": rid, 11 | "limit": req.query.limit || 20, 12 | "csrf_token": "" 13 | } 14 | createWebAPIRequest( 15 | 'music.163.com', 16 | `/weapi/v1/resource/comments/R_AL_3_${rid}/?csrf_token=`, 17 | 'POST', 18 | data, 19 | cookie, 20 | music_req => { 21 | res.send(music_req) 22 | }, 23 | err => res.status(502).send('fetch error') 24 | ) 25 | }) 26 | 27 | module.exports = router -------------------------------------------------------------------------------- /api/router/comment_dj.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const rid=req.query.id 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const data = { 9 | "offset": req.query.offset || 0, 10 | "rid": rid, 11 | "limit": req.query.limit || 20, 12 | "csrf_token": "" 13 | } 14 | createWebAPIRequest( 15 | 'music.163.com', 16 | `/weapi/v1/resource/comments/A_DJ_1_${rid}/?csrf_token=`, 17 | 'POST', 18 | data, 19 | cookie, 20 | music_req => { 21 | res.send(music_req) 22 | }, 23 | err => res.status(502).send('fetch error') 24 | ) 25 | }) 26 | 27 | module.exports = router -------------------------------------------------------------------------------- /api/router/comment_like.js: -------------------------------------------------------------------------------- 1 | //comment like 2 | const express = require("express") 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | router.get("/", (req, res) => { 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const cid = req.query.cid //评论 id 9 | const id=req.query.id// 歌曲 id 10 | const typeMap={ 11 | 0:"R_SO_4_",//歌曲 12 | 1:"R_MV_5_",//mv 13 | 2:"A_PL_0_",//歌单 14 | 3:"R_AL_3_",//专辑 15 | 4:"A_DJ_1_",//电台 16 | } 17 | const type=typeMap[req.query.type] 18 | const data = { 19 | "threadId": `${type}${id}`, 20 | commentId:cid, 21 | "csrf_token": "" 22 | } 23 | const action=(req.query.t==1?'like':'unlike') 24 | 25 | const url = `/weapi/v1/comment/${action}` 26 | createWebAPIRequest( 27 | 'music.163.com', 28 | url, 29 | 'POST', 30 | data, 31 | cookie, 32 | music_req => res.send(music_req), 33 | err => res.status(502).send('fetch error') 34 | ) 35 | }) 36 | 37 | 38 | 39 | module.exports = router -------------------------------------------------------------------------------- /api/router/comment_music.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const rid=req.query.id 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const data = { 9 | "offset": req.query.offset || 0, 10 | "rid": rid, 11 | "limit": req.query.limit || 20, 12 | "csrf_token": "" 13 | } 14 | createWebAPIRequest( 15 | 'music.163.com', 16 | `/weapi/v1/resource/comments/R_SO_4_${rid}/?csrf_token=`, 17 | 'POST', 18 | data, 19 | cookie, 20 | music_req => { 21 | res.send(music_req) 22 | }, 23 | err => res.status(502).send('fetch error') 24 | ) 25 | }) 26 | 27 | module.exports = router -------------------------------------------------------------------------------- /api/router/comment_mv.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const rid=req.query.id 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const data = { 9 | "offset": req.query.offset || 0, 10 | "rid": rid, 11 | "limit": req.query.limit || 20, 12 | "csrf_token": "" 13 | } 14 | createWebAPIRequest( 15 | 'music.163.com', 16 | `/weapi/v1/resource/comments/R_MV_5_${rid}/?csrf_token=`, 17 | 'POST', 18 | data, 19 | cookie, 20 | music_req => { 21 | res.send(music_req) 22 | }, 23 | err => res.status(502).send('fetch error') 24 | ) 25 | }) 26 | 27 | module.exports = router -------------------------------------------------------------------------------- /api/router/comment_playlist.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const rid=req.query.id 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const data = { 9 | "offset": req.query.offset || 0, 10 | "rid": rid, 11 | "limit": req.query.limit || 20, 12 | "csrf_token": "" 13 | } 14 | createWebAPIRequest( 15 | 'music.163.com', 16 | `/weapi/v1/resource/comments/A_PL_0_${rid}/?csrf_token=`, 17 | 'POST', 18 | data, 19 | cookie, 20 | music_req => { 21 | res.send(music_req) 22 | }, 23 | err => res.status(502).send('fetch error') 24 | ) 25 | }) 26 | 27 | module.exports = router -------------------------------------------------------------------------------- /api/router/daily_signin.js: -------------------------------------------------------------------------------- 1 | // 签到 2 | const express = require("express") 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | router.get("/", (req, res) => { 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const data = { 9 | "csrf_token": "" 10 | } 11 | // {'android': {'point': 3, 'code': 200}, 'web': {'point': 2, 'code': 200}} 12 | // {'android': {'code': -2, 'msg': '重复签到'}, 'web': {'code': -2, 'msg': '重复签到'}} 13 | // 'android': {'code': 301}, 'web': {'code': 301}} 14 | 15 | let type = req.query.type || 0 //0为安卓端签到 3点经验,1为网页签到,2点经验 16 | const action = `/weapi/point/dailyTask?type=${type}` 17 | createWebAPIRequest( 18 | 'music.163.com', 19 | action, 20 | 'POST', 21 | data, 22 | cookie, 23 | music_req => res.send(music_req), 24 | err => res.status(502).send('fetch error') 25 | ) 26 | }) 27 | 28 | 29 | 30 | module.exports = router -------------------------------------------------------------------------------- /api/router/dj_catelist.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "" 9 | } 10 | createWebAPIRequest( 11 | 'music.163.com', 12 | '/weapi/djradio/category/get', 13 | 'POST', 14 | data, 15 | cookie, 16 | music_req => { 17 | res.send(music_req) 18 | }, 19 | err => res.status(502).send('fetch error') 20 | ) 21 | }) 22 | 23 | module.exports = router -------------------------------------------------------------------------------- /api/router/dj_detail.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const rid=req.query.rid 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const data = { 9 | 'id': rid, 10 | "csrf_token": "" 11 | } 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | '/weapi/djradio/get', 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => { 19 | res.send(music_req) 20 | }, 21 | err => res.status(502).send('fetch error') 22 | ) 23 | }) 24 | 25 | module.exports = router -------------------------------------------------------------------------------- /api/router/dj_hot.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | 'cat': req.query.type, 9 | cateId: req.query.type, 10 | type: req.query.type, 11 | categoryId: req.query.type, 12 | category: req.query.type, 13 | limit: req.query.limit, 14 | offset: req.query.offset, 15 | "csrf_token": "" 16 | } 17 | createWebAPIRequest( 18 | 'music.163.com', 19 | '/weapi/djradio/hot/v1', 20 | 'POST', 21 | data, 22 | cookie, 23 | music_req => { 24 | res.send(music_req) 25 | }, 26 | err => res.status(502).send('fetch error') 27 | ) 28 | }) 29 | 30 | module.exports = router -------------------------------------------------------------------------------- /api/router/dj_program.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const rid=req.query.rid 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const data = { 9 | 'asc': req.query.asc, 10 | 'radioId': rid, 11 | 'limit': req.query.limit, 12 | 'offset': req.query.offset, 13 | "csrf_token": "" 14 | } 15 | createWebAPIRequest( 16 | 'music.163.com', 17 | '/weapi/dj/program/byradio', 18 | 'POST', 19 | data, 20 | cookie, 21 | music_req => { 22 | res.send(music_req) 23 | }, 24 | err => res.status(502).send('fetch error') 25 | ) 26 | }) 27 | 28 | module.exports = router -------------------------------------------------------------------------------- /api/router/dj_program_detail.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | 'id': req.query.id, 9 | "csrf_token": "" 10 | } 11 | createWebAPIRequest( 12 | 'music.163.com', 13 | '/weapi/dj/program/detail', 14 | 'POST', 15 | data, 16 | cookie, 17 | music_req => { 18 | res.send(music_req) 19 | }, 20 | err => res.status(502).send('fetch error') 21 | ) 22 | }) 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/dj_recommend.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "" 9 | } 10 | createWebAPIRequest( 11 | 'music.163.com', 12 | '/weapi/djradio/recommend/v1', 13 | 'POST', 14 | data, 15 | cookie, 16 | music_req => { 17 | res.send(music_req) 18 | }, 19 | err => res.status(502).send('fetch error') 20 | ) 21 | }) 22 | 23 | module.exports = router -------------------------------------------------------------------------------- /api/router/dj_recommend_type.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | cateId: req.query.type, 9 | "csrf_token": "" 10 | } 11 | createWebAPIRequest( 12 | 'music.163.com', 13 | '/weapi/djradio/recommend', 14 | 'POST', 15 | data, 16 | cookie, 17 | music_req => { 18 | res.send(music_req) 19 | }, 20 | err => res.status(502).send('fetch error') 21 | ) 22 | }) 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/dj_sub.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "id": req.query.rid, 9 | "csrf_token": "" 10 | } 11 | const action=(req.query.t==1?'sub':'unsub') 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | `/weapi/djradio/${action}`, 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => { 19 | res.send(music_req) 20 | }, 21 | err => res.status(502).send('fetch error') 22 | ) 23 | }) 24 | 25 | module.exports = router -------------------------------------------------------------------------------- /api/router/event.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "" 9 | } 10 | createWebAPIRequest( 11 | 'music.163.com', 12 | '/weapi/v1/event/get', 13 | 'POST', 14 | data, 15 | cookie, 16 | music_req => res.send(music_req), 17 | err => res.status(502).send('fetch error') 18 | ) 19 | }) 20 | 21 | module.exports = router 22 | -------------------------------------------------------------------------------- /api/router/fm_trash.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "" 9 | } 10 | const songid = req.query.id 11 | const alg = "RT" 12 | const time = req.query.time || 25 13 | createWebAPIRequest( 14 | 'music.163.com', 15 | `/weapi/radio/trash/add?alg=${alg}&songId=${songid}&time=${time}`, 16 | 'POST', 17 | data, 18 | cookie, 19 | music_req => res.send(music_req), 20 | err => res.status(502).send('fetch error') 21 | ) 22 | }) 23 | 24 | 25 | 26 | module.exports = router -------------------------------------------------------------------------------- /api/router/follow.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "" 9 | } 10 | const url = req.query.type == 'add' ? 'follow' : "delfollow" 11 | const id = req.query.id 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | `/weapi/user/${url}/${id}`, 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => { 19 | res.send(music_req) 20 | }, 21 | err => res.status(502).send('fetch error') 22 | ) 23 | }) 24 | 25 | module.exports = router -------------------------------------------------------------------------------- /api/router/like.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "" 9 | } 10 | const trackId = req.query.id 11 | const like = req.query.like || true 12 | const alg = req.query.alg || "itembased" 13 | const time = req.query.time || 25 14 | console.log(alg) 15 | createWebAPIRequest( 16 | 'music.163.com', 17 | `/weapi/radio/like?alg=${alg}&trackId=${trackId}&like=${like}&time=${time}`, 18 | 'POST', 19 | data, 20 | cookie, 21 | music_req => res.send(music_req), 22 | err => res.status(502).send('fetch error') 23 | ) 24 | }) 25 | 26 | 27 | 28 | module.exports = router -------------------------------------------------------------------------------- /api/router/likelist.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | uid: req.query.uid, 9 | "csrf_token": "" 10 | } 11 | createWebAPIRequest( 12 | 'music.163.com', 13 | `/weapi/song/like/get`, 14 | 'POST', 15 | data, 16 | cookie, 17 | music_req => { 18 | res.send(music_req) 19 | }, 20 | err => res.status(502).send('fetch error') 21 | ) 22 | }) 23 | 24 | 25 | 26 | module.exports = router -------------------------------------------------------------------------------- /api/router/logWeb.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "", 9 | } 10 | 11 | createWebAPIRequest( 12 | 'music.163.com', 13 | '/weapi/feedback/weblog', 14 | 'POST', 15 | data, 16 | cookie, 17 | music_req => res.send(music_req), 18 | err => res.status(502).send('fetch error') 19 | ) 20 | }) 21 | 22 | 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/login.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const crypto = require('crypto') 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | router.get("/", (req, res) => { 7 | const email = req.query.email 8 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 9 | const md5sum = crypto.createHash('md5') 10 | md5sum.update(req.query.password) 11 | const data = { 12 | 'username': email, 13 | 'password': md5sum.digest('hex'), 14 | 'rememberLogin': 'true', 15 | 'clientToken':"1_jVUMqWEPke0/1/Vu56xCmJpo5vP1grjn_SOVVDzOc78w8OKLVZ2JH7IfkjSXqgfmh" 16 | } 17 | console.log(email,req.query.password); 18 | 19 | createWebAPIRequest( 20 | 'music.163.com', 21 | '/weapi/login?csrf_token=', 22 | 'POST', 23 | data, 24 | cookie, 25 | (music_req, cookie) => { 26 | console.log(music_req) 27 | res.set({ 28 | 'Set-Cookie': cookie, 29 | }) 30 | res.send(music_req) 31 | }, 32 | err => res.status(502).send('fetch error') 33 | ) 34 | }) 35 | 36 | module.exports = router -------------------------------------------------------------------------------- /api/router/loginCellphone.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const crypto = require('crypto') 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | router.get("/", (req, res) => { 7 | const phone = req.query.phone 8 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 9 | const md5sum = crypto.createHash('md5') 10 | md5sum.update(req.query.password) 11 | const data = { 12 | 'phone': phone, 13 | 'password': md5sum.digest('hex'), 14 | 'rememberLogin': 'true' 15 | } 16 | 17 | createWebAPIRequest( 18 | 'music.163.com', 19 | '/weapi/login/cellphone', 20 | 'POST', 21 | data, 22 | cookie, 23 | (music_req, cookie) => { 24 | console.log(music_req) 25 | res.set({ 26 | 'Set-Cookie': cookie, 27 | }) 28 | res.send(music_req) 29 | }, 30 | err => res.status(502).send('fetch error') 31 | ) 32 | }) 33 | 34 | module.exports = router -------------------------------------------------------------------------------- /api/router/login_refresh.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | let csrf = req.query.t || '' 8 | for (let i in cookie) { 9 | if (cookie[i].name == '__csrf') { 10 | csrf = cookie.value 11 | } 12 | } 13 | const data = { 14 | "csrf_token": csrf 15 | } 16 | createWebAPIRequest( 17 | 'music.163.com', 18 | `/weapi/login/token/refresh?csrf_token=${csrf}`, 19 | 'POST', 20 | data, 21 | cookie, 22 | music_req => { 23 | res.send(music_req) 24 | }, 25 | err => res.status(502).send('fetch error') 26 | ) 27 | }) 28 | 29 | module.exports = router -------------------------------------------------------------------------------- /api/router/lyric.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const id = req.query.id 7 | createRequest('/api/song/lyric?os=osx&id=' + id + '&lv=-1&kv=-1&tv=-1', 'GET', null) 8 | .then(result => { 9 | res.setHeader("Content-Type", "application/json") 10 | res.send(result) 11 | }) 12 | .catch(err => { 13 | res.status(502).send('fetch error') 14 | }) 15 | }) 16 | 17 | 18 | module.exports = router -------------------------------------------------------------------------------- /api/router/musicUrl.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const id = req.query.id 7 | const br = req.query.br || 999000 8 | const data = { 9 | "ids": [id], 10 | "br": br, 11 | "csrf_token": "" 12 | } 13 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 14 | 15 | createWebAPIRequest( 16 | 'music.163.com', 17 | '/weapi/song/enhance/player/url', 18 | 'POST', 19 | data, 20 | cookie, 21 | music_req => { 22 | res.setHeader("Content-Type", "application/json") 23 | res.send(music_req) 24 | }, 25 | err => { 26 | res.status(502).send('fetch error') 27 | } 28 | ) 29 | }) 30 | 31 | module.exports = router -------------------------------------------------------------------------------- /api/router/mv.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createRequest } = require("../util/util") 4 | const request=require("request") 5 | router.get("/", (req, res) => { 6 | const mvid = req.query.mvid 7 | createRequest(`/api/mv/detail/?id=${mvid}&type=mp4`, 'GET', null) 8 | .then(result => { 9 | res.setHeader("Content-Type", "application/json") 10 | res.send(result) 11 | }) 12 | .catch(err => { 13 | res.status(502).send('fetch error') 14 | }) 15 | }) 16 | 17 | 18 | module.exports = router -------------------------------------------------------------------------------- /api/router/mv_first.js: -------------------------------------------------------------------------------- 1 | //最新mv 2 | const express = require("express") 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | // type ALL, ZH,EA,KR,JP 7 | router.get("/", (req, res) => { 8 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 9 | const data = { 10 | // 'offset': req.query.offset || 0, 11 | 'total': true, 12 | 'limit': req.query.limit || 30, 13 | "csrf_token": "" 14 | } 15 | createWebAPIRequest( 16 | 'music.163.com', 17 | '/weapi/mv/first', 18 | 'POST', 19 | data, 20 | cookie, 21 | music_req => res.send(music_req), 22 | err => res.status(502).send('fetch error') 23 | ) 24 | }) 25 | 26 | 27 | module.exports = router -------------------------------------------------------------------------------- /api/router/mv_url.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const request = require("request") 4 | 5 | router.get("/", (req, res) => { 6 | const url = req.query.url 7 | const headers = { 8 | "Referer": "http://music.163.com/", 9 | "Cookie": "appver=1.5.0.75771;", 10 | 'Content-Type': 'video/mp4', 11 | 'Location': url 12 | } 13 | const options = { 14 | header: headers, 15 | url: url 16 | } 17 | request(options) 18 | .on('error', err => { 19 | res.send({ err }) 20 | }) 21 | .pipe(res) 22 | }) 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/personal_fm.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "" 9 | } 10 | 11 | createWebAPIRequest( 12 | 'music.163.com', 13 | '/weapi/v1/radio/get', 14 | 'POST', 15 | data, 16 | cookie, 17 | music_req => res.send(music_req), 18 | err => res.status(502).send('fetch error') 19 | ) 20 | }) 21 | 22 | 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/personalized.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | } 9 | createWebAPIRequest( 10 | 'music.163.com', 11 | '/api/personalized/playlist', 12 | 'POST', 13 | data, 14 | cookie, 15 | music_req => { 16 | res.send(music_req) 17 | }, 18 | err => res.status(502).send('fetch error') 19 | ) 20 | }) 21 | 22 | 23 | module.exports = router -------------------------------------------------------------------------------- /api/router/personalized_djprogram.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | } 9 | createWebAPIRequest( 10 | 'music.163.com', 11 | '/api/personalized/djprogram', 12 | 'POST', 13 | data, 14 | cookie, 15 | music_req => { 16 | res.send(music_req) 17 | }, 18 | err => res.status(502).send('fetch error') 19 | ) 20 | }) 21 | 22 | 23 | module.exports = router -------------------------------------------------------------------------------- /api/router/personalized_mv.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | } 9 | createWebAPIRequest( 10 | 'music.163.com', 11 | '/api/personalized/mv', 12 | 'POST', 13 | data, 14 | cookie, 15 | music_req => { 16 | res.send(music_req) 17 | }, 18 | err => res.status(502).send('fetch error') 19 | ) 20 | }) 21 | 22 | 23 | module.exports = router -------------------------------------------------------------------------------- /api/router/personalized_newsong.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | type: "recommend" 9 | } 10 | createWebAPIRequest( 11 | 'music.163.com', 12 | '/api/personalized/newsong', 13 | 'POST', 14 | data, 15 | cookie, 16 | music_req => { 17 | res.send(music_req) 18 | }, 19 | err => res.status(502).send('fetch error') 20 | ) 21 | }) 22 | 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/personalized_privatecontent.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | } 9 | createWebAPIRequest( 10 | 'music.163.com', 11 | '/api/personalized/privatecontent', 12 | 'POST', 13 | data, 14 | cookie, 15 | music_req => { 16 | res.send(music_req) 17 | }, 18 | err => res.status(502).send('fetch error') 19 | ) 20 | }) 21 | 22 | 23 | module.exports = router -------------------------------------------------------------------------------- /api/router/playlist_catlist.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "" 9 | } 10 | createWebAPIRequest( 11 | 'music.163.com', 12 | '/weapi/playlist/catalogue', 13 | 'POST', 14 | data, 15 | cookie, 16 | music_req => { 17 | res.send(music_req) 18 | }, 19 | err => res.status(502).send('fetch error') 20 | ) 21 | }) 22 | 23 | module.exports = router -------------------------------------------------------------------------------- /api/router/playlist_detail.js: -------------------------------------------------------------------------------- 1 | const http = require('http') 2 | const express = require("express") 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | router.get("/", (req, res) => { 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | let detail, imgurl 9 | const data = { 10 | "id": req.query.id, 11 | "offset": 0, 12 | "total": true, 13 | "limit": 1000, 14 | "n": 1000, 15 | "csrf_token": "" 16 | } 17 | 18 | createWebAPIRequest( 19 | 'music.163.com', 20 | '/weapi/v3/playlist/detail', 21 | 'POST', 22 | data, 23 | cookie, 24 | music_req => { 25 | console.log(music_req) 26 | // detail = music_req 27 | res.send(music_req) 28 | // mergeRes() 29 | }, 30 | err => { 31 | res.status(502).send('fetch error') 32 | } 33 | ) 34 | 35 | // FIXME:i dont know the api to get coverimgurl 36 | // so i get it by parsing html 37 | // const http_client = http.get({ 38 | // hostname: 'music.163.com', 39 | // path: '/playlist?id=' + req.query.id, 40 | // headers: { 41 | // 'Referer': 'http://music.163.com', 42 | // }, 43 | // }, function (res) { 44 | // res.setEncoding('utf8') 45 | // let html = '' 46 | // res.on('data', function (chunk) { 47 | // html += chunk 48 | // }) 49 | // res.on('end', function () { 50 | // console.log('end', html) 51 | // const regImgCover = /\ { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | } 9 | createWebAPIRequest( 10 | 'music.163.com', 11 | '/api/playlist/hottags', 12 | 'POST', 13 | data, 14 | cookie, 15 | music_req => { 16 | res.send(music_req) 17 | }, 18 | err => res.status(502).send('fetch error') 19 | ) 20 | }) 21 | 22 | module.exports = router -------------------------------------------------------------------------------- /api/router/playlist_tracks.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | //收藏单曲到歌单,从歌单删除歌曲 op=del,add;pid=歌单id,tracks=歌曲id 5 | router.get("/", (req, res) => { 6 | const op = req.query.op 7 | const pid = req.query.pid 8 | const tracks = req.query.tracks 9 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 10 | // console.log('COOKIESS', cookie) 11 | const data = { 12 | "op": op, 13 | "pid": pid, 14 | "tracks": tracks, 15 | "trackIds": JSON.stringify([tracks]), 16 | "csrf_token": "", 17 | } 18 | createWebAPIRequest( 19 | 'music.163.com', 20 | '/weapi/playlist/manipulate/tracks', 21 | 'POST', 22 | data, 23 | cookie, 24 | music_req => res.send(music_req), 25 | err => res.status(502).send('fetch error') 26 | 27 | ) 28 | }) 29 | 30 | module.exports = router -------------------------------------------------------------------------------- /api/router/program_recommend.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | cateId: req.query.type, 9 | "csrf_token": "" 10 | } 11 | createWebAPIRequest( 12 | 'music.163.com', 13 | '/weapi/program/recommend/v1', 14 | 'POST', 15 | data, 16 | cookie, 17 | music_req => { 18 | res.send(music_req) 19 | }, 20 | err => res.status(502).send('fetch error') 21 | ) 22 | }) 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/recommend_dislike.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "" 9 | } 10 | 11 | createWebAPIRequest( 12 | 'music.163.com', 13 | '/weapi/v1/radio/get', 14 | 'POST', 15 | data, 16 | cookie, 17 | music_req => res.send(music_req), 18 | err => res.status(502).send('fetch error') 19 | ) 20 | }) 21 | 22 | 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/recommend_resource.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "" 9 | } 10 | 11 | createWebAPIRequest( 12 | 'music.163.com', 13 | '/weapi/v1/discovery/recommend/resource', 14 | 'POST', 15 | data, 16 | cookie, 17 | music_req => res.send(music_req), 18 | err => res.status(502).send('fetch error') 19 | ) 20 | }) 21 | 22 | 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/recommend_songs.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/",(req,res)=>{ 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "offset": 0, 9 | "total": true, 10 | "limit": 20, 11 | "csrf_token": "" 12 | } 13 | 14 | createWebAPIRequest( 15 | 'music.163.com', 16 | '/weapi/v1/discovery/recommend/songs', 17 | 'POST', 18 | data, 19 | cookie, 20 | music_req => res.send(music_req), 21 | err => res.status(502).send('fetch error') 22 | ) 23 | }) 24 | 25 | 26 | 27 | module.exports=router -------------------------------------------------------------------------------- /api/router/resource_like.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "threadId": req.query.id, 9 | "csrf_token": "" 10 | } 11 | const action = (req.query.t == 1 ? 'like' : 'unlike') 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | `/weapi/resource/${action}`, 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => { 19 | res.send(music_req) 20 | }, 21 | err => res.status(502).send('fetch error') 22 | ) 23 | }) 24 | 25 | module.exports = router -------------------------------------------------------------------------------- /api/router/search.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const keywords = req.query.keywords 7 | const type = req.query.type || 1 8 | const limit = req.query.limit || 30 9 | const offset = req.query.offset || 0 10 | // 搜索单曲(1),歌手(100),专辑(10),歌单(1000),用户(1002) *(type)* 11 | const data = 's=' + keywords + '&limit=' + limit + '&type=' + type + '&offset=' + offset 12 | createRequest('/api/search/pc/', 'POST', data) 13 | .then(result => { 14 | res.setHeader("Content-Type", "application/json") 15 | res.send(result) 16 | }) 17 | .catch(err => { 18 | res.status(502).send('fetch error') 19 | }) 20 | }) 21 | 22 | module.exports = router -------------------------------------------------------------------------------- /api/router/search_hot.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "", 9 | type: req.query.type || 1, 10 | s: keywords || req.query.keywords || '' 11 | } 12 | 13 | createWebAPIRequest( 14 | 'music.163.com', 15 | '/api/search/hot?type=1', 16 | 'POST', 17 | data, 18 | cookie, 19 | music_req => res.send(music_req), 20 | err => res.status(502).send('fetch error') 21 | ) 22 | }) 23 | 24 | 25 | 26 | module.exports = router -------------------------------------------------------------------------------- /api/router/search_multimatch.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "", 9 | type: req.query.type || 1, 10 | s: req.query.keywords || req.query.keywords || '' 11 | } 12 | 13 | createWebAPIRequest( 14 | 'music.163.com', 15 | '/weapi/search/suggest/multimatch', 16 | 'POST', 17 | data, 18 | cookie, 19 | music_req => { 20 | res.send(music_req) 21 | console.log(Object.keys(JSON.parse(music_req).result)) 22 | }, 23 | err => res.status(502).send('fetch error') 24 | ) 25 | }) 26 | 27 | 28 | 29 | module.exports = router -------------------------------------------------------------------------------- /api/router/search_suggest.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "", 9 | s: req.query.keywords || req.query.keywords || '' 10 | } 11 | 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | '/weapi/search/suggest/web', 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => { 19 | res.send(music_req) 20 | }, 21 | err => res.status(502).send('fetch error') 22 | ) 23 | }) 24 | 25 | 26 | 27 | module.exports = router -------------------------------------------------------------------------------- /api/router/simi_artists.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const id = req.query.id 8 | const data = { 9 | artistid:id, 10 | "csrf_token": "" 11 | } 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | `/weapi/discovery/simiArtist`, 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => res.send(music_req), 19 | err => res.status(502).send('fetch error') 20 | ) 21 | }) 22 | 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/simi_mv.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | mvid: req.query.mvid 9 | } 10 | createWebAPIRequest( 11 | 'music.163.com', 12 | '/weapi/discovery/simiMV', 13 | 'POST', 14 | data, 15 | cookie, 16 | music_req => res.send(music_req), 17 | err => res.status(502).send('fetch error') 18 | ) 19 | }) 20 | 21 | 22 | module.exports = router -------------------------------------------------------------------------------- /api/router/simi_playlist.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | songid: req.query.id, 9 | offset: req.query.offset || 0, 10 | limit: req.query.limit || 50 11 | } 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | '/weapi/discovery/simiPlaylist', 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => { 19 | res.send(music_req) 20 | }, 21 | err => res.status(502).send('fetch error') 22 | ) 23 | }) 24 | 25 | module.exports = router -------------------------------------------------------------------------------- /api/router/simi_song.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | songid: req.query.id, 9 | offset: req.query.offset || 0, 10 | limit: req.query.limit || 50 11 | } 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | '/weapi/v1/discovery/simiSong', 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => { 19 | res.send(music_req) 20 | }, 21 | err => res.status(502).send('fetch error') 22 | ) 23 | }) 24 | 25 | module.exports = router -------------------------------------------------------------------------------- /api/router/simi_user.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | songid: req.query.id, 9 | offset: req.query.offset || 0, 10 | limit: req.query.limit || 50 11 | } 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | '/weapi/discovery/simiUser', 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => { 19 | res.send(music_req) 20 | }, 21 | err => res.status(502).send('fetch error') 22 | ) 23 | }) 24 | 25 | module.exports = router -------------------------------------------------------------------------------- /api/router/song_detail.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const id = parseInt(req.query.ids) 8 | const data = { 9 | // "id": id, 10 | 'c': JSON.stringify([{ id: id }]), 11 | "ids": '[' + id + ']', 12 | "csrf_token": "" 13 | } 14 | console.log(data) 15 | createWebAPIRequest( 16 | 'music.163.com', 17 | '/weapi/v3/song/detail', 18 | 'POST', 19 | data, 20 | cookie, 21 | music_req => { 22 | res.send(music_req) 23 | }, 24 | err => res.status(502).send('fetch error') 25 | ) 26 | }) 27 | 28 | module.exports = router -------------------------------------------------------------------------------- /api/router/top_album.js: -------------------------------------------------------------------------------- 1 | //最新mv 2 | const express = require("express") 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | // type ALL, ZH,EA,KR,JP 7 | router.get("/", (req, res) => { 8 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 9 | const data = { 10 | 'offset': req.query.offset, 11 | 'total': true, 12 | 'limit': req.query.limit, 13 | 'area': req.query.type, 14 | "csrf_token": "" 15 | } 16 | createWebAPIRequest( 17 | 'music.163.com', 18 | '/weapi/album/new', 19 | 'POST', 20 | data, 21 | cookie, 22 | music_req => { 23 | res.send(music_req) 24 | }, 25 | err => res.status(502).send('fetch error') 26 | ) 27 | }) 28 | 29 | 30 | module.exports = router -------------------------------------------------------------------------------- /api/router/top_artists.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const offset = req.query.offset || 0 7 | const limit = req.query.limit || 50 8 | createRequest(`/api/artist/top?offset=${offset}&total=false&limit=${limit}`, 'GET', null) 9 | .then(result => { 10 | res.setHeader("Content-Type", "application/json") 11 | res.send(result) 12 | }) 13 | .catch(err => { 14 | res.status(502).send('fetch error') 15 | }) 16 | }) 17 | 18 | 19 | module.exports = router -------------------------------------------------------------------------------- /api/router/top_list.js: -------------------------------------------------------------------------------- 1 | const top_list_all = { 2 | "0": ['云音乐新歌榜', '/api/playlist/detail?id=3779629'], 3 | "1": ['云音乐热歌榜', '/api/playlist/detail?id=3778678'], 4 | "2": ['网易原创歌曲榜', '/api/playlist/detail?id=2884035'], 5 | "3": ['云音乐飙升榜', '/api/playlist/detail?id=19723756'], 6 | "4": ['云音乐电音榜', '/api/playlist/detail?id=10520166'], 7 | "5": ['UK排行榜周榜', '/api/playlist/detail?id=180106'], 8 | "6": ['美国Billboard周榜', '/api/playlist/detail?id=60198'], 9 | "7": ['KTV嗨榜', '/api/playlist/detail?id=21845217'], 10 | "8": ['iTunes榜', '/api/playlist/detail?id=11641012'], 11 | "9": ['Hit FM Top榜', '/api/playlist/detail?id=120001'], 12 | "10": ['日本Oricon周榜', '/api/playlist/detail?id=60131'], 13 | "11": ['韩国Melon排行榜周榜', '/api/playlist/detail?id=3733003'], 14 | "12": ['韩国Mnet排行榜周榜', '/api/playlist/detail?id=60255'], 15 | "13": ['韩国Melon原声周榜', '/api/playlist/detail?id=46772709'], 16 | "14": ['中国TOP排行榜(港台榜)', '/api/playlist/detail?id=112504'], 17 | "15": ['中国TOP排行榜(内地榜)', '/api/playlist/detail?id=64016'], 18 | "16": ['香港电台中文歌曲龙虎榜', '/api/playlist/detail?id=10169002'], 19 | "17": ['华语金曲榜', '/api/playlist/detail?id=4395559'], 20 | "18": ['中国嘻哈榜', '/api/playlist/detail?id=1899724'], 21 | "19": ['法国 NRJ EuroHot 30周榜', '/api/playlist/detail?id=27135204'], 22 | "20": ['台湾Hito排行榜', '/api/playlist/detail?id=112463'], 23 | "21": ['Beatport全球电子舞曲榜', '/api/playlist/detail?id=3812895'] 24 | } 25 | const express = require("express") 26 | const router = express() 27 | const { createRequest } = require("../util/util") 28 | 29 | router.get("/", (req, res) => { 30 | const idx = req.query.idx 31 | const action = 'http://music.163.com' + top_list_all[idx][1] 32 | createRequest(`${action}`, 'GET', null) 33 | .then(result => { 34 | res.setHeader("Content-Type", "application/json") 35 | res.send(result) 36 | }) 37 | .catch(err => { 38 | res.status(502).send('fetch error') 39 | }) 40 | }) 41 | 42 | 43 | module.exports = router -------------------------------------------------------------------------------- /api/router/top_mv.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | 'offset': req.query.offset || 0, 9 | 'total': true, 10 | 'limit': req.query.limit || 30, 11 | "csrf_token": "" 12 | } 13 | 14 | createWebAPIRequest( 15 | 'music.163.com', 16 | '/weapi/mv/toplist', 17 | 'POST', 18 | data, 19 | cookie, 20 | music_req => { 21 | res.setHeader("Content-Type", "application/json") 22 | res.send(music_req) 23 | }, 24 | err => res.status(502).send('fetch error') 25 | ) 26 | }) 27 | 28 | 29 | 30 | module.exports = router -------------------------------------------------------------------------------- /api/router/top_playlist.js: -------------------------------------------------------------------------------- 1 | //分类歌单 2 | const express = require("express") 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | router.get("/", (req, res) => { 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | // order可为 'hot' 可为 'new' 9 | const data = { 10 | cat: req.query.cat || "全部", 11 | order: req.query.order || "hot", 12 | offset: req.query.offset || 0, 13 | total: req.query.total ? 'true' : 'false', 14 | limit: req.query.limit || 50 15 | } 16 | createWebAPIRequest( 17 | 'music.163.com', 18 | '/weapi/playlist/list', 19 | 'POST', 20 | data, 21 | cookie, 22 | music_req => { 23 | res.send(music_req) 24 | }, 25 | err => res.status(502).send('fetch error') 26 | ) 27 | }) 28 | 29 | module.exports = router -------------------------------------------------------------------------------- /api/router/top_playlist_highquality.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | cat: req.query.cat || "全部", 9 | offset: req.query.offset || 0, 10 | limit: req.query.limit || 20 11 | } 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | '/weapi/playlist/highquality/list', 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => { 19 | res.send(music_req) 20 | }, 21 | err => res.status(502).send('fetch error') 22 | ) 23 | }) 24 | 25 | module.exports = router -------------------------------------------------------------------------------- /api/router/top_songs.js: -------------------------------------------------------------------------------- 1 | //新歌上架 2 | const express = require("express") 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | // type ALL, ZH,EA,KR,JP 7 | router.get("/", (req, res) => { 8 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 9 | const data = { 10 | 'offset': request.query.offset, 11 | 'total': true, 12 | 'limit': request.query.limit, 13 | 'area': request.query.type, 14 | "csrf_token": "" 15 | } 16 | createWebAPIRequest( 17 | 'music.163.com', 18 | '/weapi/v1/discovery/new/songs', 19 | 'POST', 20 | data, 21 | cookie, 22 | music_req => { 23 | res.send(music_req) 24 | }, 25 | err => res.status(502).send('fetch error') 26 | ) 27 | }) 28 | 29 | 30 | module.exports = router -------------------------------------------------------------------------------- /api/router/toplist.js: -------------------------------------------------------------------------------- 1 | // 排行榜 2 | const express = require("express") 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | router.get("/", (req, res) => { 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const data = { 9 | "csrf_token": "", 10 | } 11 | createWebAPIRequest( 12 | 'music.163.com', 13 | '/weapi/toplist', 14 | 'POST', 15 | data, 16 | cookie, 17 | music_req => { 18 | res.send(music_req) 19 | }, 20 | err => res.status(502).send('fetch error') 21 | ) 22 | }) 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/toplist_artist.js: -------------------------------------------------------------------------------- 1 | //艺术家分类 2 | const express = require("express") 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | router.get("/", (req, res) => { 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const data = { 9 | type: request.query.type, 10 | "csrf_token": "", 11 | } 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | '/weapi/toplist/artist', 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => { 19 | res.send(music_req) 20 | }, 21 | err => res.status(502).send('fetch error') 22 | ) 23 | }) 24 | 25 | module.exports = router -------------------------------------------------------------------------------- /api/router/toplist_detail.js: -------------------------------------------------------------------------------- 1 | // 排行榜详情 2 | const express = require("express") 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | router.get("/", (req, res) => { 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const data = { 9 | id: request.query.id, 10 | limit: 20, 11 | "csrf_token": "", 12 | } 13 | createWebAPIRequest( 14 | 'music.163.com', 15 | '/weapi/toplist/detail', 16 | 'POST', 17 | data, 18 | cookie, 19 | music_req => { 20 | res.send(music_req) 21 | }, 22 | err => res.status(502).send('fetch error') 23 | ) 24 | }) 25 | 26 | module.exports = router -------------------------------------------------------------------------------- /api/router/user_audio.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const data = { 7 | "userId": req.query.uid, 8 | "csrf_token": "" 9 | } 10 | console.log(data) 11 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 12 | 13 | createWebAPIRequest( 14 | 'music.163.com', 15 | '/weapi/djradio/get/byuser', 16 | 'POST', 17 | data, 18 | cookie, 19 | music_req => { 20 | res.setHeader("Content-Type", "application/json") 21 | res.send(music_req) 22 | }, 23 | err => { 24 | res.status(502).send('fetch error') 25 | } 26 | ) 27 | }) 28 | 29 | module.exports = router -------------------------------------------------------------------------------- /api/router/user_cloud.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const data = { 7 | limit: req.query.limit || 10, 8 | offset: req.query.offset || 0, 9 | "csrf_token": "" 10 | } 11 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | '/weapi/v1/cloud/get', 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => { 19 | res.setHeader("Content-Type", "application/json") 20 | res.send(music_req) 21 | }, 22 | err => { 23 | res.status(502).send('fetch error') 24 | } 25 | ) 26 | }) 27 | 28 | 29 | module.exports = router -------------------------------------------------------------------------------- /api/router/user_cloud_search.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const data = { 7 | byids: req.query.id, 8 | id: req.query.id, 9 | "csrf_token": "" 10 | } 11 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | '/weapi/v1/cloud/get/byids', 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => { 19 | res.setHeader("Content-Type", "application/json") 20 | res.send(music_req) 21 | }, 22 | err => { 23 | res.status(502).send('fetch error') 24 | } 25 | ) 26 | }) 27 | 28 | 29 | module.exports = router -------------------------------------------------------------------------------- /api/router/user_detail.js: -------------------------------------------------------------------------------- 1 | // 用户详情 2 | const express = require("express") 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | router.get("/", (req, res) => { 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const id = req.query.uid 9 | const data = { 10 | "csrf_token": "" 11 | } 12 | createWebAPIRequest( 13 | 'music.163.com', 14 | `/api/v1/user/detail/${id}`, 15 | 'POST', 16 | data, 17 | cookie, 18 | music_req => { 19 | res.send(music_req) 20 | }, 21 | err => res.status(502).send('fetch error') 22 | ) 23 | }) 24 | 25 | module.exports = router -------------------------------------------------------------------------------- /api/router/user_dj.js: -------------------------------------------------------------------------------- 1 | // 用户电台 2 | const express = require("express") 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | router.get("/", (req, res) => { 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | const id = req.query.uid 9 | const data = { 10 | 'offset': req.query.offset || '0', 11 | 'limit': req.query.limit || 30, 12 | "csrf_token": "" 13 | } 14 | createWebAPIRequest( 15 | 'music.163.com', 16 | `/weapi/dj/program/${id}`, 17 | 'POST', 18 | data, 19 | cookie, 20 | music_req => { 21 | res.send(music_req) 22 | }, 23 | err => res.status(502).send('fetch error') 24 | ) 25 | }) 26 | 27 | module.exports = router -------------------------------------------------------------------------------- /api/router/user_event.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const id = req.query.uid 8 | const data = { 9 | 'time': -1, 10 | 'getcounts': true, 11 | "csrf_token": "" 12 | } 13 | createWebAPIRequest( 14 | 'music.163.com', 15 | `/weapi/event/get/${id}`, 16 | 'POST', 17 | data, 18 | cookie, 19 | music_req => { 20 | res.send(music_req) 21 | }, 22 | err => res.status(502).send('fetch error') 23 | ) 24 | }) 25 | 26 | module.exports = router -------------------------------------------------------------------------------- /api/router/user_followeds.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | 'userId': req.query.uid, 9 | offset: req.query.offset || '0', 10 | limit: req.query.limit || 30, 11 | "csrf_token": "" 12 | } 13 | createWebAPIRequest( 14 | 'music.163.com', 15 | `/weapi/user/getfolloweds/`, 16 | 'POST', 17 | data, 18 | cookie, 19 | music_req => { 20 | res.send(music_req) 21 | }, 22 | err => res.status(502).send('fetch error') 23 | ) 24 | }) 25 | 26 | module.exports = router -------------------------------------------------------------------------------- /api/router/user_follows.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const id = req.query.uid 8 | const data = { 9 | offset: req.query.offset || '0', 10 | limit: req.query.limit || 30, 11 | order: true 12 | } 13 | createWebAPIRequest( 14 | 'music.163.com', 15 | `/weapi/user/getfollows/${id}`, 16 | 'POST', 17 | data, 18 | cookie, 19 | music_req => { 20 | res.send(music_req) 21 | }, 22 | err => res.status(502).send('fetch error') 23 | ) 24 | }) 25 | 26 | module.exports = router -------------------------------------------------------------------------------- /api/router/user_playlist.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "offset": 0, 9 | "uid": req.query.uid, 10 | "limit": 1000, 11 | "csrf_token": "" 12 | } 13 | createWebAPIRequest( 14 | 'music.163.com', 15 | '/weapi/user/playlist', 16 | 'POST', 17 | data, 18 | cookie, 19 | music_req => res.send(music_req), 20 | err => res.status(502).send('fetch error') 21 | ) 22 | }) 23 | 24 | module.exports = router -------------------------------------------------------------------------------- /api/router/user_playrecord.js: -------------------------------------------------------------------------------- 1 | //播放记录 2 | const express = require("express") 3 | const router = express() 4 | const { createWebAPIRequest } = require("../util/util") 5 | 6 | router.get("/", (req, res) => { 7 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 8 | 9 | // type=1时只返回weekData, type=0时返回allData 10 | const data = { 11 | 'type': req.query.type || 0, 12 | uid: req.query.uid, //用户 id, 13 | "csrf_token": "" 14 | } 15 | const action = `/weapi/v1/play/record` 16 | createWebAPIRequest( 17 | 'music.163.com', 18 | action, 19 | 'POST', 20 | data, 21 | cookie, 22 | music_req => res.send(music_req), 23 | err => res.status(502).send('fetch error') 24 | ) 25 | }) 26 | 27 | 28 | 29 | module.exports = router -------------------------------------------------------------------------------- /api/router/user_subcount.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express() 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | router.get("/", (req, res) => { 6 | const cookie = req.get('Cookie') ? req.get('Cookie') : '' 7 | const data = { 8 | "csrf_token": "" 9 | } 10 | createWebAPIRequest( 11 | 'music.163.com', 12 | '/weapi/subcount', 13 | 'POST', 14 | data, 15 | cookie, 16 | music_req => res.send(music_req), 17 | err => res.status(502).send('fetch error') 18 | ) 19 | }) 20 | 21 | module.exports = router -------------------------------------------------------------------------------- /api/test/album.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const crypto = require('crypto') 3 | const { createRequest } = require("../util/util") 4 | 5 | describe('测试获取歌手专辑列表是否正常', () => { 6 | it('数据的 code 应该为200', done => { 7 | const id = 32311 8 | createRequest(`/api/album/${id}`, 'GET', null) 9 | .then(result => { 10 | const code = JSON.parse(result).code 11 | console.log("code:" + code) 12 | assert(code === 200) 13 | done() 14 | }) 15 | .catch(err => done(err)) 16 | }) 17 | }) -------------------------------------------------------------------------------- /api/test/comment.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const crypto = require('crypto') 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | describe('测试获取评论是否正常', () => { 6 | it('数据的 code 应该为200', done => { 7 | const rid = 32311 8 | const cookie = '' 9 | const data = { 10 | "offset": 0, 11 | "rid": rid, 12 | "limit": 20, 13 | "csrf_token": "" 14 | } 15 | createWebAPIRequest( 16 | 'music.163.com', 17 | `/weapi/v1/resource/comments/R_SO_4_${rid}/?csrf_token=`, 18 | 'POST', 19 | data, 20 | cookie, 21 | music_req => { 22 | console.log({ 23 | code:JSON.parse(music_req).code 24 | }) 25 | assert(JSON.parse(music_req).code === 200) 26 | done() 27 | }, 28 | err => done(err) 29 | ) 30 | }) 31 | }) -------------------------------------------------------------------------------- /api/test/login.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const crypto = require('crypto') 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | console.log("注意:测试登陆需要替换这里的账号密码!!!") 6 | 7 | describe('测试登录是否正常', () => { 8 | it('手机登录 code 应该等于200', done => { 9 | const phone = "换成你的手机号" 10 | const password = "换成你的密码" 11 | let cookie = '' 12 | const md5sum = crypto.createHash('md5') 13 | md5sum.update(password) 14 | const data = { 15 | 'phone': phone, 16 | 'password': md5sum.digest('hex'), 17 | 'rememberLogin': 'true' 18 | } 19 | 20 | createWebAPIRequest( 21 | 'music.163.com', 22 | '/weapi/login/cellphone', 23 | 'POST', 24 | data, 25 | cookie, 26 | (music_req, cookie) => { 27 | const result = JSON.parse(music_req) 28 | console.log({ 29 | loginType: result.loginType, 30 | code: result.code, 31 | account: result.account 32 | }) 33 | assert(result.code === 200) 34 | done() 35 | }, 36 | err => done(err) 37 | ) 38 | }) 39 | 40 | it('邮箱登录 code 应该等于200', done => { 41 | const email = "换成你的163网易邮箱" 42 | const password = "换成你的密码" 43 | const cookie = '' 44 | const md5sum = crypto.createHash('md5') 45 | md5sum.update(password) 46 | const data = { 47 | 'username': email, 48 | 'password': md5sum.digest('hex'), 49 | 'rememberLogin': 'true' 50 | } 51 | 52 | createWebAPIRequest( 53 | 'music.163.com', 54 | '/weapi/login', 55 | 'POST', 56 | data, 57 | cookie, 58 | (music_req, cookie) => { 59 | const result = JSON.parse(music_req) 60 | console.log({ 61 | loginType: result.loginType, 62 | code: result.code, 63 | account: result.account 64 | }) 65 | assert(result.code === 200) 66 | done() 67 | }, 68 | err => done(err) 69 | ) 70 | }) 71 | }) -------------------------------------------------------------------------------- /api/test/lyric.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const crypto = require('crypto') 3 | const { createRequest } = require("../util/util") 4 | 5 | describe('测试获取歌词是否正常', () => { 6 | it('数据应该有 lrc 字段', done => { 7 | const id = 347230 8 | createRequest('/api/song/lyric?os=osx&id=' + id + '&lv=-1&kv=-1&tv=-1', 'GET', null) 9 | .then(result => { 10 | console.log(JSON.parse(result).lrc) 11 | assert(typeof JSON.parse(result).lrc!=='undefined') 12 | done() 13 | }) 14 | .catch(err => { 15 | done(err) 16 | }) 17 | }) 18 | }) -------------------------------------------------------------------------------- /api/test/musicUrl.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const crypto = require('crypto') 3 | const { createWebAPIRequest } = require("../util/util") 4 | 5 | describe('测试获取歌曲是否正常', () => { 6 | it('歌曲的 url 不应该为空', done => { 7 | const id = 347230 8 | const br = 999000 9 | const data = { 10 | "ids": [id], 11 | "br": br, 12 | "csrf_token": "" 13 | } 14 | const cookie = '' 15 | 16 | createWebAPIRequest( 17 | 'music.163.com', 18 | '/weapi/song/enhance/player/url', 19 | 'POST', 20 | data, 21 | cookie, 22 | music_req => { 23 | console.log(JSON.parse(music_req).data[0].url) 24 | assert(!!JSON.parse(music_req).data[0].url) 25 | done() 26 | }, 27 | err => { 28 | done(err) 29 | } 30 | ) 31 | }) 32 | }) -------------------------------------------------------------------------------- /api/test/search.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const crypto = require('crypto') 3 | const { createRequest } = require("../util/util") 4 | 5 | describe('测试搜索是否正常', () => { 6 | it('获取到的数据的 name 应该和搜索关键词一致', done => { 7 | const keywords = "海阔天空" 8 | const type = 1 9 | const limit = 30 10 | const data = 's=' + keywords + '&limit=' + limit + '&type=' + type + '&offset=0' 11 | createRequest('/api/search/pc/', 'POST', data) 12 | .then(result => { 13 | console.log(JSON.parse(result).result.songs[0].mp3Url) 14 | assert(JSON.parse(result).result.songs[0].name === '海阔天空') 15 | done() 16 | }) 17 | .catch(err => { 18 | done(err) 19 | }) 20 | }) 21 | }) -------------------------------------------------------------------------------- /api/util/crypto.js: -------------------------------------------------------------------------------- 1 | // 参考 https://github.com/darknessomi/musicbox/wiki/ 2 | 'use strict' 3 | const crypto = require('crypto') 4 | const bigInt = require('big-integer') 5 | const modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7' 6 | const nonce = '0CoJUm6Qyw8W8jud' 7 | const pubKey = '010001' 8 | 9 | String.prototype.hexEncode = function(){ 10 | let hex, i 11 | 12 | let result = "" 13 | for (i=0; i { 4 | /** 5 | * Reload browser when HTMLWebpackPlugin emits a new index.html 6 | */ 7 | if (event.action === 'reload') { 8 | window.location.reload() 9 | } 10 | 11 | /** 12 | * Notify `mainWindow` when `main` process is compiling, 13 | * giving notice for an expected reload of the `electron` process 14 | */ 15 | if (event.action === 'compiling') { 16 | document.body.innerHTML += ` 17 | 30 | 31 |
32 | Compiling Main Process... 33 |
34 | ` 35 | } 36 | }) 37 | -------------------------------------------------------------------------------- /config/webpack.main.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.BABEL_ENV = 'main' 4 | 5 | const path = require('path') 6 | const { dependencies } = require('../package.json') 7 | const webpack = require('webpack') 8 | 9 | const BabiliWebpackPlugin = require('babili-webpack-plugin') 10 | 11 | let mainConfig = { 12 | entry: { 13 | main: path.join(__dirname, '../src/main/index.js') 14 | }, 15 | externals: [ 16 | ...Object.keys(dependencies || {}) 17 | ], 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.(js)$/, 22 | enforce: 'pre', 23 | exclude: /node_modules/, 24 | use: { 25 | loader: 'eslint-loader', 26 | options: { 27 | formatter: require('eslint-friendly-formatter') 28 | } 29 | } 30 | }, 31 | { 32 | test: /\.js$/, 33 | use: 'babel-loader', 34 | exclude: /node_modules/ 35 | }, 36 | { 37 | test: /\.node$/, 38 | use: 'node-loader' 39 | } 40 | ] 41 | }, 42 | node: { 43 | __dirname: process.env.NODE_ENV !== 'production', 44 | __filename: process.env.NODE_ENV !== 'production' 45 | }, 46 | output: { 47 | filename: '[name].js', 48 | libraryTarget: 'commonjs2', 49 | path: path.join(__dirname, '../dist/electron') 50 | }, 51 | plugins: [ 52 | new webpack.NoEmitOnErrorsPlugin() 53 | ], 54 | resolve: { 55 | extensions: ['.js', '.json', '.node'] 56 | }, 57 | target: 'electron-main' 58 | } 59 | 60 | /** 61 | * Adjust mainConfig for development settings 62 | */ 63 | if (process.env.NODE_ENV !== 'production') { 64 | mainConfig.plugins.push( 65 | new webpack.DefinePlugin({ 66 | '__static': `"${path.join(__dirname, '../static')}"` 67 | }) 68 | ) 69 | } 70 | 71 | /** 72 | * Adjust mainConfig for production settings 73 | */ 74 | if (process.env.NODE_ENV === 'production') { 75 | mainConfig.plugins.push( 76 | new BabiliWebpackPlugin(), 77 | new webpack.DefinePlugin({ 78 | 'process.env.NODE_ENV': '"production"' 79 | }) 80 | ) 81 | } 82 | 83 | module.exports = mainConfig 84 | -------------------------------------------------------------------------------- /doc/1588237435-599597952042d_articlex.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eugeneCN/vue-electron-music/896f902ce93ff3537228f8f852b305bad8514195/doc/1588237435-599597952042d_articlex.jpeg -------------------------------------------------------------------------------- /doc/3872766167-5999ac798b713_articlex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eugeneCN/vue-electron-music/896f902ce93ff3537228f8f852b305bad8514195/doc/3872766167-5999ac798b713_articlex.png -------------------------------------------------------------------------------- /doc/3913213729-5999ac98a8c6c_articlex.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eugeneCN/vue-electron-music/896f902ce93ff3537228f8f852b305bad8514195/doc/3913213729-5999ac98a8c6c_articlex.jpeg -------------------------------------------------------------------------------- /src/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vue-electron-music 6 | <% if (htmlWebpackPlugin.options.nodeModules) { %> 7 | 8 | 11 | <% } %> 12 | 13 | 14 |
15 | 16 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/index.dev.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is used specifically and only for development. It installs 3 | * `electron-debug` & `vue-devtools`. There shouldn't be any need to 4 | * modify this file, but it can be used to extend your development 5 | * environment. 6 | */ 7 | 8 | /* eslint-disable */ 9 | 10 | // Set environment for development 11 | process.env.NODE_ENV = 'development' 12 | 13 | // Install `electron-debug` with `devtron` 14 | require('electron-debug')({ showDevTools: true }) 15 | 16 | // Install `vue-devtools` 17 | require('electron').app.on('ready', () => { 18 | let installExtension = require('electron-devtools-installer') 19 | installExtension.default(installExtension.VUEJS_DEVTOOLS) 20 | .then(() => {}) 21 | .catch(err => { 22 | console.log('Unable to install `vue-devtools`: \n', err) 23 | }) 24 | }) 25 | 26 | // Require `main` process to boot app 27 | require('./index') 28 | -------------------------------------------------------------------------------- /src/main/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import { app, BrowserWindow } from 'electron' 4 | import { command } from './command' 5 | 6 | /** 7 | * Set `__static` path to static files in production 8 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html 9 | */ 10 | if (process.env.NODE_ENV !== 'development') { 11 | global.__static = require('path').join(__dirname, '/static') 12 | } 13 | 14 | let mainWindow 15 | const winURL = process.env.NODE_ENV === 'development' ? `http://localhost:9080` : `file://${__dirname}/index.html` 16 | 17 | function createWindow() { 18 | /** 19 | * Initial window options 20 | */ 21 | const win = new BrowserWindow({ 22 | height: 670, 23 | minHieght: 670, 24 | width: 1000, 25 | minWidth: 1000, 26 | useContentSize: true, 27 | transparent: false, 28 | frame: false, 29 | darkTheme: true, 30 | backgroundColor: '#FFFFFFFF' 31 | }) 32 | 33 | win.loadURL(winURL) 34 | 35 | win.on('closed', () => { 36 | mainWindow = null 37 | }) 38 | 39 | return win 40 | } 41 | 42 | app.on('ready', () => { 43 | mainWindow = createWindow() 44 | command(mainWindow) 45 | }) 46 | 47 | app.on('window-all-closed', () => { 48 | if (process.platform !== 'darwin') { 49 | app.quit() 50 | } 51 | }) 52 | 53 | app.on('activate', () => { 54 | if (mainWindow === null) { 55 | createWindow() 56 | } 57 | }) 58 | -------------------------------------------------------------------------------- /src/renderer/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /src/renderer/api/api.js: -------------------------------------------------------------------------------- 1 | import { config } from './config.js' 2 | 3 | export default { 4 | loginCellphone: config + '/login/cellphone', // 手机登陆 5 | userPlaylist: config + '/user/playlist', // 获取用户歌单 6 | playlistDetail: config + '/playlist/detail', // 获取歌单详情 7 | musicUrl: config + '/music/url', // 获取音乐url 8 | search: config + '/search', // 搜素音乐 9 | lyric: config + '/lyric', // 获取歌词 10 | comment: config + '/comment', // 获取评论 11 | album: config + '/album', // 获取专辑内容 12 | artists: config + '/artists', // 获取歌手单曲列表 13 | artistAlbum: config + '/artist/album', // 获取歌手专辑列表 14 | artistDesc: config + '/artist/desc', // 获取歌手信息 15 | recommendResource: '/recommend/resource', // 获取每日推荐歌单(用户级) 16 | recommendSongs: config + '/recommend/songs', // 获取每日推荐歌曲(用户级) 17 | personalFm: config + '/personal_fm', // 私人FM(用户级) 18 | dailySignin: config + '/daily_signin', // 签到(用户级) 19 | likeMusic: config + '/like', // 添加喜欢的音乐(用户级) 20 | fmTrash: config + '/fm_trash', // 将音乐从私人FM中移除至垃圾桶 21 | topPlaylist: config + '/top/playlist', // 歌单(网友精选歌单) 22 | newAlbum: config + '/top/albums', // 新碟上架 23 | topArtists: config + '/top/artists', // 热门歌手 24 | topList: config + '/top/list', // 音乐排行榜 25 | personalized: config + '/personalized', // 推荐歌单 26 | privatecontent: config + '/personalized/privatecontent', // 独家放送 27 | personalizedMv: config + '/personalized/mv' // 推荐MV 28 | } 29 | -------------------------------------------------------------------------------- /src/renderer/api/config.js: -------------------------------------------------------------------------------- 1 | export const config = 'http://localhost:3000' 2 | -------------------------------------------------------------------------------- /src/renderer/assets/scss/common.scss: -------------------------------------------------------------------------------- 1 | /*! minireset.css v0.0.3 | MIT License | github.com/jgthms/minireset.css */ 2 | blockquote, 3 | body, 4 | dd, 5 | dl, 6 | dt, 7 | fieldset, 8 | figure, 9 | h1, 10 | h2, 11 | h3, 12 | h4, 13 | h5, 14 | h6, 15 | hr, 16 | html, 17 | iframe, 18 | legend, 19 | li, 20 | ol, 21 | p, 22 | pre, 23 | textarea, 24 | ul { 25 | margin: 0; 26 | padding: 0; 27 | } 28 | 29 | h1, 30 | h2, 31 | h3, 32 | h4, 33 | h5, 34 | h6 { 35 | font-size: 100%; 36 | font-weight: normal; 37 | } 38 | 39 | ul { 40 | list-style: none; 41 | } 42 | 43 | button, 44 | input, 45 | select, 46 | textarea { 47 | margin: 0; 48 | } 49 | 50 | html { 51 | box-sizing: border-box; 52 | } 53 | *, 54 | *:after, 55 | *:before { 56 | box-sizing: inherit; 57 | outline: inherit; 58 | } 59 | 60 | audio, 61 | embed, 62 | iframe, 63 | img, 64 | object, 65 | video { 66 | height: auto; 67 | max-width: 100%; 68 | } 69 | 70 | iframe { 71 | border: 0; 72 | } 73 | 74 | table { 75 | border-collapse: collapse; 76 | border-spacing: 0; 77 | } 78 | 79 | td, 80 | th { 81 | padding: 0; 82 | text-align: left; 83 | } 84 | body { 85 | color: #333; 86 | font-family: Arial, Helvetica, sans-serif; 87 | } 88 | 89 | body, 90 | html { 91 | overflow: hidden; 92 | font-size: 100%; 93 | height: 100%; 94 | } 95 | 96 | .ellipsis { 97 | overflow: hidden; 98 | text-overflow: ellipsis; 99 | white-space: nowrap; 100 | } 101 | 102 | .wordwrap { 103 | word-wrap: break-word; 104 | word-break: break-all; 105 | } 106 | -------------------------------------------------------------------------------- /src/renderer/assets/scss/function.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | //----------------------------------------------------- 3 | // function scss 4 | //----------------------------------------------------- 5 | // 导入所有功能类相关文件 6 | //----------------------------------------------------- 7 | @import "core/setting"; 8 | @import "core/css3"; 9 | @import "core/mixin"; 10 | @import "core/grid"; 11 | -------------------------------------------------------------------------------- /src/renderer/assets/scss/var.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/function.scss"; 2 | /**/ 3 | //----------------------------------------------------- 4 | // var scss 2017年07月04日19:36:15 5 | //----------------------------------------------------- 6 | // ---------------------------------------------------- 7 | // Colors 8 | //----------------------------------------------------- 9 | $primary: #20a0ff; 10 | $success: #13ce66; 11 | $warning: #f7ba2a; 12 | $danger: #ff4949; 13 | $info: #50BFFF; 14 | $red: #b72712; 15 | $red-light: #d43c33; 16 | $red-lighter: rgba($red, 0.12); 17 | $white: #fafafa; 18 | $black: #000; 19 | $grey: #636363; 20 | $grey-main: #f5f5f5; 21 | $grey-light: #c5c5c5; 22 | $grey-lighter: #d1d1d1; 23 | /**/ 24 | // ---------------------------------------------------- 25 | // Font Colors 26 | //----------------------------------------------------- 27 | $font-light: #999999; 28 | $font: #636363; 29 | $font-dark: #5e6a76; 30 | $font-darker: #222222; 31 | $font-gary: #bbbbbb; 32 | $font-red: #d43c33; 33 | 34 | /**/ 35 | // ---------------------------------------------------- 36 | // Font size 37 | //----------------------------------------------------- 38 | $font-size-big: 18px; 39 | $font-size-large: 16px; 40 | $font-size-head: 13px; 41 | $font-size-base: 12px; 42 | /**/ 43 | // ---------------------------------------------------- 44 | // bar Width 45 | //----------------------------------------------------- 46 | $nav-width: 200px; 47 | $head-height: 60px; 48 | $play-bar-height: 65px; 49 | 50 | /**/ 51 | // ---------------------------------------------------- 52 | // 主体混合函数 53 | //----------------------------------------------------- 54 | @mixin min-height($height) { 55 | min-height: $height; 56 | height: auto; 57 | } 58 | @mixin line-height($height) { 59 | height: $height; 60 | line-height: $height; 61 | } 62 | @mixin case($box) { 63 | width: $box; 64 | height: $box; 65 | } 66 | @mixin horizontal-center { 67 | margin-left: auto; 68 | margin-right: auto; 69 | } 70 | @mixin position-all { 71 | position: absolute; 72 | top: 0; 73 | right: 0; 74 | bottom: 0; 75 | left: 0; 76 | } 77 | @mixin transition-set($args) { 78 | -webkit-transition: $args; 79 | transition: $args; 80 | } 81 | -------------------------------------------------------------------------------- /src/renderer/assets/style/app.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | .bar { 3 | position: absolute; 4 | right: 0; 5 | left: 0; 6 | z-index: 100; 7 | height: $head-height; 8 | background-color: #f7f7f8; 9 | -webkit-backface-visibility: hidden; 10 | backface-visibility: hidden; 11 | } 12 | .bar-header { 13 | top: 0; 14 | } 15 | .bar-footer { 16 | bottom: 0; 17 | } 18 | .views { 19 | @include position-all; 20 | overflow: hidden; 21 | -webkit-overflow-scrolling: touch; 22 | } 23 | .bar-footer ~ .views { 24 | bottom: $play-bar-height; 25 | } 26 | .bar-header ~ .views { 27 | top: $head-height; 28 | } 29 | .page { 30 | overflow: hidden; 31 | position: relative; 32 | height: 100%; 33 | } 34 | .scrollBar { 35 | overflow-y: scroll !important; 36 | position: relative; 37 | top: 0; 38 | left: 0; 39 | right: 0; 40 | height: 100%; 41 | z-index: 500; 42 | } 43 | ::-webkit-scrollbar-track { 44 | background-color: rgba(0,0,0,0.05); 45 | } 46 | 47 | ::-webkit-scrollbar { 48 | width: 5px; 49 | } 50 | 51 | ::-webkit-scrollbar-thumb { 52 | border-radius: 1000px; 53 | background-color: rgba(0,0,0,0.2); 54 | } 55 | ::-webkit-scrollbar-thumb:hover { 56 | background-color: rgba(0,0,0,0.3); 57 | } 58 | 59 | .radius-border { 60 | position: relative; 61 | } 62 | @media screen and (-webkit-min-device-pixel-ratio: 2) { 63 | .radius-border:before { 64 | content: ''; 65 | pointer-events: none; 66 | box-sizing: border-box; 67 | position: absolute; 68 | width: 200%; 69 | height: 200%; 70 | left: 0; 71 | top: 0; 72 | /* 改变这个值 100% 就是我们平常的border-radius 50% */ 73 | border-radius: 100%; 74 | border: 1px solid #999; 75 | transform: scale(0.5); 76 | transform-origin: 0 0; 77 | } 78 | } 79 | * { 80 | background-color: rgba(0,0,0, 0); 81 | } 82 | 83 | .btn { 84 | -webkit-app-region: no-drag; 85 | } 86 | .drag { 87 | -webkit-app-region: no-drag; 88 | } 89 | -------------------------------------------------------------------------------- /src/renderer/assets/style/instructViews.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | .instructViews { 3 | position: absolute; 4 | top: 0; 5 | left: 0; 6 | padding-left: $nav-width; 7 | width: 100%; 8 | height: 100%; 9 | z-index: 10; 10 | > div { 11 | position: relative; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/renderer/assets/style/table.scss: -------------------------------------------------------------------------------- 1 | .table { 2 | /* Remove spacing between table cells (from Normalize.css) */ 3 | border-collapse: collapse; 4 | border-spacing: 0; 5 | empty-cells: show; 6 | border: 1px solid #cbcbcb; 7 | } 8 | 9 | .table caption { 10 | color: #000; 11 | font: italic 85%/1 arial, sans-serif; 12 | padding: 1em 0; 13 | text-align: center; 14 | } 15 | 16 | .table td, 17 | .table th { 18 | border-left: 1px solid #cbcbcb; 19 | /* inner column border */ 20 | border-width: 0 0 0 1px; 21 | font-size: inherit; 22 | margin: 0; 23 | overflow: visible; 24 | /*to make ths where the title is really long work*/ 25 | padding: 0.5em 1em; 26 | /* cell padding */ 27 | } 28 | /* Consider removing this next declaration block, as it causes problems when 29 | there's a rowspan on the first cell. Case added to the tests. issue#432 */ 30 | .table td:first-child, 31 | .table th:first-child { 32 | border-left-width: 0; 33 | } 34 | 35 | .table thead { 36 | background-color: #e0e0e0; 37 | color: #000; 38 | text-align: left; 39 | vertical-align: bottom; 40 | } 41 | /* 42 | striping: 43 | even - #fff (white) 44 | odd - #f2f2f2 (light gray) 45 | */ 46 | .table td { 47 | background-color: transparent; 48 | } 49 | 50 | .table-odd td { 51 | background-color: #f2f2f2; 52 | } 53 | /* nth-child selector for modern browsers */ 54 | .table-striped tr:nth-child(2n-1) td { 55 | background-color: #f2f2f2; 56 | } 57 | /* BORDERED TABLES */ 58 | .table-bordered td { 59 | border-bottom: 1px solid #cbcbcb; 60 | } 61 | 62 | .table-bordered tbody > tr:last-child > td { 63 | border-bottom-width: 0; 64 | } 65 | /* HORIZONTAL BORDERED TABLES */ 66 | .table-horizontal td, 67 | .table-horizontal th { 68 | border-width: 0 0 1px 0; 69 | border-bottom: 1px solid #cbcbcb; 70 | } 71 | 72 | .table-horizontal tbody > tr:last-child > td { 73 | border-bottom-width: 0; 74 | } 75 | -------------------------------------------------------------------------------- /src/renderer/components/common/404/404.scss: -------------------------------------------------------------------------------- 1 | 2 | .page-404 { 3 | height: 100%; 4 | text-align: center; 5 | .box { 6 | width: 200px; 7 | height: 200px; 8 | position: absolute; 9 | left: 50%; 10 | top: 50%; 11 | margin-left: -100px; 12 | margin-top: -100px; 13 | } 14 | .background { 15 | background: url('./assets/404.svg') no-repeat; 16 | width: 128px; 17 | height: 128px; 18 | opacity: 0.6; 19 | margin: 10px auto; 20 | } 21 | p { 22 | color: #777; 23 | font-size: 14px; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/renderer/components/common/404/404.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | -------------------------------------------------------------------------------- /src/renderer/components/common/banner/index.js: -------------------------------------------------------------------------------- 1 | import loading from './loading' 2 | 3 | export default { 4 | install: function (Vue) { 5 | Vue.component('v-loading', loading) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/renderer/components/common/barButton/barButton.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | 3 | .header-bar-button { 4 | position: relative; 5 | font-size: 0; 6 | z-index: 200; 7 | padding: 3px 5px; 8 | .btn { 9 | -webkit-app-region: no-drag; 10 | display: inline-block; 11 | position: relative; 12 | padding: 0; 13 | border: none; 14 | box-shadow: 0 0 1px rgba(0,0,0,0.3); 15 | @include case(12px); 16 | border-radius: 50%; 17 | margin: 0 4px; 18 | cursor: pointer; 19 | &.closes { 20 | background-color: #fc625d; 21 | } 22 | &.minimize { 23 | background-color: #fdbc40; 24 | } 25 | &.maximize { 26 | background-color: #34c84a; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/renderer/components/common/barButton/barButton.vue: -------------------------------------------------------------------------------- 1 | 10 | 34 | 37 | -------------------------------------------------------------------------------- /src/renderer/components/common/dropdown/dropdown.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | .dropdownList { 3 | 4 | .user-dropdown { 5 | .arrow { 6 | display: inline-block; 7 | font-size: 0; 8 | margin: 0 7px; 9 | position: absolute; 10 | top: 0; 11 | right: 0; 12 | &:after, 13 | &:before { 14 | display: inline-block; 15 | content: ''; 16 | width: 0; 17 | height: 0; 18 | position: absolute; 19 | top: 0; 20 | right: 0; 21 | border-left: 8px solid transparent; 22 | border-right: 8px solid transparent; 23 | } 24 | &:after { 25 | border-bottom: 7px solid #c3c3c3; 26 | margin-top: -7px; 27 | } 28 | &:before { 29 | border-bottom: 7px solid $white; 30 | margin-top: -6px; 31 | z-index: 1; 32 | } 33 | } 34 | position: absolute; 35 | width: 250px; 36 | box-shadow: 0 0 10px rgba(0,0,0,0.3); 37 | background-color: $white; 38 | top: 0; 39 | right: 0; 40 | margin-top: $head-height; 41 | margin-right: 20px; 42 | z-index: 100; 43 | border-radius: 5px; 44 | border: 1px solid #c3c3c3; 45 | li { 46 | .user { 47 | padding: 15px 20px; 48 | img { 49 | display: inline-block; 50 | @include case(40px); 51 | border-radius: 50%; 52 | vertical-align: middle; 53 | border: 1px solid $grey-main; 54 | } 55 | span { 56 | display: inline-block; 57 | @include line-height(40px); 58 | vertical-align: middle; 59 | font-size: 14px; 60 | margin: 0 10px; 61 | color: $font; 62 | } 63 | } 64 | 65 | .item { 66 | padding: 8px 20px; 67 | border-top: 1px solid rgba($grey-lighter,0.5); 68 | a { 69 | display: block; 70 | text-align: center; 71 | text-decoration: none; 72 | font-size: 14px; 73 | color: $font; 74 | } 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/renderer/components/common/dropdown/dropdown.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 67 | 70 | -------------------------------------------------------------------------------- /src/renderer/components/common/header/assets/dropdown-a.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/header/assets/dropdown.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/header/assets/i-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/header/assets/i-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/header/assets/icon-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/header/assets/icon-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/header/assets/so.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/loading/index.js: -------------------------------------------------------------------------------- 1 | import loading from './loading' 2 | 3 | export default { 4 | install: function (Vue) { 5 | Vue.component('v-loading', loading) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/renderer/components/common/loading/loading.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 28 | 29 | 34 | -------------------------------------------------------------------------------- /src/renderer/components/common/pagination/assets/i-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/pagination/assets/i-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/pagination/index.js: -------------------------------------------------------------------------------- 1 | import pagination from './pagination' 2 | 3 | export default { 4 | install: function (Vue) { 5 | Vue.component('v-pagination', pagination) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/renderer/components/common/pagination/pagination.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | .pager { 3 | text-align: center; 4 | font-size: 14px; 5 | margin: 30px 0; 6 | height: 24px; 7 | } 8 | 9 | .btn-pager { 10 | font-size: 0; 11 | padding: 0; 12 | margin: 0 5px; 13 | width: 26px; 14 | height: 24px; 15 | text-align: center; 16 | background-color: #fafafa; 17 | color: $black; 18 | border: 1px solid $grey-lighter; 19 | border-radius: 4px; 20 | &:after { 21 | content: ''; 22 | display: inline-block; 23 | @include case(10px); 24 | } 25 | &.prev:after { 26 | background: url('./assets/i-left.svg') no-repeat; 27 | background-size: 100%; 28 | } 29 | &.next:after { 30 | background: url('./assets/i-right.svg') no-repeat; 31 | background-size: 100%; 32 | } 33 | &:active { 34 | box-shadow: none; 35 | background-color: #f9f9f9; 36 | border-color: $grey-light; 37 | } 38 | } 39 | 40 | .page-index { 41 | margin: 0 4px; 42 | display: inline-block; 43 | @include case(24px); 44 | line-height: 24px; 45 | background-color: #fafafa; 46 | cursor: pointer; 47 | color: $black; 48 | } 49 | 50 | .active { 51 | color: $red; 52 | text-decoration: underline; 53 | } 54 | .btn[disabled] { 55 | border: 1px solid $grey-lighter; 56 | } 57 | -------------------------------------------------------------------------------- /src/renderer/components/common/playDetail/api.js: -------------------------------------------------------------------------------- 1 | import { ajax } from '@/utils/libs' 2 | import { config } from '@/api/config.js' 3 | /** 4 | * 获取歌词 5 | * @param {Object} param 参数 6 | * @param {Function} callback 返回函数 7 | * @param return 8 | */ 9 | export default { 10 | loadLyric(param, callback) { 11 | ajax(`${config}/lyric`, param, callback) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/renderer/components/common/playDetail/assets/close_1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playDetail/assets/close_2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playDetail/assets/close_3.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playDetail/assets/download.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playDetail/assets/like.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playDetail/assets/like_1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playDetail/assets/like_2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playDetail/assets/share_2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playList/assets/1clone.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playList/assets/clone.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playerBar/assets/for.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playerBar/assets/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playerBar/assets/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playerBar/assets/pause.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playerBar/assets/play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playerBar/assets/prev.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playerBar/assets/voice-close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/playerBar/assets/voice-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/sideBar/api.js: -------------------------------------------------------------------------------- 1 | import { ajax } from '@/utils/libs' 2 | import { config } from '@/api/config.js' 3 | /** 4 | * 获取用户歌单 5 | * @param {Object} param 参数 6 | * @param {Function} callback 返回函数 7 | * @param return 8 | */ 9 | export default { 10 | userPlay(param, callback) { 11 | ajax(`${config}/user/playlist`, param, callback) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/renderer/components/common/sideBar/assets/download.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/sideBar/assets/fm.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/sideBar/assets/friend.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/sideBar/assets/iTunes.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/sideBar/assets/likes.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/sideBar/assets/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/sideBar/assets/music.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/sideBar/assets/mv.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/sideBar/assets/shows.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/common/slider/slider.scss: -------------------------------------------------------------------------------- 1 | .mu-slider { 2 | width: 100%; 3 | position: relative; 4 | height: 24px; 5 | display: flex; 6 | align-items: center; 7 | cursor: default; 8 | user-select: none; 9 | outline: none; 10 | } 11 | 12 | .mu-slider-track { 13 | position: absolute; 14 | height: 4px; 15 | left: 0; 16 | right: 0; 17 | top: 50%; 18 | margin-top: -2px; 19 | background-color: #bbbfc4; 20 | border-radius: 1000px; 21 | } 22 | 23 | .mu-slider-fill { 24 | position: absolute; 25 | height: 4px; 26 | width: 100%; 27 | background-color: #d43c33; 28 | left: 0; 29 | top: 50%; 30 | margin-top: -2px; 31 | border-radius: 1000px; 32 | .mu-slider.disabled & { 33 | background-color: #bbbfc4; 34 | } 35 | } 36 | 37 | .mu-slider-thumb { 38 | position: absolute; 39 | top: 50%; 40 | width: 14px; 41 | height: 14px; 42 | background-color: #fff; 43 | color: #d43c33; 44 | border-radius: 50%; 45 | transform: translate(-50%, -50%); 46 | transition: background 450ms cubic-bezier(0.23, 1, 0.32, 1), border-color 450ms cubic-bezier(0.23, 1, 0.32, 1), width 450ms cubic-bezier(0.23, 1, 0.32, 1), height 450ms cubic-bezier(0.23, 1, 0.32, 1); 47 | cursor: pointer; 48 | box-shadow: 0 0 3px rgba(0,0,0,0.5); 49 | 50 | &::after { 51 | content: ''; 52 | font-size: 0; 53 | display: block; 54 | padding: 2px; 55 | border-radius: 8px; 56 | background-color: #d43c33; 57 | position: absolute; 58 | top: 50%; 59 | margin-left: 1px; 60 | transform: translate(100%, -50%); 61 | } 62 | } 63 | 64 | .volume { 65 | .mu-slider-track { 66 | background-color: #ddd; 67 | } 68 | .mu-slider-thumb { 69 | width: 10px; 70 | height: 10px; 71 | &::after { 72 | display: none; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/renderer/components/common/toast/index.js: -------------------------------------------------------------------------------- 1 | import toast from './toast' 2 | 3 | export default { 4 | install: function (Vue) { 5 | Vue.component('v-toast', toast) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/renderer/components/common/toast/toast.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | .toast-mask { 3 | position: fixed; 4 | z-index: 1024; 5 | top: 0; 6 | right: 0; 7 | bottom: 0; 8 | left: 0; 9 | font-size: 16px; 10 | background-color: transparent; 11 | pointer-events: none; 12 | } 13 | .toast { 14 | @include line-height(60px); 15 | padding: 0 24px; 16 | background-color: rgba(0,0,0,0.8); 17 | color: #fff; 18 | border-radius: 5px; 19 | width: auto; 20 | position: fixed; 21 | left: 50%; 22 | top: 50%; 23 | bottom: 0; 24 | transform: translate(-50%, -50%); 25 | } 26 | 27 | .toast-enter-active, 28 | .toast-leave-active { 29 | transition: opacity 0.2s; 30 | backface-visibility: hidden; 31 | } 32 | 33 | .toast-enter, 34 | .toast-leave-active { 35 | opacity: 0; 36 | } 37 | -------------------------------------------------------------------------------- /src/renderer/components/common/toast/toast.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 48 | 49 | 52 | -------------------------------------------------------------------------------- /src/renderer/components/common/windowTop/windowTop.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 21 | -------------------------------------------------------------------------------- /src/renderer/components/instructViews.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 35 | 38 | -------------------------------------------------------------------------------- /src/renderer/components/views/details/api.js: -------------------------------------------------------------------------------- 1 | import { ajax } from '@/utils/libs' 2 | import { config } from '@/api/config.js' 3 | /** 4 | * 获取歌单详情 5 | * @param {Object} param 参数 6 | * @param {Function} callback 返回函数 7 | * @param return 8 | */ 9 | export default { 10 | playDetails(param, callback) { 11 | ajax(`${config}/playlist/detail`, param, callback) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/renderer/components/views/details/details.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 50 | 51 | 60 | -------------------------------------------------------------------------------- /src/renderer/components/views/download/download.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/renderer/components/views/fm/fm.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/renderer/components/views/friend/friend.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/renderer/components/views/home/api.js: -------------------------------------------------------------------------------- 1 | import { ajax } from '@/utils/libs' 2 | import { config } from '@/api/config.js' 3 | /** 4 | * 获取banner 5 | * @param {Object} param 参数 6 | * @param {Function} callback 返回函数 7 | * @param return 8 | */ 9 | export default { 10 | banner(param, callback) { 11 | ajax(`${config}/banner`, param, callback) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/renderer/components/views/home/home.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | 3 | .home { 4 | .nav { 5 | @include line-height(42px); 6 | text-align: center; 7 | font-size: 0; 8 | border-bottom: 1px solid $grey-light; 9 | a { 10 | margin: 0 34px; 11 | display: inline-block; 12 | text-decoration: none; 13 | font-size: 14px; 14 | color: $font; 15 | &.active { 16 | color: $red; 17 | } 18 | } 19 | } 20 | .up { 21 | padding: 100px 0; 22 | text-align: center; 23 | color: $font-light; 24 | font-size: $font-size-base; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/renderer/components/views/home/home.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 44 | 45 | 48 | -------------------------------------------------------------------------------- /src/renderer/components/views/iTunes/iTunes.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | .iTunes { 3 | height: 100%; 4 | .headers { 5 | border-bottom: 1px solid $grey-lighter; 6 | height: 52px; 7 | line-height: 1; 8 | padding-top: 24px; 9 | padding-left: 40px; 10 | span { 11 | font-size: 20px; 12 | } 13 | small { 14 | font-size: $font-size-base; 15 | color: $font; 16 | } 17 | } 18 | .content { 19 | position: relative; 20 | text-align: center; 21 | font-weight: 500; 22 | width: 300px; 23 | height: 180px; 24 | position: absolute; 25 | top: 50%; 26 | left: 50%; 27 | margin-left: -150px; 28 | margin-top: -90px; 29 | p { 30 | color: $font-light; 31 | font-size: $font-size-head; 32 | margin-bottom: 5px; 33 | } 34 | a { 35 | cursor: pointer; 36 | display: inline-block; 37 | font-size: $font-size-base; 38 | margin: 10px; 39 | color: #2b77cd; 40 | } 41 | img { 42 | @include case(68px); 43 | margin-bottom: 10px; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/renderer/components/views/iTunes/iTunes.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 28 | 29 | 32 | -------------------------------------------------------------------------------- /src/renderer/components/views/login/api.js: -------------------------------------------------------------------------------- 1 | import { ajax } from '@/utils/libs' 2 | import { config } from '@/api/config.js' 3 | /** 4 | * 登录 5 | * @param {Object} param 参数 6 | * @param {Function} callback 返回函数 7 | * @param return 8 | */ 9 | export default { 10 | login(param, callback) { 11 | ajax(`${config}/login/cellphone`, param, callback) 12 | }, 13 | refresh(param, callback) { 14 | ajax(`${config}/login/refresh`, param, callback) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/renderer/components/views/login/assets/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eugeneCN/vue-electron-music/896f902ce93ff3537228f8f852b305bad8514195/src/renderer/components/views/login/assets/bg.png -------------------------------------------------------------------------------- /src/renderer/components/views/login/assets/clone.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/views/login/assets/mobile.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/views/login/assets/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/views/login/assets/phone.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/views/login/assets/yes.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/views/login/login.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | 3 | .form { 4 | background-color: #fafafa; 5 | height: 100%; 6 | header { 7 | height: 34px; 8 | } 9 | .bg { 10 | background: url('./assets/bg.png') no-repeat; 11 | width: 100%; 12 | background-size: 100%; 13 | height: 150px; 14 | } 15 | .form-group { 16 | margin: 0 auto; 17 | padding: 20px; 18 | width: 300px; 19 | border-radius: 5px; 20 | } 21 | 22 | .input-group { 23 | position: relative; 24 | .icon { 25 | display: inline-block; 26 | @include case(19px); 27 | position: absolute; 28 | left: 0; 29 | top: 0; 30 | } 31 | .phone { 32 | background: url('./assets/phone.svg') no-repeat; 33 | background-size: 100%; 34 | margin: 10px 5px; 35 | } 36 | .password { 37 | background: url('./assets/password.svg') no-repeat; 38 | background-size: 100%; 39 | margin: 9px 5px; 40 | } 41 | input { 42 | width: 100%; 43 | font-size: 12px; 44 | border: 1px solid #d9d9d9; 45 | border-radius: 5px 5px 0 0; 46 | height: 38px; 47 | padding-left: 30px; 48 | } 49 | &:nth-child(2) { 50 | input { 51 | margin-top: -1px; 52 | border-radius: 0 0 5px 5px; 53 | } 54 | margin-bottom: 20px; 55 | } 56 | } 57 | .button-group { 58 | button { 59 | font-size: 12px; 60 | background-color: #de3f42; 61 | border: none; 62 | width: 100%; 63 | height: 40px; 64 | color: #fff; 65 | font-size: 15px; 66 | &.on { 67 | opacity: 0.8; 68 | pointer-events: none; 69 | } 70 | } 71 | } 72 | 73 | .tip { 74 | color: #de3f42; 75 | line-height: 40px; 76 | font-size: $font-size-base; 77 | } 78 | } 79 | .closed { 80 | position: absolute; 81 | top: 0; 82 | left: 0; 83 | background: url('./assets/clone.svg') no-repeat; 84 | background-size: 100%; 85 | @include case(12px); 86 | border: 0; 87 | padding: 0; 88 | border-radius: 0; 89 | margin: 15px; 90 | &:active { 91 | background-color: rgba(0,0,0,0); 92 | box-shadow: none; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/renderer/components/views/login/login.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 81 | 82 | 85 | -------------------------------------------------------------------------------- /src/renderer/components/views/mv/mv.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/api.js: -------------------------------------------------------------------------------- 1 | import { ajax } from '@/utils/libs' 2 | import { config } from '@/api/config.js' 3 | /** 4 | * 搜索歌曲 5 | * @param {Object} param 参数 6 | * @param {Function} callback 返回函数 7 | * @param return 8 | */ 9 | export default { 10 | search(param, callback, error) { 11 | ajax(`${config}/search`, param, callback, error) 12 | }, 13 | loadUrl(param, callback, error) { 14 | ajax(`${config}/music/url`, param, callback, error) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/album/album.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | .album { 3 | li { 4 | padding: 10px 30px; 5 | a { 6 | display: block; 7 | @include line-height(40px); 8 | text-decoration: none; 9 | font-size: 0; 10 | } 11 | .coverImg { 12 | display: inline-block; 13 | @include case(40px); 14 | background-size: 100%; 15 | } 16 | .picUrl { 17 | @include case(40px); 18 | vertical-align: middle; 19 | border: 1px solid $grey-lighter; 20 | } 21 | .name { 22 | display: inline-block; 23 | font-size: $font-size-head; 24 | color: $black; 25 | vertical-align: middle; 26 | margin: 0 10px; 27 | } 28 | .album { 29 | margin: 0; 30 | font-size: $font-size-base; 31 | color: $grey; 32 | } 33 | &:nth-child(even) { 34 | background-color: #f4f4f4; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/album/album.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 54 | 55 | 58 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/album/assets/default.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/lyric/lyric.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/mv/assets/default.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/mv/mv.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | .mv { 3 | padding: 30px; 4 | width: 100%; 5 | .list { 6 | display: flex; 7 | justify-content: space-between; 8 | margin-bottom: 25px; 9 | &:last-child { 10 | margin-bottom: 0; 11 | } 12 | } 13 | .item { 14 | width: 150px; 15 | } 16 | .coverImg { 17 | display: flex; 18 | height: 90px; 19 | background-size: 100%; 20 | background-position: center center; 21 | position: relative; 22 | color: $white; 23 | font-size: $font-size-base; 24 | span { 25 | @include position-all; 26 | padding: 5px; 27 | } 28 | .playCount { 29 | bottom: auto; 30 | text-align: right; 31 | background: -webkit-gradient(linear,right center,center top,from(rgba(0,0,0,1)),to(rgba(0,0,0,0))); 32 | } 33 | .time { 34 | top: auto; 35 | bottom: 0; 36 | text-align: left; 37 | } 38 | } 39 | .desc { 40 | font-size: 14px; 41 | font-weight: normal; 42 | .name { 43 | display: inline-block; 44 | padding: 5px 0; 45 | } 46 | .artist { 47 | font-size: $font-size-base; 48 | color: $grey-light; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/radio/radio.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/singer/assets/default.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/singer/singer.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | .singer { 3 | li { 4 | padding: 10px 30px; 5 | a { 6 | display: block; 7 | @include line-height(40px); 8 | text-decoration: none; 9 | font-size: 0; 10 | } 11 | .coverImg { 12 | display: inline-block; 13 | @include case(40px); 14 | background-size: 100%; 15 | } 16 | .picUrl { 17 | @include case(40px); 18 | vertical-align: middle; 19 | border: 1px solid $grey-lighter; 20 | } 21 | .name { 22 | display: inline-block; 23 | font-size: $font-size-head; 24 | color: $black; 25 | vertical-align: middle; 26 | margin: 0 10px; 27 | } 28 | &:nth-child(even) { 29 | background-color: #f4f4f4; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/singer/singer.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 53 | 54 | 57 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/single/assets/collect.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/single/assets/download.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/single/assets/right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/song/assets/default.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/song/song.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | .song { 3 | li { 4 | padding: 10px 30px; 5 | a { 6 | display: block; 7 | @include line-height(40px); 8 | text-decoration: none; 9 | font-size: 0; 10 | } 11 | .coverImg { 12 | display: inline-block; 13 | @include case(40px); 14 | background-size: 100%; 15 | } 16 | .picUrl { 17 | @include case(40px); 18 | vertical-align: middle; 19 | border: 1px solid $grey-lighter; 20 | } 21 | .name { 22 | display: inline-block; 23 | font-size: $font-size-head; 24 | color: $black; 25 | vertical-align: middle; 26 | margin: 0 10px; 27 | } 28 | .album { 29 | display: block; 30 | margin: 0; 31 | font-size: $font-size-base; 32 | color: $grey; 33 | } 34 | &:nth-child(even) { 35 | background-color: #f4f4f4; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/song/song.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 57 | 58 | 61 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/component/user/user.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/renderer/components/views/search/search.scss: -------------------------------------------------------------------------------- 1 | @import "src/renderer/assets/scss/var.scss"; 2 | 3 | .search-main { 4 | background-color: #fafafa; 5 | .keywords { 6 | @include line-height(54px); 7 | font-size: $font-size-base; 8 | padding: 0 30px; 9 | span { 10 | color: #2b77cd; 11 | } 12 | } 13 | 14 | .search-nav { 15 | border-bottom: 2px solid $red; 16 | padding: 0 30px; 17 | font-size: 0; 18 | li { 19 | display: inline-block; 20 | } 21 | a { 22 | display: inline-block; 23 | width: 80px; 24 | text-align: center; 25 | font-size: $font-size-head; 26 | text-decoration: none; 27 | border: 1px solid #c8c8c8; 28 | border-bottom: none; 29 | background-color: #f4f4f4; 30 | margin-right: 5px; 31 | @include line-height(32px); 32 | padding: 0 20px; 33 | color: $black; 34 | &.active { 35 | background-color: $red-light; 36 | border-color: $red; 37 | color: $white; 38 | } 39 | } 40 | } 41 | 42 | .loading { 43 | text-align: center; 44 | padding: 50px; 45 | } 46 | 47 | .not-data { 48 | font-size: $font-size-base; 49 | padding: 100px 0; 50 | text-align: center; 51 | span { 52 | color: #2b77cd; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/renderer/components/windowViews.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 21 | -------------------------------------------------------------------------------- /src/renderer/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App' 3 | import router from './router' 4 | import store from './store' 5 | import loading from '@/components/common/loading' 6 | import toast from '@/components/common/toast' 7 | import pagination from '@/components/common/pagination' 8 | // loading 9 | Vue.use(loading) 10 | // toast 11 | Vue.use(toast); 12 | // pagination 13 | Vue.use(pagination) 14 | 15 | if (!process.env.IS_WEB) Vue.use(require('vue-electron')) 16 | Vue.config.productionTip = false 17 | 18 | /* eslint-disable no-new */ 19 | new Vue({ 20 | components: { App }, 21 | router, 22 | store, 23 | template: '' 24 | }).$mount('#app') 25 | -------------------------------------------------------------------------------- /src/renderer/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import logger from 'vuex/dist/logger' 4 | 5 | import modules from './modules' 6 | 7 | Vue.use(Vuex) 8 | 9 | const debug = process.env.NODE_ENV !== 'production' 10 | 11 | if (debug) { 12 | console.log(modules) 13 | } 14 | 15 | export default new Vuex.Store({ 16 | modules, 17 | strict: debug, 18 | plugins: !debug ? [logger()] : [] 19 | }) 20 | -------------------------------------------------------------------------------- /src/renderer/store/modules/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The file enables `@/store/index.js` to import all vuex modules 3 | * in a one-shot manner. There should not be any reason to edit this file. 4 | */ 5 | 6 | const files = require.context('.', false, /\.js$/) 7 | const modules = {} 8 | 9 | files.keys().forEach(key => { 10 | if (key === './index.js') return 11 | modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default 12 | }) 13 | 14 | export default modules 15 | -------------------------------------------------------------------------------- /src/renderer/store/modules/routes.js: -------------------------------------------------------------------------------- 1 | import * as types from '../mutation-types' 2 | 3 | const state = { 4 | record: [], 5 | index: 0, 6 | routesBtn: false, 7 | backActive: false, 8 | forwardActive: false 9 | } 10 | 11 | const getters = { 12 | getBackStatus: state => state.backActive, 13 | getForwardStatus: state => state.forwardActive, 14 | getRoutesBtn: state => state.routesBtn 15 | } 16 | 17 | const actions = { 18 | routesBack({ state, commit }, router) { 19 | commit(types.ROUTERS_BACKS, router) 20 | }, 21 | routesForward({ state, commit }, router) { 22 | commit(types.ROUTERS_FORWARD, router) 23 | }, 24 | routesRecord({ state, commit }, path) { 25 | commit(types.ROUTERS_RECORD, path) 26 | }, 27 | routesBtnReset({ state, commit }, param) { 28 | commit(types.ROUTERS_STATUS, param) 29 | } 30 | } 31 | 32 | const callbacks = { 33 | buttonBack(index) { 34 | if (index > 0) { 35 | state.backActive = true 36 | } else { 37 | state.backActive = false 38 | } 39 | }, 40 | buttonForward(index) { 41 | if ((index + 1) === state.record.length) { 42 | state.forwardActive = false 43 | } else { 44 | state.forwardActive = true 45 | } 46 | }, 47 | buttonHighlight(index) { 48 | state.routesBtn = true 49 | this.buttonBack(index) 50 | this.buttonForward(index) 51 | } 52 | } 53 | 54 | const mutations = { 55 | [types.ROUTERS_RECORD](state, path) { 56 | state.record.push(path.fullPath) 57 | state.index = state.record.length - 1 58 | state.forwardActive = false 59 | callbacks.buttonBack(state.index) 60 | }, 61 | [types.ROUTERS_BACKS](state, router) { 62 | if ((state.index - 1) < 0) return 63 | state.index--; 64 | callbacks.buttonHighlight(state.index) 65 | router.replace({ path: state.record[state.index] }) 66 | }, 67 | [types.ROUTERS_FORWARD](state, router) { 68 | if ((state.index + 1) >= state.record.length) return 69 | state.index++; 70 | callbacks.buttonHighlight(state.index) 71 | router.replace({ path: state.record[state.index] }) 72 | }, 73 | [types.ROUTERS_STATUS](state, param) { 74 | state.routesBtn = param 75 | } 76 | } 77 | export default { 78 | state, 79 | getters, 80 | actions, 81 | mutations 82 | } 83 | -------------------------------------------------------------------------------- /src/renderer/store/modules/search.js: -------------------------------------------------------------------------------- 1 | import * as types from '../mutation-types' 2 | 3 | const state = { 4 | data: {} 5 | } 6 | 7 | const getters = { 8 | getSearchStatus: state => state.data 9 | } 10 | 11 | const actions = { 12 | setSearchData({ state, commit }, data) { 13 | commit(types.SEARCH_DATA_SAVE, data) 14 | } 15 | } 16 | 17 | const mutations = { 18 | [types.SEARCH_DATA_SAVE](state, data) { 19 | state.data = data 20 | } 21 | } 22 | 23 | export default { 24 | state, 25 | getters, 26 | actions, 27 | mutations 28 | } 29 | -------------------------------------------------------------------------------- /src/renderer/store/modules/user.js: -------------------------------------------------------------------------------- 1 | import * as types from '../mutation-types' 2 | import { local } from '@/utils/storage' 3 | 4 | const obj = {} 5 | 6 | const state = { 7 | user: local.getItem('user') || obj, 8 | path: obj, 9 | data: obj 10 | } 11 | 12 | const getters = { 13 | getUserStatus: state => state.data, 14 | getUserPathStatus: state => state.path 15 | } 16 | 17 | const actions = { 18 | userSignin({ commit }, user) { 19 | commit(types.USER_SIGNIN, user) 20 | }, 21 | userSignout({ commit }) { 22 | commit(types.USER_SIGNOUT) 23 | }, 24 | instructionRoute({ state, commit }, path) { 25 | commit(types.USER_ROUTER_PATH, path) 26 | }, 27 | setUserData({ state, commit }, data) { 28 | commit(types.USER_DATA_SAVE, data) 29 | } 30 | } 31 | 32 | const mutations = { 33 | [types.USER_SIGNIN](state, user) { 34 | local.setItem('user', user) 35 | state.user = user 36 | }, 37 | [types.USER_SIGNOUT](state) { 38 | local.removeItem('user') 39 | state.user = {} 40 | }, 41 | [types.USER_ROUTER_PATH](state, path) { 42 | state.path = path 43 | }, 44 | [types.USER_DATA_SAVE](state, data) { 45 | state.data = data 46 | } 47 | } 48 | 49 | export default { 50 | state, 51 | getters, 52 | actions, 53 | mutations 54 | } 55 | -------------------------------------------------------------------------------- /src/renderer/store/mutation-types.js: -------------------------------------------------------------------------------- 1 | // S toggle.js 2 | export const TOGGLE_LOGIN_DIALOG = 'TOGGLE_LOGIN_DIALOG' // 登录Dialog 3 | export const TOGGLE_DROPDOWN_LIST = 'TOGGLE_DROPDOWN_LIST' // 用户菜单 4 | export const TOGGLE_PLAY_LIST = 'TOGGLE_PLAY_LIST' // 播放列表 5 | export const TOGGLE_PLAY_INFO_BAR = 'TOGGLE_PLAY_INFO_BAR' // 导航下方播放信息 6 | export const TOGGLE_PLAY_DETAIL = 'TOGGLE_PLAY_DETAIL' // 播放音乐详情 7 | // E toggle.js 8 | 9 | // S user.js 10 | export const USER_SIGNIN = 'USER_SIGNIN' // 登录成功 11 | export const USER_SIGNOUT = 'USER_SIGNOUT' // 退出登录 12 | export const USER_ROUTER_PATH = 'USER_ROUTER_PATH' // 用户路由 13 | export const USER_DATA_SAVE = 'USER_DATA_SAVE' // 用户数据 14 | // E user.js 15 | 16 | // S play.js 17 | export const SET_MUSIC_URL = 'SET_MUSIC_URL' // 存入音乐地址 18 | export const SET_MUSIC_LIST = 'SET_MUSIC_LIST' // 添加播放列表 19 | export const SET_MUSIC_DETAILS = 'SET_MUSIC_DETAILS' // 存入音乐详情 20 | export const SET_MUSIC_CHANGE = 'SET_MUSIC_CHANGE' // ... 21 | export const MUSIC_PLAY = 'MUSIC_PLAY' // 设置音乐播放 22 | export const MUSIC_PAUSE = 'MUSIC_PAUSE' // 设置音乐暂停 23 | export const PLAY_PREV = 'PLAY_PREV' // 播放上一曲 24 | export const PLAY_NEXT = 'PLAY_NEXT' // 播放下一曲 25 | export const PLAY_CHANGE_TIME = 'PLAY_CHANGE_TIME' // 改变播放时间 26 | export const UPDATE_CURRENT_TIME = 'UPDATE_CURRENT_TIME' // .. 27 | export const UPDATE_DURATION_TIME = 'UPDATE_DURATION_TIME' // .. 28 | export const UPDATE_BUFFERED_TIME = 'UPDATE_BUFFERED_TIME' // .. 29 | // E play.js 30 | 31 | // S search.js 32 | export const SEARCH_DATA_SAVE = 'SEARCH_DATA_SAVE' // 用户数据 33 | // E search.js 34 | 35 | // S roters.js 36 | export const ROUTERS_BACKS = 'ROUTERS_BACKS' // 路由后退 37 | export const ROUTERS_FORWARD = 'ROUTERS_FORWARD' // 路由前进 38 | export const ROUTERS_RECORD = 'ROUTERS_RECORD' // 路由记录 39 | export const ROUTERS_STATUS = 'ROUTERS_STATUS' // 路由状态 40 | // E roters.js 41 | -------------------------------------------------------------------------------- /src/renderer/utils/background.js: -------------------------------------------------------------------------------- 1 | import { ipcRenderer, remote } from 'electron'; 2 | import { renderer } from '@/utils/ipcRenderer' 3 | import { session } from '@/utils/storage' 4 | renderer() 5 | export const app = { 6 | showMenus() { 7 | /** 8 | * 显示原生下拉菜单 9 | * @param return 10 | */ 11 | // settingsMenu() 12 | ipcRenderer.send('show-content-menu', session.getItem('CREATEPLAY')) 13 | }, 14 | openDialog() { 15 | /** 16 | * 打开登录新窗口 17 | * @param return 18 | */ 19 | ipcRenderer.send('open-login-window', `${window.location.origin}/#/login`) 20 | }, 21 | closeDialog() { 22 | /** 23 | * 关闭登录新窗口 24 | * @param return 25 | */ 26 | ipcRenderer.send('close-login-window') 27 | }, 28 | sendUserData(data) { 29 | /** 30 | * 登录成功发送登录信息 31 | * @param {Object} 登录详情 32 | */ 33 | ipcRenderer.send('user-data', data) 34 | }, 35 | loadUserData(callback) { 36 | /** 37 | * 登录成功接收登录信息 38 | * @param {Function} 接收成功返回 39 | */ 40 | remote.ipcMain.on('user-data', (event, data) => { 41 | callback(data) 42 | }) 43 | } 44 | } 45 | 46 | export function isDialog(dialog) { 47 | if (dialog) { 48 | app.openDialog() 49 | } else { 50 | app.closeDialog() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/renderer/utils/ipcRenderer.js: -------------------------------------------------------------------------------- 1 | import { ipcRenderer } from 'electron'; 2 | import { session } from '@/utils/storage' 3 | import store from '@/store' 4 | 5 | export function renderer() { 6 | // 开始播放 7 | ipcRenderer.on('play-start', (event) => { 8 | console.log('开始播放吧') 9 | var audio = session.getItem('AUDIO') 10 | store.dispatch('setMusicList', audio) 11 | store.dispatch('loadMusicUrl', audio.id) 12 | }) 13 | // 添加到播放列表 14 | ipcRenderer.on('add-play-list', (event) => { 15 | console.log('添加到播放列表') 16 | var audio = session.getItem('AUDIO') 17 | audio['addUrl'] = true 18 | store.dispatch('setMusicList', audio) 19 | }) 20 | // 21 | ipcRenderer.on('i-like-star', (event) => { 22 | window.alert('来个star给我动力做下去~ ^_^') 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /src/renderer/utils/libs.js: -------------------------------------------------------------------------------- 1 | import os from 'os' 2 | import request from 'request' 3 | import _ from 'lodash' 4 | const platform = os.platform() 5 | const color = 'color:rgb(66, 185, 131)' 6 | /** 7 | * 获取Dom元素 8 | * @param return 9 | */ 10 | export const $ = document.querySelector.bind(document) 11 | 12 | /** 13 | * 获取平台信息 14 | * @param {String} types win32(windows) || darwin(mac) 15 | * @param return 16 | */ 17 | export function platforms(types) { 18 | if (types === 'darwin' || types === 'win32') { 19 | return platform === types 20 | } 21 | return undefined 22 | } 23 | 24 | /** 25 | * 判断对象是否为空 26 | * @param {Object} obj 对象 27 | * @param return 28 | */ 29 | export function isNullObject(obj) { 30 | for (let i in obj) { 31 | return false 32 | } 33 | return true 34 | } 35 | 36 | /** 37 | * 封装ajax 38 | * @param {String} url 请求地址 39 | * @param {Object} param 请求参数 40 | * @param {Function} callback 请求成功后,服务器返回数据 41 | * @param {Function} err 错误处理 42 | * @param return 43 | */ 44 | export function ajax(url, param, callback, err) { 45 | console.log('%c接口: ' + url, color) 46 | console.log('%c参数: ' + JSON.stringify(param), color) 47 | var options = { 48 | method: 'GET', 49 | url: url, 50 | qs: param, 51 | headers: { 'content-type': 'application/x-www-form-urlencoded' } 52 | }; 53 | console.log('request参数=> ', [options]) 54 | request(options, (error, response, body) => { 55 | if (!error && response.statusCode === 200) { 56 | // 返回json字符串 `{}`,最好做个判断 57 | if (body.startsWith('{') && body.endsWith('}')) { 58 | body = JSON.parse(body) 59 | console.log('成功返回=>', body) 60 | callback(body) 61 | } else { 62 | console.error(body) 63 | } 64 | } else { 65 | console.log('错误返回=>', error) 66 | if (_.isFunction(err)) err() 67 | } 68 | }) 69 | } 70 | -------------------------------------------------------------------------------- /src/renderer/utils/storage.js: -------------------------------------------------------------------------------- 1 | const JSON = window.JSON 2 | const localStorage = window.localStorage 3 | const sessionStorage = window.sessionStorage 4 | 5 | export const local = { 6 | getItem(name) { 7 | let value = localStorage.getItem(name) 8 | if (/^\{.*\}$/.test(value)) value = JSON.parse(value) 9 | return value 10 | }, 11 | setItem(name, value) { 12 | if (typeof value === typeof {}) value = JSON.stringify(value) 13 | return localStorage.setItem(name, value) 14 | }, 15 | removeItem(name) { 16 | return localStorage.removeItem(name) 17 | } 18 | } 19 | 20 | export const session = { 21 | getItem(name) { 22 | let value = sessionStorage.getItem(name) 23 | if (/^\{.*\}$/.test(value)) value = JSON.parse(value) 24 | return value 25 | }, 26 | setItem(name, value) { 27 | if (typeof value === typeof {}) value = JSON.stringify(value) 28 | return sessionStorage.setItem(name, value) 29 | }, 30 | removeItem(name) { 31 | return sessionStorage.removeItem(name) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eugeneCN/vue-electron-music/896f902ce93ff3537228f8f852b305bad8514195/static/.gitkeep -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "assert": true, 7 | "expect": true, 8 | "should": true, 9 | "__static": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/e2e/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // Set BABEL_ENV to use proper env config 4 | process.env.BABEL_ENV = 'test' 5 | 6 | // Enable use of ES6+ on required files 7 | require('babel-register')({ 8 | ignore: /node_modules/ 9 | }) 10 | 11 | // Attach Chai APIs to global scope 12 | const { expect, should, assert } = require('chai') 13 | global.expect = expect 14 | global.should = should 15 | global.assert = assert 16 | 17 | // Require all JS files in `./specs` for Mocha to consume 18 | require('require-dir')('./specs') 19 | -------------------------------------------------------------------------------- /test/e2e/specs/Launch.spec.js: -------------------------------------------------------------------------------- 1 | import utils from '../utils' 2 | 3 | describe('Launch', function () { 4 | beforeEach(utils.beforeEach) 5 | afterEach(utils.afterEach) 6 | 7 | it('shows the proper application title', function () { 8 | return this.app.client.getTitle() 9 | .then(title => { 10 | expect(title).to.equal('vue-electron-music') 11 | }) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /test/e2e/utils.js: -------------------------------------------------------------------------------- 1 | import electron from 'electron' 2 | import { Application } from 'spectron' 3 | 4 | export default { 5 | afterEach () { 6 | this.timeout(10000) 7 | 8 | if (this.app && this.app.isRunning()) { 9 | return this.app.stop() 10 | } 11 | }, 12 | beforeEach () { 13 | this.timeout(10000) 14 | this.app = new Application({ 15 | path: electron, 16 | args: ['dist/electron/main.js'], 17 | startTimeout: 10000, 18 | waitTimeout: 10000 19 | }) 20 | 21 | return this.app.start() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | Vue.config.devtools = false 3 | Vue.config.productionTip = false 4 | 5 | // require all test files (files that ends with .spec.js) 6 | const testsContext = require.context('./specs', true, /\.spec$/) 7 | testsContext.keys().forEach(testsContext) 8 | 9 | // require all src files except main.js for coverage. 10 | // you can also change this to match only the subset of files that 11 | // you want coverage for. 12 | const srcContext = require.context('../../src/renderer', true, /^\.\/(?!main(\.js)?$)/) 13 | srcContext.keys().forEach(srcContext) 14 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const merge = require('webpack-merge') 5 | const webpack = require('webpack') 6 | 7 | const baseConfig = require('../../.config/webpack.renderer.config') 8 | const projectRoot = path.resolve(__dirname, '../../src/renderer') 9 | 10 | // Set BABEL_ENV to use proper preset config 11 | process.env.BABEL_ENV = 'test' 12 | 13 | let webpackConfig = merge(baseConfig, { 14 | devtool: '#inline-source-map', 15 | plugins: [ 16 | new webpack.DefinePlugin({ 17 | 'process.env.NODE_ENV': '"testing"' 18 | }) 19 | ] 20 | }) 21 | 22 | // don't treat dependencies as externals 23 | delete webpackConfig.entry 24 | delete webpackConfig.externals 25 | delete webpackConfig.output.libraryTarget 26 | 27 | // apply vue option to apply isparta-loader on js 28 | webpackConfig.module.rules 29 | .find(rule => rule.use.loader === 'vue-loader').use.options.loaders.js = 'babel-loader' 30 | 31 | module.exports = config => { 32 | config.set({ 33 | browsers: ['visibleElectron'], 34 | client: { 35 | useIframe: false 36 | }, 37 | coverageReporter: { 38 | dir: './coverage', 39 | reporters: [ 40 | { type: 'lcov', subdir: '.' }, 41 | { type: 'text-summary' } 42 | ] 43 | }, 44 | customLaunchers: { 45 | 'visibleElectron': { 46 | base: 'Electron', 47 | flags: ['--show'] 48 | } 49 | }, 50 | frameworks: ['mocha', 'chai'], 51 | files: ['./index.js'], 52 | preprocessors: { 53 | './index.js': ['webpack', 'sourcemap'] 54 | }, 55 | reporters: ['spec', 'coverage'], 56 | singleRun: true, 57 | webpack: webpackConfig, 58 | webpackMiddleware: { 59 | noInfo: true 60 | } 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /test/unit/specs/LandingPage.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import LandingPage from '@/components/LandingPage' 3 | 4 | describe('LandingPage.vue', () => { 5 | it('should render correct contents', () => { 6 | const vm = new Vue({ 7 | el: document.createElement('div'), 8 | render: h => h(LandingPage) 9 | }).$mount() 10 | 11 | expect(vm.$el.querySelector('.title').textContent).to.contain('Welcome to your new project!') 12 | }) 13 | }) 14 | --------------------------------------------------------------------------------