├── .babelrc
├── .ctrlpignore
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .gitmodules
├── .travis.yml
├── NeteaseCloudMusicApi
├── .gitignore
├── .npmignore
├── .pre-commit-config.yaml
├── .travis.yml
├── CHANGELOG.MD
├── LICENSE
├── README.MD
├── app.js
├── docs
│ ├── .nojekyll
│ ├── README.md
│ ├── _coverpage.md
│ ├── favicon.ico
│ ├── icon.png
│ ├── index.html
│ └── sw.js
├── issue_template.md
├── 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_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
├── static
│ ├── artist_album.png
│ ├── artists.png
│ ├── banner.png
│ ├── comment.png
│ ├── docs.png
│ ├── fm_trash.png
│ ├── like.png
│ ├── likeSuccess.png
│ ├── mv.png
│ ├── new_albums.png
│ ├── personal_fm.png
│ ├── play_mv.png
│ ├── screenshot1.png
│ ├── screenshot2.png
│ ├── signinError.png
│ ├── signinSuccess.png
│ ├── songDetail.png
│ ├── top_artists.png
│ ├── top_list.png
│ ├── top_playlist.png
│ ├── 专辑.png
│ ├── 推荐歌单.png
│ ├── 推荐歌曲.png
│ ├── 搜索.png
│ ├── 歌单详情.png
│ ├── 歌词.png
│ ├── 用户歌单.png
│ ├── 登录.png
│ └── 音乐 url.png
├── test
│ ├── album.test.js
│ ├── comment.test.js
│ ├── login.test.js
│ ├── lyric.test.js
│ ├── musicUrl.test.js
│ └── search.test.js
├── util
│ ├── crypto.js
│ └── util.js
└── yarn.lock
├── README.md
├── config
├── index.js
├── webpack.config.base.js
├── webpack.config.dev.js
├── webpack.config.electron.js
└── webpack.config.production.js
├── main.js
├── package.json
├── publish.sh
├── resource
├── 128x128.png
├── 16x16.png
├── 24x24.png
├── 256x256.png
├── 32x32.png
├── 48x48.png
├── 64x64.png
├── 96x96.png
├── dock.icns
├── xbyjMusic.ico
└── xbyjMusic.sketch
├── screenshots
├── 1.首页.gif
├── 10.歌单主页.png
├── 11.搜索页.png
├── 12.偏好设置.png
├── 13.公众号.jpg
├── 14.微信群.png
├── 15.支付宝.jpg
├── 16.微信支付.png
├── 2.每日推荐.png
├── 3.私人FM.png
├── 4.歌曲页.gif
├── 5.歌单页.gif
├── 6.歌手页.gif
├── 7.用户页.png
├── 8.加载页.png
└── 9.排行榜.png
├── server
├── api.js
├── api
│ ├── follow.js
│ ├── hot_album.js
│ ├── login.js
│ ├── loginCellphone.js
│ ├── login_refresh.js
│ ├── scrobble.js
│ ├── sub.js
│ ├── subscribe.js
│ ├── thumbsup.js
│ ├── unfollow.js
│ ├── unsub.js
│ └── unsubscribe.js
├── dev.js
├── router
│ ├── artist.js
│ ├── comments.js
│ ├── dailyplayer.js
│ ├── fm.js
│ ├── home.js
│ ├── lyrics.js
│ ├── player.js
│ ├── playlist.js
│ ├── search.js
│ ├── top.js
│ └── user.js
└── search
│ ├── Baidu.js
│ ├── Kugou.js
│ ├── QQ.js
│ ├── Xiami.js
│ ├── index.js
│ └── test.js
└── src
├── app.js
├── assets
├── WeChat.jpg
├── WeChatGroup.png
├── close-white.png
├── close.png
├── dock.png
├── github.png
├── loading.gif
├── notplaying.png
└── playing.png
├── globalCss
└── global.css
├── index.html
├── js
├── components
│ ├── AboutMe
│ │ ├── classes.js
│ │ └── index.js
│ ├── AudioPlayer
│ │ └── index.js
│ ├── Carousel
│ │ └── index.js
│ ├── Comments
│ │ ├── classes.js
│ │ └── index.js
│ ├── Controller
│ │ ├── classes.js
│ │ └── index.js
│ ├── Header
│ │ ├── classes.js
│ │ └── index.js
│ ├── LeftMenu
│ │ ├── classes.js
│ │ └── index.js
│ ├── Lyrics
│ │ ├── classes.js
│ │ └── index.js
│ ├── Playing
│ │ ├── classes.js
│ │ └── index.js
│ ├── Ripple
│ │ ├── PlayerMode.js
│ │ ├── PlayerNavigation.js
│ │ ├── PlayerStatus.js
│ │ ├── VolumeUpDown.js
│ │ └── classes.js
│ ├── Search
│ │ ├── classes.js
│ │ └── index.js
│ └── SyncCarousel
│ │ ├── classes.js
│ │ └── index.js
├── pages
│ ├── Artist
│ │ ├── classes.js
│ │ └── index.js
│ ├── FM
│ │ ├── classes.js
│ │ └── index.js
│ ├── Home
│ │ ├── classes.js
│ │ └── index.js
│ ├── Layout.js
│ ├── Login
│ │ ├── classes.js
│ │ └── index.js
│ ├── Player
│ │ ├── Search
│ │ │ ├── classes.js
│ │ │ └── index.js
│ │ ├── classes.js
│ │ └── index.js
│ ├── Playlist
│ │ ├── classes.js
│ │ └── index.js
│ ├── Preferences
│ │ ├── classes.js
│ │ └── index.js
│ ├── Song
│ │ ├── classes.js
│ │ └── index.js
│ ├── Top
│ │ ├── classes.js
│ │ └── index.js
│ ├── User
│ │ ├── classes.js
│ │ └── index.js
│ └── dailyPlayer
│ │ ├── classes.js
│ │ └── index.js
├── routes.js
├── stores
│ ├── aboutme.js
│ ├── artist.js
│ ├── comments.js
│ ├── controller.js
│ ├── dailyplayer.js
│ ├── fm.js
│ ├── home.js
│ ├── index.js
│ ├── lyrics.js
│ ├── me.js
│ ├── player.js
│ ├── playing.js
│ ├── playlist.js
│ ├── preferences.js
│ ├── search.js
│ ├── top.js
│ └── user.js
├── ui
│ ├── FadeImage
│ │ ├── classes.js
│ │ └── index.js
│ ├── Loader
│ │ ├── classes.js
│ │ └── index.js
│ ├── Offline
│ │ ├── classes.js
│ │ └── index.js
│ ├── ProgressImage
│ │ ├── classes.js
│ │ └── index.js
│ └── Switch
│ │ ├── classes.js
│ │ └── index.js
└── utils
│ ├── albumColors.js
│ ├── colors.js
│ ├── helper.js
│ ├── lastfm.js
│ ├── sine.js
│ ├── storage.js
│ └── wave.js
└── theme.js
/.babelrc:
--------------------------------------------------------------------------------
1 | // NOTE: These options are overriden by the babel-loader configuration
2 | // for webpack, which can be found in ~/build/webpack.config.
3 | //
4 | // Why? The react-transform-hmr plugin depends on HMR (and throws if
5 | // module.hot is disabled), so keeping it and related plugins contained
6 | // within webpack helps prevent unexpected errors.
7 | {
8 | "presets": ['es2015', "react", "stage-0"],
9 | "plugins": ["babel-polyfill", "transform-decorators-legacy"],
10 | "env": {
11 | "production": {
12 | "presets": ["react-optimize"]
13 | },
14 | "development": {
15 | "presets": ["react-hmre"]
16 | },
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/.ctrlpignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | ios
3 | DS_Store
4 | android
5 | git
6 | dist
7 | release
8 | __tests__
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 |
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 | insert_final_newnewline = true
11 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 |
2 | dist/*
3 | __tests__/*
4 | src/assets/*
5 | server/api/*
6 | NeteaseCloudMusicApi/*
7 |
8 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parserOptions": {
3 | "ecmaVersion": 6,
4 | "sourceType": "module",
5 | "ecmaFeatures": {
6 | "impliedStrict": true,
7 | "jsx": true
8 | }
9 | },
10 | "env": {
11 | "es6": true,
12 | "node": true
13 | },
14 | "parser": "babel-eslint",
15 | "plugins": ["react"],
16 | "rules": {
17 | "semi": [2, "always"],
18 | "new-cap": [0],
19 | "prefer-promise-reject-errors": [0],
20 | "indent": [2, 4, { "SwitchCase": 1 }],
21 | "comma-dangle": [2, "only-multiline"],
22 | "space-before-function-paren": [2, "never"],
23 | "operator-linebreak": [2, "before"],
24 | "no-floating-decimal": [0],
25 | "react/jsx-indent": [2, 4],
26 | "react/jsx-indent-props": [2, 4],
27 | "react/jsx-boolean-value": [2, "always"],
28 | "react/prop-types": [0],
29 | "jsx-quotes": [2, "prefer-double"]
30 | },
31 | "extends": ["standard", "standard-react"]
32 | }
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Other
6 | #
7 | dist/
8 | release/
9 | package-lock.json
10 |
11 | # Xcode
12 | #
13 | build/
14 | *.pbxuser
15 | !default.pbxuser
16 | *.mode1v3
17 | !default.mode1v3
18 | *.mode2v3
19 | !default.mode2v3
20 | *.perspectivev3
21 | !default.perspectivev3
22 | xcuserdata
23 | *.xccheckout
24 | *.moved-aside
25 | DerivedData
26 | *.hmap
27 | *.ipa
28 | *.xcuserstate
29 | project.xcworkspace
30 |
31 | # Android/IntelliJ
32 | #
33 | build/
34 | .idea
35 | .gradle
36 | local.properties
37 | *.iml
38 |
39 | # node.js
40 | #
41 | node_modules/
42 | npm-debug.log
43 |
44 | # BUCK
45 | buck-out/
46 | \.buckd/
47 | android/app/libs
48 | *.keystore
49 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "NeteaseCloudMusicApi"]
2 | path = NeteaseCloudMusicApi
3 | url = https://github.com/trazyn/NeteaseCloudMusicApi
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | language: node_js
4 |
5 | node_js:
6 | - 8
7 | - 7
8 | - 6
9 |
10 | script: npm run lint && npm run build
11 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | *.log
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/.npmignore:
--------------------------------------------------------------------------------
1 | static
2 | docs
3 | node_modules
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | - repo: https://github.com/Binaryify/NeteaseCloudMusicApi
2 | sha: '' # Use the sha or tag you want to point at
3 | hooks:
4 | - id: prettier
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - 6.2
5 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/CHANGELOG.MD:
--------------------------------------------------------------------------------
1 | # 更新日志
2 | ### 2.7.2 | 2017.9.7
3 | 修复搜索接口 offset 参数失效问题
4 |
5 | ### 2.7.0 | 2017.8.21
6 | 优化刷新登录代码
7 |
8 | ### 2.6.5 | 2017.7.16
9 | 优化 CORS 设置
10 |
11 | ### 2.6.4 | 2017.7.16
12 | 添加缓存机制和随机 UA 机制 感谢[@u3u](https://github.com/u3u)
13 | [issue:77](https://github.com/Binaryify/NeteaseCloudMusicApi/issues/77)
14 | 优化请求代码 感谢 [@huhuime](https://github.com/huhuime)
15 | [issue:83](https://github.com/Binaryify/NeteaseCloudMusicApi/issues/83)
16 |
17 | ### 2.6.2 | 2017.7.16
18 | 修复垃圾桶接口
19 |
20 | ### 2.6.1 | 2017.7.16
21 | 修复红心接口
22 |
23 | ### 2.6.0 | 2017.6.25
24 | 修复签到接口
25 |
26 | ### 2.5.9 | 2017.6.14
27 | 增加启动说明页
28 |
29 | ### 2.5.8 | 2017.6.1
30 | 修复若干细节问题
31 |
32 | ### 2.5.7 | 2017.5.22
33 | 修复若干问题
34 |
35 | ### 2.5.6 | 2017.5.14
36 | 增加动态消息接口
37 |
38 | ### 2.5.5 | 2017.5.10
39 | 修复 mv 排行榜接口崩溃问题
40 |
41 | ### 2.5.4 | 2017.5.5
42 | 新增点赞接口,更新文档
43 |
44 | ### 2.5.3 | 2017.5.2
45 | 修复歌手单曲数据空白问题和文档获取歌手单曲url 描述问题,更新文档
46 |
47 | ### 2.5.0 | 2017.4.29
48 | 增加 mv/专辑/歌单评论接口,增加云盘相关接口,增加获取用户动态/信息接口,增加关注/粉丝列表接口,增加收藏歌单接口,增加相似 mv/歌曲/用户接口,增加 banner 接口,增加刷新登录接口,增加电台相关接口,补充评论接口,更新文档
49 |
50 | ### 2.4.6 | 2017.4.21
51 | 增加播放 mv 接口,更新文档
52 |
53 | ### 2.4.5 | 2017.4.20
54 | 增加歌手专辑,歌手单曲等接口,修复/album 接口描述错误,更新文档
55 |
56 | ### 2.4.0 | 2017.4.20
57 | 增加歌单(网友精选碟),新碟上架,热门歌手等接口,更新文档
58 |
59 | ### 2.3.4 | 2017.4.20
60 | 增加歌曲详情接口,更新文档
61 |
62 | ### 2.3.0 | 2017.4.15
63 | 增加排行榜接口,更新文档
64 |
65 | ### 2.2.0 |2017.4.14
66 | 增加私人 FM, 喜欢歌曲,垃圾桶,每日签到等接口,更新文档
67 |
68 | ### 2.1.3 | 2017.4.6
69 | 改善文档
70 |
71 | ### 2.1.0 | 2017.4.6
72 | 增加获取评论接口以及对应单元测试,增加更新日志
73 |
74 | ### 2.0.0 | 2017.4.1
75 | 版本升级到 2.0.增加使用文档,完成项目重构,增加更完善的单元测试,升级 api 到 v2+,支持登录并获取用户信息和创建的歌单,可通过获取音乐 url 接口获取用户歌单里的的音乐,获取每日推荐歌单和每日推荐音乐
76 |
77 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013-2016 Binaryify
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/docs/.nojekyll
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/docs/_coverpage.md:
--------------------------------------------------------------------------------
1 | # 网易云音乐 API
2 |
3 | > 网易云音乐 NodeJS 版 API
4 |
5 | - 全部接口已升级到最新
6 | - 具备登录接口
7 | - 更完善的文档
8 |
9 |
10 | [GitHub](https://github.com/Binaryify/NeteaseCloudMusicApi)
11 | [Get Started](#neteasecloudmusicapi)
12 |
13 | 
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/docs/favicon.ico
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/docs/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/docs/icon.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 网易云音乐 NodeJS 版 API
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
23 |
24 |
29 |
30 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/issue_template.md:
--------------------------------------------------------------------------------
1 | ### 环境
2 | 系统:
3 |
4 | nodejs 版本:
5 |
6 | ### 出现问题
7 |
8 | ### 重现步骤
9 |
10 | ### 期待效果
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "NeteaseCloudMusicApi",
3 | "version": "2.7.2",
4 | "description": "网易云音乐 NodeJS 版 API",
5 | "scripts": {
6 | "start": "node app.js",
7 | "test": "mocha -r intelli-espower-loader -t 20000 test",
8 | "precommit": "lint-staged"
9 | },
10 | "lint-staged": {
11 | "*.js": [
12 | "prettier --write",
13 | "git add"
14 | ]
15 | },
16 | "keywords": [
17 | "网易云音乐",
18 | "网易云",
19 | "音乐",
20 | "网易云音乐nodejs"
21 | ],
22 | "author": "",
23 | "license": "MIT",
24 | "dependencies": {
25 | "apicache": "^0.9.0",
26 | "big-integer": "^1.6.17",
27 | "express": "^4.15.2",
28 | "request": "^2.81.0"
29 | },
30 | "devDependencies": {
31 | "husky": "^0.14.3",
32 | "intelli-espower-loader": "^1.0.1",
33 | "lint-staged": "^4.0.3",
34 | "mocha": "^3.2.0",
35 | "power-assert": "^1.4.2",
36 | "prettier": "^1.5.3"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 网易云音乐 API
8 |
9 |
10 | 网易云音乐 API
11 | 当你看到这个页面时,这个服务已经成功跑起来了~
12 | 查看文档
13 | 例子:
14 |
19 |
47 |
48 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
25 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
26 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
25 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
24 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
27 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/router/banner.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 | createWebAPIRequest(
9 | 'music.163.com',
10 | '/weapi/v2/banner/get',
11 | 'POST',
12 | data,
13 | cookie,
14 | music_req => {
15 | res.send(music_req)
16 | },
17 | err => res.status(502).send('fetch error')
18 | )
19 | })
20 |
21 | module.exports = router
22 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
34 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
28 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
28 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
38 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
28 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
28 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
28 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | let type = req.query.type || 0 //0为安卓端签到 3点经验,1为网页签到,2点经验
9 | const data = {
10 | csrf_token: '',
11 | type
12 | }
13 | // {'android': {'point': 3, 'code': 200}, 'web': {'point': 2, 'code': 200}}
14 | // {'android': {'code': -2, 'msg': '重复签到'}, 'web': {'code': -2, 'msg': '重复签到'}}
15 | // 'android': {'code': 301}, 'web': {'code': 301}}
16 |
17 | createWebAPIRequest(
18 | 'music.163.com',
19 | '/weapi/point/dailyTask',
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 | module.exports = router
29 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
24 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
26 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
31 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
29 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
25 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
24 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
25 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
26 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 songId = req.query.id
8 | const alg = 'RT'
9 | const time = req.query.time || 25
10 | const data = {
11 | csrf_token: '',
12 | songId
13 | }
14 |
15 | createWebAPIRequest(
16 | 'music.163.com',
17 | `/weapi/radio/trash/add?alg=${alg}&songId=${songId}&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 | module.exports = router
27 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
26 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 trackId = req.query.id
8 | const like = req.query.like || true
9 | const alg = req.query.alg || 'itembased'
10 | const time = req.query.time || 25
11 | const data = {
12 | csrf_token: '',
13 | trackId,
14 | like
15 | }
16 | createWebAPIRequest(
17 | 'music.163.com',
18 | `/weapi/radio/like?alg=${alg}&trackId=${trackId}&like=${like}&time=${time}`,
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 | module.exports = router
28 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
25 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
23 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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:
16 | '1_jVUMqWEPke0/1/Vu56xCmJpo5vP1grjn_SOVVDzOc78w8OKLVZ2JH7IfkjSXqgfmh'
17 | }
18 | console.log(email, req.query.password)
19 |
20 | createWebAPIRequest(
21 | 'music.163.com',
22 | '/weapi/login?csrf_token=',
23 | 'POST',
24 | data,
25 | cookie,
26 | (music_req, cookie) => {
27 | // console.log(music_req)
28 | res.set({
29 | 'Set-Cookie': cookie
30 | })
31 | res.send(music_req)
32 | },
33 | err => res.status(502).send('fetch error')
34 | )
35 | })
36 |
37 | module.exports = router
38 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | createWebAPIRequest(
17 | 'music.163.com',
18 | '/weapi/login/cellphone',
19 | 'POST',
20 | data,
21 | cookie,
22 | (music_req, cookie) => {
23 | // console.log(music_req)
24 | res.set({
25 | 'Set-Cookie': cookie
26 | })
27 | res.send(music_req)
28 | },
29 | err => res.status(502).send('fetch error')
30 | )
31 | })
32 |
33 | module.exports = router
34 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | const data = {
8 | csrf_token: ''
9 | }
10 | console.log({ cookie })
11 | createWebAPIRequest(
12 | 'music.163.com',
13 | `/weapi/login/token/refresh`,
14 | 'POST',
15 | data,
16 | cookie,
17 | (music_req, cookie) => {
18 | console.log({ cookie })
19 | res.set({
20 | 'Set-Cookie': cookie
21 | })
22 | res.send(music_req)
23 | },
24 | err => res.status(502).send('fetch error')
25 | )
26 | })
27 |
28 | module.exports = router
29 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/router/lyric.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 | const id = req.query.id
9 | createWebAPIRequest(
10 | 'music.163.com',
11 | '/weapi/song/lyric?os=osx&id=' + id + '&lv=-1&kv=-1&tv=-1',
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
23 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
32 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/router/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 mvid = req.query.mvid
8 | const data = {
9 | id: mvid
10 | }
11 |
12 | createWebAPIRequest(
13 | 'music.163.com',
14 | `/weapi/mv/detail`,
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
26 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
27 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
25 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | createWebAPIRequest(
11 | 'music.163.com',
12 | '/weapi/v1/radio/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 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | createWebAPIRequest(
9 | 'music.163.com',
10 | '/weapi/personalized/playlist',
11 | 'POST',
12 | data,
13 | cookie,
14 | music_req => {
15 | res.send(music_req)
16 | },
17 | err => res.status(502).send('fetch error')
18 | )
19 | })
20 |
21 | module.exports = router
22 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | createWebAPIRequest(
9 | 'music.163.com',
10 | '/weapi/personalized/djprogram',
11 | 'POST',
12 | data,
13 | cookie,
14 | music_req => {
15 | res.send(music_req)
16 | },
17 | err => res.status(502).send('fetch error')
18 | )
19 | })
20 |
21 | module.exports = router
22 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | createWebAPIRequest(
9 | 'music.163.com',
10 | '/weapi/personalized/mv',
11 | 'POST',
12 | data,
13 | cookie,
14 | music_req => {
15 | res.send(music_req)
16 | },
17 | err => res.status(502).send('fetch error')
18 | )
19 | })
20 |
21 | module.exports = router
22 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | '/weapi/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 | module.exports = router
24 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | createWebAPIRequest(
9 | 'music.163.com',
10 | '/weapi/personalized/privatecontent',
11 | 'POST',
12 | data,
13 | cookie,
14 | music_req => {
15 | res.send(music_req)
16 | },
17 | err => res.status(502).send('fetch error')
18 | )
19 | })
20 |
21 | module.exports = router
22 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
24 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | createWebAPIRequest(
9 | 'music.163.com',
10 | '/weapi/playlist/hottags',
11 | 'POST',
12 | data,
13 | cookie,
14 | music_req => {
15 | res.send(music_req)
16 | },
17 | err => res.status(502).send('fetch error')
18 | )
19 | })
20 |
21 | module.exports = router
22 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
30 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
25 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
23 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
23 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
26 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
26 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/router/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 cookie = req.get("Cookie") ? req.get("Cookie") : "";
7 | const keywords = req.query.keywords;
8 | const type = req.query.type || 1;
9 | const limit = req.query.limit || 30;
10 | const offset = req.query.offset || 0;
11 | // *(type)* 搜索单曲(1),歌手(100),专辑(10),歌单(1000),用户(1002)
12 | const data = {
13 | csrf_token: "",
14 | limit,
15 | type,
16 | s: keywords,
17 | offset
18 | };
19 |
20 | createWebAPIRequest(
21 | "music.163.com",
22 | "/weapi/search/get",
23 | "POST",
24 | data,
25 | cookie,
26 | music_req => res.send(music_req),
27 | err => res.status(502).send("fetch error")
28 | );
29 | });
30 |
31 | module.exports = router;
32 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 || ''
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 | },
22 | err => res.status(502).send('fetch error')
23 | )
24 | })
25 |
26 | module.exports = router
27 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 || ''
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 | module.exports = router
26 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
24 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
22 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
26 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
26 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
26 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
29 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 || 0,
11 | total: true,
12 | limit: req.query.limit || 50,
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 | module.exports = router
30 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/router/top_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 | offset: req.query.offset || 0,
9 | total: true,
10 | limit: req.query.limit || 50,
11 | csrf_token: ''
12 | }
13 | createWebAPIRequest(
14 | 'music.163.com',
15 | `/weapi/artist/top`,
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
27 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
43 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
29 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
30 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | csrf_token: ""
12 | };
13 | createWebAPIRequest(
14 | "music.163.com",
15 | "/weapi/playlist/highquality/list",
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;
27 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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: 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/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 | module.exports = router
30 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
25 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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: req.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
26 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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: req.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
27 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
30 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
29 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
29 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | `/weapi/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
26 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
28 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
27 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
27 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
27 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
25 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | module.exports = router
28 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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
22 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/artist_album.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/artist_album.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/artists.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/artists.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/banner.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/comment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/comment.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/docs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/docs.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/fm_trash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/fm_trash.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/like.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/like.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/likeSuccess.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/likeSuccess.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/mv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/mv.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/new_albums.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/new_albums.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/personal_fm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/personal_fm.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/play_mv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/play_mv.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/screenshot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/screenshot1.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/screenshot2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/screenshot2.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/signinError.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/signinError.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/signinSuccess.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/signinSuccess.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/songDetail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/songDetail.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/top_artists.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/top_artists.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/top_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/top_list.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/top_playlist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/top_playlist.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/专辑.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/专辑.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/推荐歌单.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/推荐歌单.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/推荐歌曲.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/推荐歌曲.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/搜索.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/搜索.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/歌单详情.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/歌单详情.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/歌词.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/歌词.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/用户歌单.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/用户歌单.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/登录.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/登录.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/static/音乐 url.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/NeteaseCloudMusicApi/static/音乐 url.png
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | })
18 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | })
32 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | })
72 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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(
9 | '/api/song/lyric?os=osx&id=' + id + '&lv=-1&kv=-1&tv=-1',
10 | 'GET',
11 | null
12 | )
13 | .then(result => {
14 | // console.log(JSON.parse(result).lrc)
15 | assert(typeof JSON.parse(result).lrc !== 'undefined')
16 | done()
17 | })
18 | .catch(err => {
19 | done(err)
20 | })
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 | })
33 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 =
11 | 's=' + keywords + '&limit=' + limit + '&type=' + type + '&offset=0'
12 | createRequest('/api/search/pc/', 'POST', data)
13 | .then(result => {
14 | console.log(JSON.parse(result).result.songs[0].mp3Url)
15 | assert(JSON.parse(result).result.songs[0].name === '海阔天空')
16 | done()
17 | })
18 | .catch(err => {
19 | done(err)
20 | })
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/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 =
6 | '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
7 | const nonce = '0CoJUm6Qyw8W8jud'
8 | const pubKey = '010001'
9 |
10 | String.prototype.hexEncode = function() {
11 | let hex, i
12 |
13 | let result = ''
14 | for (i = 0; i < this.length; i++) {
15 | hex = this.charCodeAt(i).toString(16)
16 | result += ('' + hex).slice(-4)
17 | }
18 | return result
19 | }
20 |
21 | function createSecretKey(size) {
22 | const keys = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
23 | let key = ''
24 | for (let i = 0; i < size; i++) {
25 | let pos = Math.random() * keys.length
26 | pos = Math.floor(pos)
27 | key = key + keys.charAt(pos)
28 | }
29 | return key
30 | }
31 |
32 | function aesEncrypt(text, secKey) {
33 | const _text = text
34 | const lv = new Buffer('0102030405060708', 'binary')
35 | const _secKey = new Buffer(secKey, 'binary')
36 | const cipher = crypto.createCipheriv('AES-128-CBC', _secKey, lv)
37 | let encrypted = cipher.update(_text, 'utf8', 'base64')
38 | encrypted += cipher.final('base64')
39 | return encrypted
40 | }
41 |
42 | function zfill(str, size) {
43 | while (str.length < size) str = '0' + str
44 | return str
45 | }
46 |
47 | function rsaEncrypt(text, pubKey, modulus) {
48 | const _text = text.split('').reverse().join('')
49 | const biText = bigInt(new Buffer(_text).toString('hex'), 16),
50 | biEx = bigInt(pubKey, 16),
51 | biMod = bigInt(modulus, 16),
52 | biRet = biText.modPow(biEx, biMod)
53 | return zfill(biRet.toString(16), 256)
54 | }
55 |
56 | function Encrypt(obj) {
57 | const text = JSON.stringify(obj)
58 | const secKey = createSecretKey(16)
59 | const encText = aesEncrypt(aesEncrypt(text, nonce), secKey)
60 | const encSecKey = rsaEncrypt(secKey, pubKey, modulus)
61 | return {
62 | params: encText,
63 | encSecKey: encSecKey
64 | }
65 | }
66 |
67 | module.exports = Encrypt
68 |
--------------------------------------------------------------------------------
/NeteaseCloudMusicApi/util/util.js:
--------------------------------------------------------------------------------
1 | const Encrypt = require('./crypto.js')
2 | const http = require('http')
3 | const querystring = require('querystring')
4 |
5 | function randomUserAgent() {
6 | return 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'
7 | }
8 | function createWebAPIRequest(
9 | host,
10 | path,
11 | method,
12 | data,
13 | cookie,
14 | callback,
15 | errorcallback
16 | ) {
17 | let music_req = ''
18 | const cryptoreq = Encrypt(data)
19 | const http_client = http.request(
20 | {
21 | hostname: host,
22 | method: method,
23 | path: path,
24 | headers: {
25 | Accept: '*/*',
26 | 'Accept-Language': 'zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4',
27 | Connection: 'keep-alive',
28 | 'Content-Type': 'application/x-www-form-urlencoded',
29 | Referer: 'http://music.163.com',
30 | Host: 'music.163.com',
31 | Cookie: cookie,
32 | 'User-Agent': randomUserAgent()
33 | }
34 | },
35 | function(res) {
36 | res.on('error', function(err) {
37 | errorcallback(err)
38 | })
39 | res.setEncoding('utf8')
40 | if (res.statusCode != 200) {
41 | createWebAPIRequest(host, path, method, data, cookie, callback)
42 | return
43 | } else {
44 | res.on('data', function(chunk) {
45 | music_req += chunk
46 | })
47 | res.on('end', function() {
48 | if (music_req == '') {
49 | createWebAPIRequest(host, path, method, data, cookie, callback)
50 | return
51 | }
52 | if (res.headers['set-cookie']) {
53 | callback(music_req, res.headers['set-cookie'])
54 | } else {
55 | callback(music_req)
56 | }
57 | })
58 | }
59 | }
60 | )
61 | http_client.write(
62 | querystring.stringify({
63 | params: cryptoreq.params,
64 | encSecKey: cryptoreq.encSecKey
65 | })
66 | )
67 | http_client.end()
68 | }
69 |
70 | function createRequest(path, method, data, callback, errorcallback) {
71 | return new Promise((resolve, reject) => {
72 | let ne_req = ''
73 | const http_client = http.request(
74 | {
75 | hostname: 'music.163.com',
76 | method: method,
77 | path: path,
78 | headers: {
79 | Referer: 'http://music.163.com',
80 | Cookie: 'appver=1.5.2',
81 | 'Content-Type': 'application/x-www-form-urlencoded',
82 | 'User-Agent': randomUserAgent()
83 | }
84 | },
85 | res => {
86 | res.setEncoding('utf8')
87 | res.on('error', err => {
88 | reject(err)
89 | })
90 | res.on('data', chunk => {
91 | ne_req += chunk
92 | })
93 | res.on('end', () => {
94 | resolve(ne_req)
95 | })
96 | }
97 | )
98 | if (method == 'POST') {
99 | http_client.write(data)
100 | }
101 | http_client.end()
102 | })
103 | }
104 | module.exports = {
105 | createWebAPIRequest,
106 | createRequest
107 | }
108 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # xbyjMusic
2 |
3 | [](http://standardjs.com)
4 | []()
5 |
6 |
7 |
8 | > 跨平台 NeteaseMusic 桌面应用
9 |
10 |
11 | # 技术栈
12 |
13 | > electron + react + mobx + react-router + jss + webpack + express + ES6/7 + axios + flex + canvas
14 |
15 |
16 |
17 | # 项目运行
18 |
19 | ### 注意:由于涉及大量的 ES6/7 等新属性,node 需要 6.0 以上版本
20 |
21 | ```
22 | git clone https://github.com/yllg/xbyjMusic.git
23 | cd xbyjMusic
24 | git submodule init
25 | git submodule update
26 | npm install
27 | npm run dev
28 |
29 | ```
30 |
31 |
32 | # 目标功能
33 | ## 页面
34 | - [x] 首页 -- 完成
35 | - [x] 登陆 -- 完成
36 | - [x] 每日推荐 -- 完成
37 | - [x] 私人FM -- 完成
38 | - [x] 歌曲页 -- 完成
39 | - [x] 歌单页 -- 完成
40 | - [x] 歌手页 -- 完成
41 | - [x] 用户页 -- 完成
42 | - [x] 排行榜 -- 完成
43 | - [x] 歌单主页 -- 完成
44 | - [x] 偏好设置页/首选项 -- 完成
45 | - [ ] MV/视频页
46 | - [ ] 朋友页
47 | - [ ] 我的歌手/我的收藏
48 | - [ ] 主播电台
49 | - [ ] 最新音乐
50 |
51 | ## 组件
52 | - [x] header组件 -- 完成
53 | - [x] 左菜单组件 -- 完成
54 | - [x] 播放条组件 -- 完成
55 | - [x] audio组件 -- 完成
56 | - [x] 播放列表组件 -- 完成
57 | - [x] 歌词组件 -- 完成
58 | - [ ] 评论组件 -- 只完成分类显示
59 | - [x] 搜索组件 -- 完成
60 | - [ ] 首页轮播 -- 接口参数不明,拿不到最新数据哦
61 | - [x] 同步轮播组件 -- 关于我
62 | - [x] 提示组件 -- 完成
63 |
64 |
65 | ## 功能
66 | - [x] 喜欢 -- 完成
67 | - [x] 不喜欢 -- 完成
68 | - [x] 收藏歌单 -- 完成
69 | - [ ] 收藏歌曲
70 | - [x] 收藏歌手 -- 完成
71 | - [ ] 评论
72 | - [x] 评论点赞 -- 完成
73 | - [ ] 下载歌曲
74 |
75 |
76 |
77 |
78 | # 效果演示
79 | (LICEcap录制GIF时,渐变色会有点失真,动图将就看下哈~)
80 |
81 | ### 首页
82 |
{
6 | const cookie = req.get('Cookie') ? req.get('Cookie') : ''
7 | const id = req.query.id
8 | const csrf = req.cookies['__csrf']
9 | const data = {
10 | csrf_token: csrf,
11 | followId: id,
12 | }
13 |
14 | createWebAPIRequest(
15 | 'music.163.com',
16 | `/weapi/user/follow/${id}?csrf_token=${csrf}`,
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
--------------------------------------------------------------------------------
/server/api/hot_album.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const router = express()
3 | const { createWebAPIRequest } = require('../../NeteaseCloudMusicApi/util/util')
4 |
5 | // type ALL, ZH,EA,KR,JP
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 | '/api/discovery/newAlbum',
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
25 |
--------------------------------------------------------------------------------
/server/api/login.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const crypto = require('crypto')
3 | const router = express()
4 | const { createWebAPIRequest } = require('../../NeteaseCloudMusicApi/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 | const csrf = req.cookies['__csrf']
11 | md5sum.update(req.query.password)
12 | const data = {
13 | csrf_token: csrf,
14 | username: email,
15 | password: md5sum.digest('hex'),
16 | rememberLogin: 'true',
17 | clientToken:
18 | '1_F7ocLT2vWrl5GOeGAMJAeo/dbaD6E3PI_yI7eXDqSmN+pLichDRZKIJ57l7VvXdOg'
19 | }
20 | console.log('Data: %O', data)
21 |
22 | createWebAPIRequest(
23 | 'music.163.com',
24 | `/weapi/login?csrf_token=${csrf}`,
25 | 'POST',
26 | data,
27 | cookie,
28 | (music_req, cookie) => {
29 | cookie = cookie.map(x => x.replace("Domain=.music.163.com", ""))
30 | res.set({
31 | 'Set-Cookie': cookie
32 | })
33 | res.send(music_req)
34 | },
35 | err => res.status(502).send('fetch error')
36 | )
37 | })
38 |
39 | module.exports = router
40 |
--------------------------------------------------------------------------------
/server/api/loginCellphone.js:
--------------------------------------------------------------------------------
1 |
2 | const express = require('express')
3 | const crypto = require('crypto')
4 | const router = express()
5 | const { createWebAPIRequest } = require('../../NeteaseCloudMusicApi/util/util')
6 |
7 | router.get('/', (req, res) => {
8 | const phone = req.query.phone
9 | const countrycode = req.query.countrycode
10 | const cookie = req.get('Cookie') ? req.get('Cookie') : ''
11 | const md5sum = crypto.createHash('md5')
12 | md5sum.update(req.query.password)
13 | const data = {
14 | countrycode: countrycode,
15 | phone: phone,
16 | password: md5sum.digest('hex'),
17 | rememberLogin: 'true'
18 | }
19 | createWebAPIRequest(
20 | 'music.163.com',
21 | '/weapi/login/cellphone',
22 | 'POST',
23 | data,
24 | cookie,
25 | (music_req, cookie) => {
26 | cookie = cookie.map(x => x.replace("Domain=.music.163.com", ""))
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
37 |
--------------------------------------------------------------------------------
/server/api/login_refresh.js:
--------------------------------------------------------------------------------
1 |
2 | const express = require('express')
3 | const router = express()
4 | const { createWebAPIRequest } = require('../../NeteaseCloudMusicApi/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 | console.log({ cookie })
12 | createWebAPIRequest(
13 | 'music.163.com',
14 | `/weapi/login/token/refresh`,
15 | 'POST',
16 | data,
17 | cookie,
18 | (music_req, cookie) => {
19 | if (cookie) {
20 | cookie = cookie.filter(x => !x.startsWith('MUSIC_U="";'))
21 | cookie = cookie.map(x => x.replace("Domain=.music.163.com", ""))
22 |
23 | res.set({
24 | 'Set-Cookie': cookie
25 | })
26 | }
27 |
28 | res.send(music_req)
29 | },
30 | err => res.status(502).send('fetch error')
31 | )
32 | })
33 |
34 | module.exports = router
35 |
--------------------------------------------------------------------------------
/server/api/scrobble.js:
--------------------------------------------------------------------------------
1 |
2 | const express = require('express')
3 | const router = express()
4 | const { createWebAPIRequest } = require('../../NeteaseCloudMusicApi/util/util')
5 |
6 | router.get('/', (req, res) => {
7 | const cookie = req.get('Cookie') ? req.get('Cookie') : ''
8 | const csrf = req.cookies['__csrf']
9 | const songid = req.query.songid
10 | const sourceid = req.query.sourceid
11 | const time = +req.query.time
12 | const data = {
13 | csrf_token: csrf,
14 | logs: JSON.stringify([{
15 | action: 'play',
16 | json: {
17 | download: 0,
18 | end: 'playend',
19 | id: +songid,
20 | sourceId: sourceid.toString(),
21 | time,
22 | type: 'song',
23 | wifi: 0,
24 | }
25 | }])
26 | }
27 |
28 | createWebAPIRequest(
29 | 'music.163.com',
30 | `/weapi/feedback/weblog?csrf_token=${csrf}`,
31 | 'POST',
32 | data,
33 | cookie,
34 | music_req => {
35 | res.send(music_req)
36 | },
37 | err => res.status(502).send('fetch error')
38 | )
39 | })
40 |
41 | module.exports = router
42 |
--------------------------------------------------------------------------------
/server/api/sub.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const router = express()
3 | const { createWebAPIRequest } = require('../../NeteaseCloudMusicApi/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 csrf = req.cookies['__csrf']
9 | const data = {
10 | csrf_token: csrf,
11 | artistId: id,
12 | }
13 |
14 | createWebAPIRequest(
15 | 'music.163.com',
16 | `/weapi/artist/sub?csrf_token=${csrf}`,
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
--------------------------------------------------------------------------------
/server/api/subscribe.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const router = express()
3 | const { createWebAPIRequest } = require('../../NeteaseCloudMusicApi/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 csrf = req.cookies['__csrf']
9 | const data = {
10 | csrf_token: csrf,
11 | id,
12 | }
13 |
14 | createWebAPIRequest(
15 | 'music.163.com',
16 | `/weapi/playlist/subscribe?csrf_token=${csrf}`,
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
--------------------------------------------------------------------------------
/server/api/thumbsup.js:
--------------------------------------------------------------------------------
1 |
2 | const express = require('express')
3 | const router = express()
4 | const { createWebAPIRequest } = require('../../NeteaseCloudMusicApi/util/util')
5 |
6 | router.get('/', (req, res) => {
7 | const cookie = req.get('Cookie') ? req.get('Cookie') : ''
8 | const csrf = req.cookies['__csrf']
9 | const cid = req.query.cid
10 | const tid = req.query.tid
11 | const like = !!+req.query.like
12 | const data = {
13 | csrf_token: csrf,
14 | commentId: cid,
15 | threadId: tid,
16 | like: like,
17 | }
18 |
19 | createWebAPIRequest(
20 | 'music.163.com',
21 | `/weapi/v1/comment/${like ? 'like' : 'unlike'}?csrf_token=${csrf}`,
22 | 'POST',
23 | data,
24 | `${cookie} osver=Version%2010.12.6%20(Build%2016G29); appver=1.5.9; os=osx; channel=netease;`,
25 | music_req => {
26 | res.send(music_req)
27 | },
28 | err => res.status(502).send('fetch error')
29 | )
30 | })
31 |
32 | module.exports = router
33 |
--------------------------------------------------------------------------------
/server/api/unfollow.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const router = express()
3 | const { createWebAPIRequest } = require('../../NeteaseCloudMusicApi/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 csrf = req.cookies['__csrf']
9 | const data = {
10 | csrf_token: csrf,
11 | followId: id,
12 | }
13 |
14 | createWebAPIRequest(
15 | 'music.163.com',
16 | `/weapi/user/delfollow/${id}?csrf_token=${csrf}`,
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
--------------------------------------------------------------------------------
/server/api/unsub.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const router = express()
3 | const { createWebAPIRequest } = require('../../NeteaseCloudMusicApi/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 csrf = req.cookies['__csrf']
9 | const data = {
10 | csrf_token: csrf,
11 | artistId: id,
12 | artistIds: [id],
13 | }
14 |
15 | console.log(data)
16 |
17 | createWebAPIRequest(
18 | 'music.163.com',
19 | `/weapi/artist/unsub?csrf_token=${csrf}`,
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
--------------------------------------------------------------------------------
/server/api/unsubscribe.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const router = express()
3 | const { createWebAPIRequest } = require('../../NeteaseCloudMusicApi/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 csrf = req.cookies['__csrf']
9 | const data = {
10 | csrf_token: csrf,
11 | pid: id,
12 | id,
13 | }
14 |
15 | createWebAPIRequest(
16 | 'music.163.com',
17 | `/weapi/playlist/unsubscribe?csrf_token=${csrf}`,
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
--------------------------------------------------------------------------------
/server/dev.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Setup and run the development server for Hot-Module-Replacement
4 | * https://webpack.github.io/docs/hot-module-replacement-with-webpack.html
5 | * @flow
6 | */
7 | import express from 'express';
8 | import webpack from 'webpack';
9 | import webpackDevMiddleware from 'webpack-dev-middleware';
10 | import webpackHotMiddleware from 'webpack-hot-middleware';
11 | import _debug from 'debug';
12 |
13 | import config from '../config';
14 | import webpackConfig from '../config/webpack.config.dev';
15 |
16 | const debug = _debug('dev:server');
17 | const app = new express();
18 | const compiler = webpack(webpackConfig);
19 |
20 | app.use(
21 | webpackDevMiddleware(compiler, {
22 | publicPath: webpackConfig.output.publicPath,
23 | stats: {
24 | colors: true
25 | },
26 | })
27 | );
28 | app.use(webpackHotMiddleware(compiler));
29 |
30 | app.listen(config.server.port, config.server.host, err => {
31 | if (err) {
32 | throw err;
33 | }
34 |
35 | debug(`Hot reload server is running with port ${config.server.port} 👏`);
36 | });
37 |
--------------------------------------------------------------------------------
/server/router/comments.js:
--------------------------------------------------------------------------------
1 |
2 | import express from 'express';
3 | import axios from 'axios';
4 | import _debug from 'debug';
5 |
6 | const debug = _debug('dev:api');
7 | const error = _debug('dev:error');
8 | const router = express();
9 |
10 | router.get('/like/:id/:songid/:like', async(req, res) => {
11 | debug('Handle request for /comments/like');
12 |
13 | var result = { success: false };
14 | var id = req.params.id;
15 | var songid = req.params.songid;
16 | var like = req.params.like;
17 |
18 | debug('Params \'id\': %s', id);
19 | debug('Params \'songid\': %s', songid);
20 | debug('Params \'liked\': %s', like);
21 |
22 | try {
23 | var response = await axios.get(`/thumbsup`, {
24 | params: {
25 | cid: id,
26 | tid: `R_SO_4_${songid}`,
27 | like: like,
28 | }
29 | });
30 | var data = response.data;
31 |
32 | if (data.code !== 200) {
33 | throw data;
34 | } else {
35 | result = {
36 | success: true,
37 | liked: !!+like,
38 | };
39 | }
40 | } catch (ex) {
41 | error('Failed to like comment: %O', ex);
42 | }
43 |
44 | res.send(result);
45 | });
46 |
47 | router.get('/:id/:offset?', async(req, res) => {
48 | debug('Handle request for /comments');
49 |
50 | var result = {};
51 | var id = req.params.id;
52 | var offset = req.params.offset || 0;
53 |
54 | debug('Params \'id\': %s', id);
55 | debug('Params \'offset\': %s', offset);
56 |
57 | try {
58 | var response = await axios.get(`/comment/music`, {
59 | params: {
60 | id,
61 | limit: 30,
62 | offset,
63 | }
64 | });
65 | var data = response.data;
66 |
67 | if (data.code !== 200) {
68 | throw data;
69 | } else {
70 | result = {
71 | newestList: data.comments,
72 | hotList: data.hotComments,
73 | total: data.total,
74 | nextHref: data.more ? `/api/comments/${id}/${+offset + 30}` : '',
75 | };
76 | }
77 | } catch (ex) {
78 | error('Failed to get comments: %O', ex);
79 | }
80 |
81 | res.send(result);
82 | });
83 |
84 | module.exports = router;
85 |
--------------------------------------------------------------------------------
/server/router/dailyplayer.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import axios from 'axios';
3 | import uuid from 'uuid';
4 | import _debug from 'debug';
5 |
6 | const debug = _debug('dev:api');
7 | const error = _debug('dev:error');
8 | const router = express();
9 |
10 | // 获取用户的每日推荐歌曲
11 | async function getDaily() {
12 | var list = [];
13 | try {
14 | let response = await axios.get('/recommend/songs');
15 |
16 | if (response.data.code !== 200) {
17 | throw response.data;
18 | } else {
19 | list = [{
20 | id: uuid.v4(),
21 | name: '每日推荐歌曲',
22 | size: response.data.recommend.length,
23 | songs: response.data.recommend.map(e => {
24 | var { album, artists } = e;
25 |
26 | return {
27 | id: e.id.toString(),
28 | name: e.name,
29 | duration: e.duration,
30 | album: {
31 | id: album.id.toString(),
32 | name: album.name,
33 | cover: `${album.picUrl}?param=100y100`,
34 | link: `/player/1/${album.id}`,
35 | },
36 | artists: artists.map(e => ({
37 | id: e.id.toString(),
38 | name: e.name,
39 | // Broken link
40 | link: e.id ? `/artist/${e.id}` : '',
41 | }))
42 | };
43 | }),
44 | }];
45 | }
46 | } catch (ex) {
47 | error('Failed to get daily songs: %O', ex);
48 | }
49 | return list;
50 | }
51 |
52 | router.get('/:id?', async(req, res) => {
53 | debug('Handle request for /home');
54 |
55 | var list = [];
56 | var unique = [];
57 | var id = req.params.id;
58 |
59 | if (id) {
60 | // 如果有用户id即登陆了,只取日推歌曲
61 | list = [
62 | ...(await getDaily()),
63 | ];
64 | }
65 |
66 | // debug('拿到日推歌曲信息', list);
67 |
68 | // Remove the duplicate items,去掉重复的歌曲
69 | list.map(e => {
70 | var index = unique.findIndex(item => item.id === e.id);
71 | // 没有相同的歌曲id才push
72 | if (index === -1) {
73 | unique.push(e);
74 | }
75 | });
76 |
77 | res.send({
78 | list: unique,
79 | });
80 | });
81 |
82 | module.exports = router;
83 |
--------------------------------------------------------------------------------
/server/router/fm.js:
--------------------------------------------------------------------------------
1 |
2 | import express from 'express';
3 | import axios from 'axios';
4 | import _debug from 'debug';
5 |
6 | const debug = _debug('dev:api');
7 | const error = _debug('dev:error');
8 | const router = express();
9 |
10 | router.get('/', async(req, res) => {
11 | debug('Handle request for /fm');
12 |
13 | var songs = [];
14 |
15 | try {
16 | let response = await axios.get(`/personal_fm`);
17 | let data = response.data;
18 |
19 | if (data.code !== 200) {
20 | throw data;
21 | }
22 |
23 | songs = data.data || [];
24 | } catch (ex) {
25 | error('Failed to get FM: %O', ex);
26 | }
27 |
28 | res.send({
29 | id: 'PERSONAL_FM',
30 | name: 'My FM',
31 | link: '/fm',
32 | size: songs.length,
33 | songs: songs.map(e => {
34 | var { album, artists } = e;
35 |
36 | return {
37 | id: e.id.toString(),
38 | name: e.name,
39 | duration: e.duration,
40 | album: {
41 | id: album.id.toString(),
42 | name: album.name,
43 | cover: album.picUrl,
44 | link: `/player/1/${album.id}`,
45 | },
46 | artists: artists.map(e => ({
47 | id: e.id.toString(),
48 | name: e.name,
49 | // Broken link
50 | link: e.id ? `/artist/${e.id}` : '',
51 | }))
52 | };
53 | }),
54 | });
55 | });
56 |
57 | module.exports = router;
58 |
--------------------------------------------------------------------------------
/server/router/lyrics.js:
--------------------------------------------------------------------------------
1 |
2 | import express from 'express';
3 | import axios from 'axios';
4 | import _debug from 'debug';
5 |
6 | const debug = _debug('dev:api');
7 | const error = _debug('dev:error');
8 | const router = express();
9 |
10 | router.get('/:id', async(req, res) => {
11 | debug('Handle request for /lyrics');
12 |
13 | var result = {};
14 | var id = req.params.id;
15 |
16 | debug('Params \'id\': %s', id);
17 |
18 | try {
19 | var response = await axios.get(`/lyric`, {
20 | params: {
21 | id,
22 | }
23 | });
24 | var data = response.data;
25 |
26 | if (data.code !== 200) {
27 | throw data;
28 | } else {
29 | let lyrics = data.lrc.lyric.split('\n');
30 |
31 | lyrics.map(e => {
32 | let match = e.match(/\[.+\]/);
33 |
34 | if (!match) {
35 | return;
36 | }
37 |
38 | let timestamp = match[0].replace(/\D/g, ':').replace(/^:|:$/g, '').split(':');
39 | let content = e.replace(/\[.+\]/, '');
40 | let times = parseInt(+timestamp[0] * 60 * 1000) + parseInt(+timestamp[1] * 1000) + parseInt(timestamp[2]);
41 |
42 | result[times] = content;
43 | });
44 | }
45 | } catch (ex) {
46 | error('Failed to get lyrics: %O', ex);
47 | }
48 |
49 | res.send(result);
50 | });
51 |
52 | module.exports = router;
53 |
--------------------------------------------------------------------------------
/server/router/playlist.js:
--------------------------------------------------------------------------------
1 |
2 | import express from 'express';
3 | import axios from 'axios';
4 | import _debug from 'debug';
5 |
6 | const debug = _debug('dev:api');
7 | const error = _debug('dev:error');
8 | const router = express();
9 | const limit = 50;
10 |
11 | router.get('/:type?/:offset?', async(req, res) => {
12 | debug('Handle request for /playlist');
13 |
14 | var playlists = [];
15 | var type = req.params.type || '全部';
16 | var offset = +req.params.offset || 0;
17 | var nextHref = '';
18 |
19 | debug('Params \'type\': %s', type);
20 |
21 | try {
22 | let response = await axios.get('/top/playlist', {
23 | params: {
24 | cat: type,
25 | limit,
26 | offset,
27 | order: 'hot',
28 | },
29 | });
30 | let data = response.data;
31 |
32 | if (data.code !== 200) {
33 | throw data;
34 | } else {
35 | data.playlists.map(e => {
36 | var creator = e.creator;
37 |
38 | playlists.push({
39 | id: e.id.toString(),
40 | name: e.name,
41 | played: e.playCount,
42 | size: e.trackCount,
43 | link: `/player/0/${e.id}`,
44 | cover: `${e.coverImgUrl}?param=100y100`,
45 | user: {
46 | id: creator.userId.toString(),
47 | name: creator.nickname,
48 | link: `/user/${creator.userId}`,
49 | },
50 | });
51 | });
52 | }
53 |
54 | if (data.more) {
55 | offset += limit;
56 | nextHref = `/api/playlist/${type}/${offset}`;
57 | }
58 | } catch (ex) {
59 | error('Failed to get playlist: %O', ex);
60 | }
61 |
62 | res.send({
63 | playlists,
64 | nextHref,
65 | });
66 | });
67 |
68 | module.exports = router;
69 |
--------------------------------------------------------------------------------
/server/router/top.js:
--------------------------------------------------------------------------------
1 |
2 | import express from 'express';
3 | import axios from 'axios';
4 | import _debug from 'debug';
5 |
6 | const debug = _debug('dev:api');
7 | const error = _debug('dev:error');
8 | const router = express();
9 |
10 | router.get('/:ids', async(req, res) => {
11 | debug('Handle request for /top');
12 |
13 | var list = [];
14 | var ids = req.params.ids;
15 |
16 | debug('Params \'ids\': %s', ids);
17 |
18 | ids = ids.split(',');
19 |
20 | try {
21 | list = await Promise.all(ids.map(async e => {
22 | let response = await axios.get(`/top/list?idx=${e}`);
23 | let data = response.data.result;
24 |
25 | if (response.data.code !== 200) {
26 | throw data;
27 | }
28 |
29 | return {
30 | name: data.name,
31 | played: data.playCount,
32 | updateTime: data.updateTime,
33 | size: data.trackCount,
34 | link: `/player/0/${data.id}`,
35 | cover: data.tracks[0]['album']['picUrl'],
36 | };
37 | }));
38 | } catch (ex) {
39 | error('Failed to get top list: %O', ex);
40 | }
41 |
42 | res.send({
43 | list,
44 | });
45 | });
46 |
47 | module.exports = router;
48 |
--------------------------------------------------------------------------------
/server/search/Baidu.js:
--------------------------------------------------------------------------------
1 |
2 | import request from 'request-promise-native';
3 | import _debug from 'debug';
4 |
5 | const debug = _debug('dev:plugin:Baidu');
6 | const error = _debug('dev:plugin:Baidu:error');
7 |
8 | export default async(keyword, artists) => {
9 | debug(`Search '${keyword} - ${artists}' use Baidu library.`);
10 |
11 | var response = await request({
12 | uri: 'http://sug.music.baidu.com/info/suggestion',
13 | qs: {
14 | word: [keyword].concat(artists.split(',')).join('+'),
15 | version: 2,
16 | from: 0,
17 | },
18 | json: true,
19 | });
20 | var songs = (response.data || {}).song;
21 | var song = (songs || []).find(e => artists.indexOf(e.artistname) > -1);
22 |
23 | if (!song) {
24 | debug('Nothing.');
25 | return Promise.reject();
26 | }
27 | response = await request({
28 | uri: 'http://music.baidu.com/data/music/fmlink',
29 | qs: {
30 | songIds: song.songid,
31 | type: 'mp3',
32 | rate: '320',
33 | },
34 | json: true,
35 | });
36 |
37 | try {
38 | song = {
39 | src: response.data.songList[0].songLink,
40 | };
41 |
42 | if (!song.src) {
43 | return Promise.reject();
44 | } else {
45 | // debug('Got a result \n"%O"', song);
46 | }
47 | } catch (ex) {
48 | // Anti-warnning
49 | error('Failed to get song: %O', ex);
50 | return Promise.reject();
51 | }
52 |
53 | return song;
54 | };
55 |
--------------------------------------------------------------------------------
/server/search/Kugou.js:
--------------------------------------------------------------------------------
1 |
2 | import request from 'request-promise-native';
3 | import _debug from 'debug';
4 | import md5 from 'md5';
5 |
6 | const debug = _debug('dev:plugin:Kugou');
7 | const error = _debug('dev:plugin:Kugou:error');
8 |
9 | async function getURL(hash) {
10 | var key = md5(`${hash}kgcloud`);
11 |
12 | var response = await request({
13 | url: `http://trackercdn.kugou.com/i/?acceptMp3=1&cmd=4&pid=6&hash=${hash}&key=${key}`,
14 | json: true,
15 | });
16 |
17 | if (response.error
18 | || response.status !== 1) {
19 | debug('Nothing.');
20 | return Promise.reject();
21 | } else {
22 | return response.url;
23 | }
24 | }
25 |
26 | export default async(keyword, artists) => {
27 | debug(`Search '${keyword} - ${artists}' use Kugou library.`);
28 |
29 | var response = await request({
30 | uri: 'http://mobilecdn.kugou.com/api/v3/search/song',
31 | qs: {
32 | format: 'json',
33 | keyword: [keyword].concat(artists.split(',')).join('+'),
34 | page: 1,
35 | pagesize: 1,
36 | showtype: 1,
37 | },
38 | json: true,
39 | });
40 |
41 | var data = response.data;
42 |
43 | if (response.status !== 1
44 | || data.info.length === 0) {
45 | error('Nothing.');
46 | return Promise.reject();
47 | }
48 |
49 | for (let e of data.info) {
50 | if (artists.split(',').find(artist => e.singername.indexOf(artist)) === -1) {
51 | continue;
52 | }
53 |
54 | // debug('Got a result \n"%O"', e);
55 | try {
56 | let song = {
57 | src: await getURL(e['320hash'] || e['hash'])
58 | };
59 |
60 | // debug('%O', song);
61 | return song;
62 | } catch (ex) {
63 | error('Failed to get song: %O', ex);
64 | return Promise.reject();
65 | }
66 | }
67 | };
68 |
--------------------------------------------------------------------------------
/server/search/Xiami.js:
--------------------------------------------------------------------------------
1 |
2 | import request from 'request-promise-native';
3 | import _debug from 'debug';
4 |
5 | const debug = _debug('dev:plugin:Xiami');
6 | const error = _debug('dev:plugin:Xiami:error');
7 | const headers = {
8 | cookie: 'user_from=2;XMPLAYER_addSongsToggler=0;XMPLAYER_isOpen=0;_xiamitoken=cb8bfadfe130abdbf5e2282c30f0b39a;',
9 | referer: 'http://h.xiami.com/',
10 | user_agent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36',
11 | };
12 |
13 | export default async(keyword, artists) => {
14 | debug(`Search '${keyword} - ${artists}' use Xiami library.`);
15 |
16 | var response = await request({
17 | uri: 'http://api.xiami.com/web',
18 | qs: {
19 | v: '2.0',
20 | key: [keyword].concat(artists.split(',')).join('+'),
21 | limit: 100,
22 | page: 1,
23 | r: 'search/songs',
24 | app_key: 1,
25 | },
26 | json: true,
27 | headers,
28 | });
29 |
30 | var data = response.data;
31 |
32 | if (response.state !== 0
33 | || data.songs.length === 0) {
34 | error('Nothing.');
35 | return Promise.reject();
36 | }
37 |
38 | for (let e of data.songs) {
39 | if (artists.split(',').find(artist => e.artist_name.indexOf(artist)) === -1) {
40 | continue;
41 | }
42 |
43 | let song = {
44 | src: e.listen_file,
45 | };
46 |
47 | if (!song.src) {
48 | return Promise.reject();
49 | } else {
50 | // debug('Got a result \n"%O"', song);
51 | return song;
52 | }
53 | }
54 | };
55 |
--------------------------------------------------------------------------------
/server/search/index.js:
--------------------------------------------------------------------------------
1 |
2 | import storage from 'electron-json-storage';
3 | import QQ from './QQ';
4 | import Kugou from './Kugou';
5 | import Baidu from './Baidu';
6 | import Xiami from './Xiami';
7 | import _debug from 'debug';
8 |
9 | const debug = _debug('dev:plugin');
10 |
11 | async function getEnginers() {
12 | return new Promise(resolve => {
13 | storage.get('preferences', (err, data) => {
14 | var fallback = {
15 | 'QQ': true,
16 | 'Xiami': true,
17 | 'Kugou': false,
18 | 'Baidu': true,
19 | };
20 |
21 | var enginers = fallback;
22 |
23 | if (!err) {
24 | enginers = data.enginers || fallback;
25 | }
26 |
27 | resolve(enginers);
28 | });
29 | });
30 | }
31 |
32 | export default async(keyword, artists) => {
33 | var enginers = await getEnginers();
34 | var plugins = [];
35 |
36 | if (enginers['QQ']) {
37 | plugins.push(QQ);
38 | debug('Loaded plugin QQ');
39 | }
40 |
41 | if (enginers['Xiami']) {
42 | plugins.push(Xiami);
43 | debug('Loaded plugin Xiami');
44 | }
45 |
46 | if (enginers['Kugou']) {
47 | plugins.push(Kugou);
48 | debug('Loaded plugin Kugou');
49 | }
50 |
51 | if (enginers['Baidu']) {
52 | plugins.push(Baidu);
53 | debug('Loaded plugin Baidu');
54 | }
55 |
56 | debug('Plugin has loaded, search: \'%s\', \'%s\'', keyword, artists);
57 |
58 | return Promise.all(
59 | plugins.map(e => {
60 | // If a request failed will keep waiting for other possible successes, if a request successed,
61 | // treat it as a rejection so Promise.all immediate break.
62 | return e(keyword, artists).then(
63 | val => Promise.reject(val),
64 | err => Promise.resolve(err)
65 | );
66 | })
67 | ).then(
68 | errs => Promise.reject(errs),
69 | val => Promise.resolve(val),
70 | );
71 | };
72 |
--------------------------------------------------------------------------------
/server/search/test.js:
--------------------------------------------------------------------------------
1 |
2 | import search from './index';
3 |
4 | async function test() {
5 | var res = {};
6 | try {
7 | res = await search('体面', '于文文');
8 | console.log(res);
9 | } catch (ex) {
10 | console.error(ex);
11 | }
12 | return res;
13 | }
14 |
15 | test();
16 |
--------------------------------------------------------------------------------
/src/assets/WeChat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/src/assets/WeChat.jpg
--------------------------------------------------------------------------------
/src/assets/WeChatGroup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/src/assets/WeChatGroup.png
--------------------------------------------------------------------------------
/src/assets/close-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/src/assets/close-white.png
--------------------------------------------------------------------------------
/src/assets/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/src/assets/close.png
--------------------------------------------------------------------------------
/src/assets/dock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/src/assets/dock.png
--------------------------------------------------------------------------------
/src/assets/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/src/assets/github.png
--------------------------------------------------------------------------------
/src/assets/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/src/assets/loading.gif
--------------------------------------------------------------------------------
/src/assets/notplaying.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/src/assets/notplaying.png
--------------------------------------------------------------------------------
/src/assets/playing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/xbyjMusic/9232f35247bdc828805fd6485958b7ee0a7529e1/src/assets/playing.png
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | xbyjMusic
7 |
8 |
9 |
10 |
11 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/js/components/AboutMe/index.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component } from 'react';
3 | import { inject, observer } from 'mobx-react';
4 | import injectSheet from 'react-jss';
5 |
6 | import classes from './classes';
7 | import SyncCarousel from 'components/SyncCarousel';
8 |
9 | @inject(stores => ({
10 | show: stores.aboutme.show,
11 | close: () => stores.aboutme.toggle(false),
12 | }))
13 | @observer
14 | class AboutMe extends Component {
15 | componentWillReceiveProps(nextProps) {
16 | if (nextProps.show === true) {
17 | setTimeout(() => {
18 | this.refs.container.focus();
19 | });
20 | }
21 | }
22 |
23 | // 渲染同步轮播图SyncCarousel~
24 | renderBannerlist() {
25 | var bannerList = [{pic: 'assets/github.png'}, {pic: 'assets/WeChat.jpg'}, {pic: 'assets/WeChatGroup.png'}];
26 | return (
27 |
28 | );
29 | }
30 |
31 | render() {
32 | var { classes, show, close } = this.props;
33 |
34 | if (!show) {
35 | return false;
36 | }
37 |
38 | return (
39 | e.keyCode === 27 && this.props.close()}
43 | ref="container"
44 | tabIndex="-1">
45 |
48 |
49 |
50 | {
51 | this.renderBannerlist()
52 | }
53 |
54 |
55 |
56 | );
57 | }
58 | }
59 |
60 | export default injectSheet(classes)(AboutMe);
61 |
--------------------------------------------------------------------------------
/src/js/components/Carousel/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Slider from 'react-slick';
3 | // 引入react-slick轮播图的样式
4 | import '../../../../node_modules/slick-carousel/slick/slick.css';
5 | import '../../../../node_modules/slick-carousel/slick/slick-theme.css';
6 |
7 | // function SampleNextArrow(props) {
8 | // const { className, style, onClick } = props;
9 | // return (
10 | //
15 | // );
16 | // }
17 |
18 | // 首页的大轮播图
19 | export default class Carousel extends Component {
20 | render() {
21 | var bannerList = this.props.bannerList;
22 | var picWidth = this.props.picWidth;
23 | // console.log('picWidth', picWidth);
24 | const settings = {
25 | // className: 'center',
26 | centerMode: true,
27 | centerPadding: '85px',
28 | dots: true,
29 | infinite: true,
30 | swipeToSlide: true,
31 | // fade: true,
32 | slidesToShow: 1,
33 | slidesToScroll: 1,
34 | autoplay: true,
35 | autoplaySpeed: 5000,
36 | pauseOnHover: true,
37 | focusOnSelect: true,
38 | // nextArrow: ,
39 | };
40 | return (
41 |
42 |
43 | {
44 | // 轮播图列表
45 | bannerList.map((e, index) => {
46 | return (
47 |
49 | {
50 |

51 | }
52 | {/*
{index}
*/}
53 |
54 | );
55 | })
56 | }
57 |
58 |
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/js/components/LeftMenu/classes.js:
--------------------------------------------------------------------------------
1 |
2 | export default theme => ({
3 |
4 | aside: {
5 | display: 'inline-block',
6 | position: 'absolute',
7 | left: 0,
8 | top: 60,
9 | width: 200,
10 | height: 'calc(100vh - 124px)',
11 | color: '#333',
12 | borderRight: '1px solid #ddd',
13 | background: 'linear-gradient(to right,#e3e3e3,#f6f6f6)',
14 | overflow: 'hidden',
15 | overflowY: 'auto',
16 |
17 | '& a': {
18 | color: '#333',
19 | },
20 | },
21 |
22 | leftMenu: {
23 | margin: '4px 12px 18px'
24 | },
25 |
26 | leftMenuSpan: {
27 | fontSize: 12,
28 | color: '#666',
29 | },
30 |
31 | leftMenuItem: {
32 | fontSize: 14,
33 | margin: '10px 8px',
34 |
35 | '& i': {
36 | display: 'inline-block',
37 | width: 16,
38 | marginRight: 8,
39 | fontSize: 17,
40 | textAlign: 'center',
41 | },
42 |
43 | '& p': {
44 | display: 'inline-block',
45 | width: 135,
46 | height: 16,
47 | margin: 0,
48 | overflow: 'hidden',
49 | whiteSpace: 'nowrap',
50 | textOverflow: 'ellipsis',
51 | },
52 | },
53 |
54 | });
55 |
--------------------------------------------------------------------------------
/src/js/components/Ripple/PlayerMode.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component } from 'react';
3 | import { inject, observer } from 'mobx-react';
4 | import injectSheet from 'react-jss';
5 |
6 | import classes from './classes';
7 | import { PLAYER_LOOP, PLAYER_SHUFFLE, PLAYER_REPEAT } from 'stores/controller';
8 |
9 | @inject(stores => ({
10 | mode: stores.controller.mode,
11 | }))
12 | @observer
13 | class PlayerMode extends Component {
14 | componentWillUpdate() {
15 | this.animationDone();
16 | }
17 |
18 | componentDidUpdate() {
19 | this.refs.container.classList.add(this.props.classes.animated);
20 | }
21 |
22 | animationDone() {
23 | this.refs.container.classList.remove(this.props.classes.animated);
24 | }
25 |
26 | renderIndicator(mode) {
27 | switch (mode) {
28 | case PLAYER_SHUFFLE:
29 | return ;
30 |
31 | case PLAYER_REPEAT:
32 | return ;
33 |
34 | case PLAYER_LOOP:
35 | return ;
36 | }
37 | }
38 |
39 | render() {
40 | var { classes, mode } = this.props;
41 |
42 | return (
43 | this.animationDone()}
46 | ref="container">
47 | {
48 | this.renderIndicator(mode)
49 | }
50 |
51 | );
52 | }
53 | }
54 |
55 | export default injectSheet(classes)(PlayerMode);
56 |
--------------------------------------------------------------------------------
/src/js/components/Ripple/PlayerNavigation.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component } from 'react';
3 | import { ipcRenderer } from 'electron';
4 | import injectSheet from 'react-jss';
5 |
6 | import classes from './classes';
7 |
8 | class PlayerNavigation extends Component {
9 | state = {
10 | // true: prev, false: next
11 | direction: true,
12 | };
13 |
14 | componentWillUpdate() {
15 | this.animationDone();
16 | }
17 |
18 | componentDidUpdate() {
19 | this.refs.container.classList.add(this.props.classes.animated);
20 | }
21 |
22 | animationDone() {
23 | this.shouldUpdate = false;
24 | this.refs.container.classList.remove(this.props.classes.animated);
25 | }
26 |
27 | shouldComponentUpdate() {
28 | return !!this.shouldUpdate;
29 | }
30 |
31 | componentDidMount() {
32 | ipcRenderer.on('player-previous', () => {
33 | this.shouldUpdate = true;
34 | this.setState({
35 | direction: true,
36 | });
37 | });
38 |
39 | ipcRenderer.on('player-next', () => {
40 | this.shouldUpdate = true;
41 | this.setState({
42 | direction: false,
43 | });
44 | });
45 | }
46 |
47 | render() {
48 | var classes = this.props.classes;
49 |
50 | return (
51 | this.animationDone()}
54 | ref="container">
55 | {
56 | this.state.direction
57 | ?
58 | :
59 | }
60 |
61 | );
62 | }
63 | }
64 |
65 | export default injectSheet(classes)(PlayerNavigation);
66 |
--------------------------------------------------------------------------------
/src/js/components/Ripple/PlayerStatus.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component } from 'react';
3 | import { inject, observer } from 'mobx-react';
4 | import injectSheet from 'react-jss';
5 |
6 | import classes from './classes';
7 |
8 | @inject(stores => ({
9 | playing: stores.controller.playing,
10 | }))
11 | @observer
12 | class PlayerStatus extends Component {
13 | componentWillUpdate() {
14 | this.animationDone();
15 | }
16 |
17 | componentWillReceiveProps(nextProps) {
18 | if (nextProps.playing !== this.props.playing) {
19 | // Force show the animation
20 | this.animationDone();
21 | }
22 | }
23 |
24 | componentDidUpdate() {
25 | this.refs.container.classList.add(this.props.classes.animated);
26 | }
27 |
28 | animationDone() {
29 | this.refs.container.classList.remove(this.props.classes.animated);
30 | }
31 |
32 | render() {
33 | var { classes, playing } = this.props;
34 |
35 | return (
36 | this.animationDone()}
39 | ref="container">
40 | {
41 | playing
42 | ?
43 | :
44 | }
45 |
46 | );
47 | }
48 | }
49 |
50 | export default injectSheet(classes)(PlayerStatus);
51 |
--------------------------------------------------------------------------------
/src/js/components/Ripple/VolumeUpDown.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component } from 'react';
3 | import { inject, observer } from 'mobx-react';
4 | import { ipcRenderer } from 'electron';
5 | import injectSheet from 'react-jss';
6 |
7 | import classes from './classes';
8 |
9 | @inject(stores => ({
10 | isMuted: () => stores.preferences.volume === 0,
11 | }))
12 | @observer
13 | class VolumeUpDown extends Component {
14 | state = {
15 | // true: up, false: down
16 | direction: true,
17 | };
18 |
19 | componentWillUpdate() {
20 | this.animationDone();
21 | }
22 |
23 | componentDidUpdate() {
24 | this.refs.container.classList.add(this.props.classes.animated);
25 | }
26 |
27 | animationDone() {
28 | this.refs.container.classList.remove(this.props.classes.animated);
29 | }
30 |
31 | componentDidMount() {
32 | ipcRenderer.on('player-volume-up', () => {
33 | this.setState({
34 | direction: true,
35 | });
36 | });
37 |
38 | ipcRenderer.on('player-volume-down', () => {
39 | this.setState({
40 | direction: false,
41 | });
42 | });
43 | }
44 |
45 | render() {
46 | var { classes, isMuted } = this.props;
47 |
48 | return (
49 | this.animationDone()}
52 | ref="container">
53 | {
54 | isMuted()
55 | ?
60 | : (
61 | this.state.direction
62 | ?
63 | :
64 | )
65 | }
66 |
67 | );
68 | }
69 | }
70 |
71 | export default injectSheet(classes)(VolumeUpDown);
72 |
--------------------------------------------------------------------------------
/src/js/components/Ripple/classes.js:
--------------------------------------------------------------------------------
1 |
2 | import helper from 'utils/helper';
3 |
4 | export default theme => {
5 | var animationName = helper.randomName();
6 |
7 | return {
8 | container: {
9 | position: 'fixed',
10 | right: '50%',
11 | top: '50%',
12 | width: 64,
13 | height: 64,
14 | lineHeight: '64px',
15 | borderRadius: 64,
16 | marginRight: -32,
17 | marginTop: -32,
18 | color: '#fff',
19 | fontSize: 24,
20 | textAlign: 'center',
21 | background: '#000',
22 | boxShadow: '0 30px 80px 0 rgba(97, 45, 45, .25)',
23 | zIndex: 999,
24 | opacity: 0,
25 | visibility: 'hidden',
26 | },
27 |
28 | animated: {
29 | animationName,
30 | animationDuration: '800ms',
31 | animationIterationCount: 1,
32 | },
33 |
34 | [`@keyframes ${animationName}`]: {
35 | '0%': {
36 | opacity: 0,
37 | transform: 'scale(.8)',
38 | visibility: 'hidden',
39 | },
40 |
41 | '40%': {
42 | opacity: 1,
43 | transform: 'scale(1)',
44 | visibility: 'visible',
45 | },
46 |
47 | '100%': {
48 | opacity: 0,
49 | transform: 'scale(1.2)',
50 | visibility: 'hidden',
51 | },
52 | },
53 | };
54 | };
55 |
--------------------------------------------------------------------------------
/src/js/components/SyncCarousel/classes.js:
--------------------------------------------------------------------------------
1 |
2 | export default theme => ({
3 |
4 | sliderOne: {
5 |
6 | '& img': {
7 | width: 200,
8 | cursor: 'pointer',
9 | margin: '0 auto',
10 | },
11 |
12 | '& img:hover': {
13 | boxShadow: '0 1px 24px rgba(0, 0, 0, .24)',
14 | },
15 | },
16 |
17 | sliderTwo: {
18 | '& h3': {
19 | fontSize: 14,
20 | textAlign: 'center',
21 | color: '#555',
22 | fontFamily: 'monospace',
23 | }
24 | }
25 | });
26 |
--------------------------------------------------------------------------------
/src/js/components/SyncCarousel/index.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component } from 'react';
3 | import Slider from 'react-slick';
4 | import injectSheet from 'react-jss';
5 |
6 | // 引入react-slick轮播图的样式
7 | import '../../../../node_modules/slick-carousel/slick/slick.css';
8 | import '../../../../node_modules/slick-carousel/slick/slick-theme.css';
9 | import classes from './classes';
10 |
11 | // AboutMe组件的同步轮播图
12 | class AsNavFor extends Component {
13 | constructor(props) {
14 | super(props);
15 | this.state = {
16 | nav1: null,
17 | nav2: null
18 | };
19 | }
20 |
21 | componentDidMount() {
22 | this.setState({
23 | nav1: this.slider1,
24 | nav2: this.slider2
25 | });
26 | }
27 |
28 | render() {
29 | var {bannerList, classes} = this.props;
30 | return (
31 |
32 |
(this.slider1 = slider)}
35 | autoplay={true}
36 | autoplaySpeed={5000}
37 | pauseOnHover={true}
38 | >
39 |
46 |
47 | {
48 |

49 | }
50 |
51 |
52 | {
53 |

54 | }
55 |
56 |
57 |
(this.slider2 = slider)}
60 | autoplay={true}
61 | autoplaySpeed={5000}
62 | pauseOnHover={true}
63 | slidesToShow={1}
64 | >
65 |
66 |
欢迎star、fork一起完善本项目
67 |
68 |
69 |
关注微信公众号
70 |
71 |
72 |
加入技术交流群(备注自己GitHub账号)
73 |
74 |
75 |
76 | );
77 | }
78 | }
79 |
80 | export default injectSheet(classes)(AsNavFor);
81 |
--------------------------------------------------------------------------------
/src/js/pages/FM/classes.js:
--------------------------------------------------------------------------------
1 |
2 | import colors from 'utils/colors';
3 |
4 | export default theme => {
5 | return {
6 | container: {
7 | backgroundColor: 'white',
8 | zIndex: 99,
9 |
10 | '& main': {
11 | position: 'fixed',
12 | top: 60,
13 | left: 200,
14 | width: 'calc(100vw - 200px)',
15 | height: 'calc(100vh - 124px)',
16 | overflowY: 'auto',
17 | },
18 | },
19 |
20 | unavailable: {
21 | display: 'flex',
22 | width: '100vw',
23 | height: 'calc(100vh - 50px)',
24 | justifyContent: 'center',
25 | flexDirection: 'column',
26 | alignItems: 'center',
27 | background: 'black',
28 |
29 | '& p': {
30 | fontWeight: 100,
31 | fontSize: 24,
32 | color: colors.pallet.dribbble
33 | },
34 |
35 | '& a': {
36 | marginTop: 20,
37 | padding: '8px 12px',
38 | border: 0,
39 | fontSize: 14,
40 | fontWeight: 'lighter',
41 | color: 'white',
42 | background: 'transparent',
43 | letterSpacing: .5,
44 | textTransform: 'uppercase',
45 | borderBottom: 'thin solid white',
46 | outline: 0,
47 | }
48 | },
49 |
50 | comments: {
51 | position: 'absolute',
52 | width: '100%',
53 | background: '#fafafa',
54 | zIndex: '-1',
55 | },
56 | };
57 | };
58 |
--------------------------------------------------------------------------------
/src/js/pages/Layout.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component } from 'react';
3 | import injectSheet from 'react-jss';
4 | import { inject, observer } from 'mobx-react';
5 | import clazz from 'classname';
6 |
7 | import Offline from 'ui/Offline';
8 | import Loader from 'ui/Loader';
9 | import lastfm from 'utils/lastfm';
10 | import AudioPlayer from 'components/AudioPlayer';
11 | import Search from 'components/Search';
12 | import AboutMe from 'components/AboutMe';
13 | import Playing from 'components/Playing';
14 | import VolumeUpDown from 'components/Ripple/VolumeUpDown';
15 | import PlayerNavigation from 'components/Ripple/PlayerNavigation';
16 | import PlayerMode from 'components/Ripple/PlayerMode';
17 | import PlayerStatus from 'components/Ripple/PlayerStatus';
18 |
19 | const classes = {
20 | container: {
21 | position: 'fixed',
22 | left: 0,
23 | top: 0,
24 | width: '100vw',
25 | height: '100vh',
26 | },
27 |
28 | mask: {
29 | filter: 'blur(10px)',
30 | },
31 | };
32 |
33 | @inject(stores => ({
34 | initialized: stores.me.initialized,
35 | init: async() => {
36 | await stores.preferences.init();
37 | await stores.me.init();
38 |
39 | var { username, password } = stores.preferences.lastfm;
40 |
41 | await lastfm.initialize(username, password);
42 | },
43 | hasLogin: stores.me.hasLogin,
44 | searching: stores.search.show,
45 | }))
46 | @observer
47 | class Layout extends Component {
48 | state = {
49 | offline: false,
50 | };
51 |
52 | async componentWillMount() {
53 | await this.props.init();
54 | }
55 |
56 | componentDidMount() {
57 | window.addEventListener('offline', () => {
58 | this.setState({
59 | offline: true,
60 | });
61 | });
62 |
63 | window.addEventListener('online', () => {
64 | this.setState({
65 | offline: false,
66 | });
67 | });
68 | }
69 |
70 | render() {
71 | var { classes, initialized, searching } = this.props;
72 |
73 | if (this.state.offline) {
74 | return ;
75 | }
76 |
77 | // Wait for app has initialized
78 | if (!initialized) {
79 | return ;
80 | }
81 |
82 | return (
83 |
86 |
89 | {this.props.children}
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | );
102 | }
103 | }
104 |
105 | export default injectSheet(classes)(Layout);
106 |
--------------------------------------------------------------------------------
/src/js/pages/Player/Search/classes.js:
--------------------------------------------------------------------------------
1 |
2 | export default theme => ({
3 | container: {
4 | position: 'absolute',
5 | top: 34,
6 | right: 0,
7 | width: '100%',
8 | height: '100%',
9 | background: '#fff',
10 | overflow: 'hidden',
11 | zIndex: 99,
12 |
13 | '& header': {
14 | display: 'flex',
15 | justifyContent: 'space-between',
16 | alignItems: 'center',
17 | height: 66,
18 | paddingLeft: 24,
19 | paddingRight: 16,
20 | boxShadow: 'inset 0 0 0.7px 0 rgba(0, 0, 0, .5)',
21 | },
22 |
23 | '& header input': {
24 | height: 40,
25 | lineHeight: '40px',
26 | width: '100%',
27 | fontFamily: 'HelveticaNeue-UltraLight',
28 | fontSize: 14,
29 | border: 0,
30 | outline: 0,
31 | }
32 | },
33 |
34 | close: {
35 | height: 32,
36 | cursor: 'pointer',
37 | },
38 |
39 | list: {
40 | '& ul': {
41 | height: 'calc(100vh - 52px - 66px) !important',
42 | }
43 | }
44 | });
45 |
--------------------------------------------------------------------------------
/src/js/pages/Player/Search/index.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component } from 'react';
3 | import PropTypes from 'prop-types';
4 | import injectSheet from 'react-jss';
5 |
6 | import classes from './classes';
7 |
8 | class Search extends Component {
9 | static propsTypes = {
10 | show: PropTypes.bool.isRequired,
11 | close: PropTypes.func.isRequired,
12 | };
13 |
14 | pressEscExit(e) {
15 | if (e.keyCode === 27) {
16 | this.props.close();
17 | }
18 | }
19 |
20 | render() {
21 | var { classes, show, close, filter, children } = this.props;
22 |
23 | if (!show) {
24 | return false;
25 | }
26 |
27 | return (
28 | this.pressEscExit(e)}>
31 |
43 |
44 |
45 | {children}
46 |
47 |
48 | );
49 | }
50 | }
51 |
52 | export default injectSheet(classes)(Search);
53 |
--------------------------------------------------------------------------------
/src/js/pages/Song/classes.js:
--------------------------------------------------------------------------------
1 |
2 | export default theme => ({
3 | container: {
4 | backgroundColor: 'white',
5 | zIndex: 99,
6 |
7 | '& main': {
8 | position: 'fixed',
9 | top: 60,
10 | width: 'calc(100vw)',
11 | height: 'calc(100vh - 124px)',
12 | overflowY: 'auto',
13 | },
14 | },
15 |
16 | comments: {
17 | position: 'absolute',
18 | width: '100%',
19 | background: '#fafafa',
20 | zIndex: '-1',
21 | },
22 |
23 | recommend: {
24 | position: 'absolute',
25 | top: 60,
26 | right: 10,
27 | width: '35vw',
28 | height: '100%',
29 | color: '#000',
30 | },
31 |
32 | recommendTitleWrap: {
33 | display: 'inline-block',
34 | width: 310,
35 | height: 31,
36 | borderBottom: '1px solid #ccc'
37 | },
38 |
39 | recommendTitle: {
40 | color: '#222',
41 | fontSize: 20,
42 | marginRight: 10,
43 | paddingBottom: 5,
44 | borderBottom: '4px solid #ccc'
45 | },
46 |
47 | recomPlayList: {
48 | position: 'absolute',
49 | left: 0,
50 | top: 0,
51 | height: 200,
52 |
53 | '& a': {
54 | color: '#666',
55 | width: 310,
56 | margin: '18px 0 0 0',
57 |
58 | '&:hover': {
59 | background: '#f0f0f0',
60 | }
61 | },
62 |
63 | '& figure': {
64 | display: 'inline-block',
65 | },
66 | },
67 |
68 | recomPlayListContent: {
69 | position: 'absolute',
70 | fontSize: 12,
71 | lineHeight: '6px',
72 | margin: '-5px 0 0 10px',
73 | },
74 |
75 | simiSong: {
76 | position: 'absolute',
77 | left: 0,
78 | top: 270,
79 | height: 350,
80 |
81 | '& a': {
82 | color: '#666',
83 | width: 310,
84 | margin: '18px 0 0 0',
85 |
86 | '&:hover': {
87 | background: '#f0f0f0',
88 | }
89 | },
90 |
91 | '& figure': {
92 | display: 'inline-block',
93 | },
94 | },
95 |
96 | users: {
97 | position: 'absolute',
98 | left: 0,
99 | top: 670,
100 | height: 350,
101 |
102 | '& a': {
103 | color: '#666',
104 | width: 310,
105 | margin: '18px 0 0 0',
106 |
107 | '&:hover': {
108 | background: '#f0f0f0',
109 | }
110 | },
111 |
112 | '& figure': {
113 | display: 'inline-block',
114 | borderRadius: 40,
115 | },
116 | },
117 |
118 | recomUserContent: {
119 | position: 'absolute',
120 | fontSize: 12,
121 | lineHeight: '6px',
122 | margin: '10px 0 0 10px',
123 | width: 260,
124 | },
125 | });
126 |
--------------------------------------------------------------------------------
/src/js/pages/Top/classes.js:
--------------------------------------------------------------------------------
1 |
2 | export default theme => ({
3 | container: {
4 | },
5 |
6 | column: {
7 | display: 'inline-block',
8 | height: 'calc((100vh - 124px))',
9 | marginTop: 60,
10 | },
11 |
12 | item: {
13 | position: 'relative',
14 | display: 'flex',
15 | height: 'calc((100vh - 124px) / 2)',
16 | width: 'calc((100vh - 124px) / 2)',
17 | alignItems: 'center',
18 | justifyContent: 'center',
19 | flexDirection: 'column',
20 | color: '#fff',
21 | textAlign: 'center',
22 | overflow: 'hidden',
23 |
24 | '& p': {
25 | fontSize: 24,
26 | },
27 |
28 | '& img:first-of-type': {
29 | width: '100%',
30 | height: '100%',
31 | transition: '.2s ease-in',
32 | },
33 |
34 | '&:after': {
35 | content: '""',
36 | position: 'absolute',
37 | left: 0,
38 | top: 0,
39 | display: 'block',
40 | height: '100%',
41 | width: '100%',
42 | background: 'rgba(0, 0, 0, .7)',
43 | visibility: 'visible',
44 | transition: '.2s',
45 | },
46 |
47 | '&:hover img:first-of-type': {
48 | transform: 'scale(1.1)',
49 | },
50 |
51 | '&:hover:after': {
52 | background: 'rgba(0, 0, 0, .3)',
53 | }
54 | },
55 |
56 | info: {
57 | position: 'absolute',
58 | color: '#fff',
59 | zIndex: 1,
60 | },
61 |
62 | line: {
63 | height: 4,
64 | width: 170,
65 | margin: '0 auto',
66 | marginTop: -10,
67 | marginBottom: 24,
68 | background: '#fff',
69 | }
70 | });
71 |
--------------------------------------------------------------------------------
/src/js/routes.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { IndexRoute, Route } from 'react-router';
4 |
5 | import Layout from './pages/Layout';
6 | import Home from './pages/Home';
7 | import Login from './pages/Login';
8 | import Player from './pages/Player';
9 | import User from './pages/User';
10 | import Artist from './pages/Artist';
11 | import Top from './pages/Top';
12 | import Playlist from './pages/Playlist';
13 | import FM from './pages/FM';
14 | import Preferences from './pages/Preferences';
15 | import Song from './pages/Song';
16 | import dailyPlayer from './pages/dailyPlayer';
17 | import stores from 'stores';
18 |
19 | function requireAuth(nextState, replace) {
20 | // console.warn(stores.me.hasLogin());
21 | if (!stores.me.hasLogin()) {
22 | replace({
23 | pathname: '/login/0'
24 | });
25 | }
26 | }
27 |
28 | export default () => {
29 | return (
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | );
44 | };
45 |
--------------------------------------------------------------------------------
/src/js/stores/aboutme.js:
--------------------------------------------------------------------------------
1 |
2 | import { observable, action } from 'mobx';
3 |
4 | class AboutMe {
5 | @observable show = false;
6 |
7 | @action toggle(show = !self.show) {
8 | self.show = show;
9 | }
10 | }
11 |
12 | const self = new AboutMe();
13 | export default self;
14 |
--------------------------------------------------------------------------------
/src/js/stores/artist.js:
--------------------------------------------------------------------------------
1 |
2 | import { observable, action } from 'mobx';
3 | import axios from 'axios';
4 |
5 | class Artist {
6 | @observable loading = true;
7 |
8 | // Profile of the artist
9 | @observable profile = {};
10 |
11 | // All albums of artist
12 | @observable albums = [];
13 |
14 | // Similar artists
15 | @observable similar = [];
16 |
17 | // Contains 'id' and 'songs'
18 | @observable playlist = {
19 | songs: [],
20 | };
21 |
22 | @action async getArtist(id) {
23 | self.loading = true;
24 |
25 | var response = await axios.get(`/api/artist/${id}`);
26 | var data = response.data;
27 |
28 | if (data) {
29 | self.profile = data.profile;
30 | self.playlist = data.playlist;
31 | self.albums = data.albums;
32 | self.similar = data.similar;
33 | }
34 |
35 | self.loading = false;
36 | }
37 |
38 | @action async follow(followed, id = self.profile.id) {
39 | var response = await axios.get(
40 | followed
41 | ? `/api/artist/unfollow/${id}`
42 | : `/api/artist/follow/${id}`
43 | );
44 | var data = response.data;
45 |
46 | if (data.success) {
47 | self.profile = Object.assign({}, self.profile, {
48 | followed: !followed,
49 | });
50 | }
51 |
52 | return data.success;
53 | }
54 | }
55 |
56 | const self = new Artist();
57 | export default self;
58 |
--------------------------------------------------------------------------------
/src/js/stores/comments.js:
--------------------------------------------------------------------------------
1 |
2 | import { observable, action } from 'mobx';
3 | import axios from 'axios';
4 |
5 | import controller from './controller';
6 |
7 | class Comments {
8 | @observable loading = true;
9 | @observable show = false;
10 | @observable hotList = [];
11 | @observable newestList = [];
12 | @observable total = 0;
13 | @observable song = {
14 | album: {},
15 | artist: [],
16 | };
17 |
18 | nextHref = '';
19 |
20 | @action toggle(show = !self.show) {
21 | self.show = show;
22 | }
23 |
24 | @action async getList(song) {
25 | self.loading = true;
26 |
27 | var response = await axios.get(`/api/comments/${song.id}`);
28 | var data = response.data;
29 |
30 | self.song = song;
31 | self.hotList = data.hotList;
32 | self.newestList = data.newestList;
33 | self.total = data.total;
34 | self.nextHref = data.nextHref;
35 | self.loading = false;
36 | }
37 |
38 | @action async like(id, liked) {
39 | var response = await axios.get(`/api/comments/like/${id}/${controller.song.id}/${+liked}`);
40 | var data = response.data;
41 |
42 | if (data.success === true) {
43 | let comment = [...self.hotList.slice(), ...self.newestList.slice()].find(e => e.commentId === id);
44 |
45 | comment.likedCount += liked ? 1 : -1;
46 | comment.liked = liked;
47 | }
48 | }
49 |
50 | @action async loadmore() {
51 | if (!self.nextHref) {
52 | return;
53 | }
54 |
55 | var response = await axios.get(self.nextHref);
56 | var data = response.data;
57 |
58 | self.newestList.push(...data.newestList);
59 | self.nextHref = data.nextHref;
60 | }
61 | }
62 |
63 | const self = new Comments();
64 | export default self;
65 |
--------------------------------------------------------------------------------
/src/js/stores/dailyplayer.js:
--------------------------------------------------------------------------------
1 |
2 | import { observable, action } from 'mobx';
3 | import axios from 'axios';
4 |
5 | import me from './me';
6 |
7 | class dailyPlayer {
8 | @observable loading = true;
9 | @observable list = [];
10 |
11 | @action async load() {
12 | var res;
13 |
14 | try {
15 | res = await axios.get(`api/dailyplayer/${me.profile.userId}`);
16 | } catch (e) {
17 | console.log('请求日推歌曲数据出错', e);
18 | };
19 | self.list = res.data.list[0];
20 | return self.list;
21 | }
22 |
23 | @action async getList() {
24 | self.loading = true;
25 | await self.load();
26 |
27 | // Just call once for init player
28 | self.getList = Function;
29 | self.loading = false;
30 | }
31 | }
32 |
33 | const self = new dailyPlayer();
34 | export default self;
35 |
--------------------------------------------------------------------------------
/src/js/stores/fm.js:
--------------------------------------------------------------------------------
1 |
2 | import { observable, action } from 'mobx';
3 | import axios from 'axios';
4 |
5 | import controller from './controller';
6 |
7 | class FM {
8 | @observable loading = true;
9 | @observable song = {};
10 | @observable playlist = {
11 | songs: [],
12 | };
13 |
14 | preload() {
15 | controller.changeMode();
16 | self.shuffle();
17 | self.preload = Function;
18 | }
19 |
20 | @action async shuffle() {
21 | self.loading = true;
22 |
23 | var response = await axios.get(`/api/fm`);
24 | self.playlist = response.data;
25 | self.song = self.playlist.songs[0];
26 | self.loading = false;
27 | }
28 |
29 | @action play() {
30 | if (controller.playlist.id === self.playlist.id) {
31 | controller.toggle();
32 | return;
33 | }
34 |
35 | controller.setup(self.playlist);
36 | controller.play();
37 | }
38 |
39 | // Ban a song
40 | @action async ban(id) {
41 | var response = await axios.get(`/fm_trash?id=${id}`);
42 |
43 | if (response.data.code === 200) {
44 | self.next();
45 | }
46 | }
47 |
48 | @action async next() {
49 | var index = self.playlist.songs.findIndex(e => e.id === controller.song.id);
50 |
51 | if (controller.playlist.id !== self.playlist.id) {
52 | self.play();
53 | return;
54 | }
55 |
56 | if (++index < self.playlist.songs.length) {
57 | let next = self.playlist.songs[index];
58 |
59 | controller.play(next.id);
60 | return;
61 | }
62 |
63 | // Refresh the playlist
64 | await self.shuffle();
65 | controller.setup(self.playlist);
66 | controller.play();
67 | }
68 | }
69 |
70 | const self = new FM();
71 | export default self;
72 |
--------------------------------------------------------------------------------
/src/js/stores/home.js:
--------------------------------------------------------------------------------
1 |
2 | import { observable, action } from 'mobx';
3 | import axios from 'axios';
4 |
5 | import me from './me';
6 | import preferences from './preferences';
7 | import controller from './controller';
8 |
9 | class Home {
10 | @observable loading = true;
11 | @observable list = [];
12 | @observable bannerList = [];
13 |
14 | @action async load() {
15 | var res;
16 |
17 | if (me.hasLogin()) {
18 | // 登陆时,获取到首页所有的歌曲数据
19 | res = await axios.get(`/api/home/${me.profile.userId}`);
20 | // 结果数组的第一个元素是“我喜欢的音乐”数组,第二个是“日推歌单”
21 | let favorite = res.data.list[0];
22 | let recommend = res.data.list[1];
23 |
24 | // Save the songs of red heart
25 | me.rocking(favorite);
26 |
27 | if (recommend.length) {
28 | // Play the recommend songs
29 | controller.setup(recommend);
30 | } else {
31 | controller.setup(favorite);
32 | }
33 | } else {
34 | // 未登陆时拿热门歌单
35 | res = await axios.get(`/api/home`);
36 | controller.setup(res.data.list[0]);
37 | }
38 |
39 | if (preferences.autoPlay) {
40 | controller.play();
41 | } else {
42 | controller.song = controller.playlist.songs[0];
43 | }
44 |
45 | self.list = res.data.list;
46 |
47 | return self.list;
48 | }
49 |
50 | @action async loadBanner() {
51 | var res;
52 |
53 | // 获取到首页banner轮播图信息,官方api参数不明,所以数据比较旧
54 | res = await axios.get(`/banner`);
55 |
56 | self.bannerList = res.data.banners;
57 |
58 | return self.bannerList;
59 | }
60 |
61 | @action async getList() {
62 | self.loading = true;
63 |
64 | await self.load();
65 | await self.loadBanner();
66 |
67 | // Just call once for init player
68 | self.getList = Function;
69 | self.loading = false;
70 | }
71 | }
72 |
73 | const self = new Home();
74 | export default self;
75 |
--------------------------------------------------------------------------------
/src/js/stores/index.js:
--------------------------------------------------------------------------------
1 |
2 | import me from './me';
3 | import aboutme from './aboutme';
4 | import home from './home';
5 | import user from './user';
6 | import controller from './controller';
7 | import player from './player';
8 | import artist from './artist';
9 | import top from './top';
10 | import playlist from './playlist';
11 | import fm from './fm';
12 | import playing from './playing';
13 | import search from './search';
14 | import comments from './comments';
15 | import lyrics from './lyrics';
16 | import preferences from './preferences';
17 | import dailyplayer from './dailyplayer';
18 |
19 | const stores = {
20 | me,
21 | aboutme,
22 | home,
23 | user,
24 | controller,
25 | player,
26 | artist,
27 | top,
28 | playlist,
29 | fm,
30 | playing,
31 | search,
32 | comments,
33 | lyrics,
34 | preferences,
35 | dailyplayer
36 | };
37 |
38 | export default stores;
39 |
--------------------------------------------------------------------------------
/src/js/stores/lyrics.js:
--------------------------------------------------------------------------------
1 |
2 | import { observable, action } from 'mobx';
3 | import axios from 'axios';
4 |
5 | import controller from './controller';
6 |
7 | class Lyrics {
8 | @observable loading = true;
9 | @observable show = false;
10 | @observable list = {};
11 |
12 | @action toggle(show = !self.show) {
13 | self.show = show;
14 | }
15 |
16 | @action async getLyrics() {
17 | self.loading = true;
18 |
19 | var response = await axios.get(`/api/lyrics/${controller.song.id}`);
20 | var data = response.data;
21 |
22 | self.list = data;
23 | self.loading = false;
24 | }
25 | }
26 |
27 | const self = new Lyrics();
28 | export default self;
29 |
--------------------------------------------------------------------------------
/src/js/stores/playing.js:
--------------------------------------------------------------------------------
1 |
2 | import { observable, action } from 'mobx';
3 | import han from 'han';
4 |
5 | import controller from './controller';
6 |
7 | class Playing {
8 | @observable show = false;
9 | @observable filtered = [];
10 |
11 | @action toggle(show = !self.show) {
12 | self.show = show;
13 | }
14 |
15 | @action doFilter(text) {
16 | var songs = [];
17 |
18 | // Convert text to chinese pinyin
19 | text = han.letter(text.trim());
20 |
21 | songs = controller.playlist.songs.filter(e => {
22 | return false
23 | // Fuzzy match the song name
24 | || han.letter(e.name).indexOf(text) > -1
25 | // Fuzzy match the album name
26 | || han.letter(e.album.name).indexOf(text) > -1
27 | // Mathc the artist name
28 | || e.artists.findIndex(e => han.letter(e.name).indexOf(text) > -1) !== -1
29 | ;
30 | });
31 |
32 | self.filtered = songs;
33 | }
34 |
35 | filter(text = '') {
36 | clearTimeout(self.timer);
37 | self.timer = setTimeout(() => self.doFilter(text), 50);
38 | }
39 | }
40 |
41 | const self = new Playing();
42 | export default self;
43 |
--------------------------------------------------------------------------------
/src/js/stores/top.js:
--------------------------------------------------------------------------------
1 |
2 | import { observable, action } from 'mobx';
3 | import axios from 'axios';
4 |
5 | class Top {
6 | @observable loading = true;
7 | @observable list = [];
8 |
9 | @action async getList() {
10 | self.loading = true;
11 |
12 | /**
13 | "0": 云音乐新歌榜,
14 | "1": 云音乐热歌榜,
15 | "2": 网易原创歌曲榜,
16 | "3": 云音乐飙升榜,
17 | "4": 云音乐电音榜,
18 | "5": UK排行榜周榜,
19 | "6": 美国Billboard周榜
20 | "7": KTV嗨榜,
21 | "8": iTunes榜,
22 | "9": Hit FM Top榜,
23 | "10": 日本Oricon周榜
24 | "11": 韩国Melon排行榜周榜,
25 | "12": 韩国Mnet排行榜周榜,
26 | "13": 韩国Melon原声周榜,
27 | "14": 中国TOP排行榜(港台榜),
28 | "15": 中国TOP排行榜(内地榜)
29 | "16": 香港电台中文歌曲龙虎榜,
30 | "17": 华语金曲榜,
31 | "18": 中国嘻哈榜,
32 | "19": 法国 NRJ EuroHot 30周榜,
33 | "20": 台湾Hito排行榜,
34 | "21": Beatport全球电子舞曲榜
35 | * */
36 | var response = await axios.get('/api/top/0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21');
37 |
38 | self.list = response.data.list;
39 | self.loading = false;
40 | }
41 | }
42 |
43 | const self = new Top();
44 | export default self;
45 |
--------------------------------------------------------------------------------
/src/js/stores/user.js:
--------------------------------------------------------------------------------
1 |
2 | import { observable, action } from 'mobx';
3 | import axios from 'axios';
4 |
5 | class User {
6 | @observable loading = true;
7 | @observable profile = {};
8 | @observable playlists = [];
9 |
10 | @action async getUser(userid) {
11 | self.loading = true;
12 |
13 | var response = await axios.get(`/api/user/${userid}`);
14 |
15 | self.profile = response.data.profile;
16 | self.playlists = response.data.playlists;
17 | self.loading = false;
18 | }
19 |
20 | @action async follow(followed) {
21 | var response = await axios.get(
22 | followed
23 | ? `/api/user/unfollow/${self.profile.id}`
24 | : `/api/user/follow/${self.profile.id}`
25 | );
26 | var data = response.data;
27 |
28 | if (data.success) {
29 | self.profile = Object.assign({}, self.profile, {
30 | followed: !followed,
31 | });
32 | }
33 | }
34 | }
35 |
36 | const self = new User();
37 | export default self;
38 |
--------------------------------------------------------------------------------
/src/js/ui/FadeImage/classes.js:
--------------------------------------------------------------------------------
1 |
2 | export default theme => ({
3 | fade: {
4 | opacity: 1,
5 | transition: '.2s',
6 | },
7 |
8 | fadein: {
9 | opacity: 0
10 | }
11 | });
12 |
--------------------------------------------------------------------------------
/src/js/ui/FadeImage/index.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component, PropTypes } from 'react';
3 | import injectSheet from 'react-jss';
4 | import clazz from 'classname';
5 |
6 | import classes from './classes';
7 |
8 | class FadeImage extends Component {
9 | static propTypes = {
10 | src: PropTypes.string,
11 | fallback: PropTypes.string,
12 | };
13 |
14 | static defaultProps = {
15 | fallback: 'https://source.unsplash.com/random',
16 | };
17 |
18 | componentWillReceiveProps(nextProps) {
19 | var ele = this.refs.image;
20 |
21 | if (ele
22 | && this.props.src !== nextProps.src) {
23 | ele.classList.add(nextProps.classes.fadein);
24 | }
25 | }
26 |
27 | handleError(e) {
28 | e.target.src = this.props.fallback;
29 | }
30 |
31 | handleLoad(e) {
32 | e.target.classList.remove(this.props.classes.fadein);
33 | }
34 |
35 | render() {
36 | var classes = this.props.classes;
37 |
38 | if (!this.props.src) return false;
39 |
40 | return (
41 |
this.handleLoad(e)}
46 | onError={e => this.handleError(e)} />
47 | );
48 | }
49 | }
50 |
51 | export default injectSheet(classes)(FadeImage);
52 |
--------------------------------------------------------------------------------
/src/js/ui/Loader/classes.js:
--------------------------------------------------------------------------------
1 |
2 | export default theme => {
3 | return {
4 | container: {
5 | position: 'fixed',
6 | top: 60,
7 | left: 0,
8 | display: 'flex',
9 | width: '100vw',
10 | height: 'calc(100vh - 124px)',
11 | justifyContent: 'center',
12 | alignItems: 'center',
13 | background: 'white',
14 | fontFamily: 'HelveticaNeue-UltraLight',
15 | fontSize: 24,
16 | opacity: 0,
17 | visibility: 'hidden',
18 | transition: '.2s',
19 |
20 | '& span': {
21 | marginTop: '8%',
22 | maxWidth: '60vw',
23 | textAlign: 'center',
24 | lineHeight: '32px',
25 | color: '#ea4c89',
26 | },
27 |
28 | '&:before': {
29 | content: 'url(assets/loading.gif)',
30 | position: 'absolute',
31 | width: '140px',
32 | height: '140px',
33 | top: '36%',
34 | left: '50%',
35 | display: 'block',
36 | transform: 'translateX(-25%)',
37 | },
38 | },
39 |
40 | show: {
41 | opacity: 1,
42 | visibility: 'visible',
43 | zIndex: 1000,
44 | },
45 | };
46 | };
47 |
--------------------------------------------------------------------------------
/src/js/ui/Loader/index.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component, PropTypes } from 'react';
3 | import injectSheet from 'react-jss';
4 | import clazz from 'classname';
5 |
6 | import classes from './classes';
7 |
8 | /**
9 | * FROM NGA:
10 | * http://bbs.ngacn.cc/read.php?tid=7803294&forder_by=postdatedesc&_ff=7
11 | * */
12 | const randomText = [
13 | '当你觉得孤独无助时,想一想还有十几亿的细胞只为了你一个人而活',
14 | '人要是矫情起来,听什么都像是听自己',
15 | '每个人的裂痕,最后都会变成故事的花纹 ',
16 | '你那么孤独,却说一个人真好',
17 | '我在最没有能力的年纪,碰见了最想照顾一生的人',
18 | '世界如此广阔,人类却走进了悲伤的墙角',
19 | '喜欢这种东西,捂住嘴巴,也会从眼睛里跑出来',
20 | '我听过一万首歌,看过一千部电影,读过一百本书,却从未俘获一个人的心',
21 | '最怕一生碌碌无为,还说平凡难能可贵 ',
22 | '年轻时我想变成任何人,除了我自己 ',
23 | '别人稍一注意你,你就敞开心扉,你觉得这是坦率,其实这是孤独……',
24 | '我已经过了餐桌上有只鸡就一定能吃到鸡腿的年纪了',
25 | '不在一起就不在一起吧,反正一辈子也没多长',
26 | '你别皱眉,我走就好',
27 | '那年上初中,夏天是好漫长的,西瓜是吃不完的,作业是最后两天才赶的',
28 | '小时候总是骗爸妈自己没钱了,现在骗他们自己还有钱',
29 | '你那么擅长安慰他人,一定度过了很多自己安慰自己的日子吧',
30 | '余生好长,你好难忘',
31 | '我从未拥有过你一秒钟,心里却失去过你千万次',
32 | '你是来和我告别的吗。那就隆重一点,等我眼里装满泪水',
33 | '机场比婚礼的殿堂见证了更多真诚的吻,医院的墙比教堂听到了更多的祈祷',
34 | '祝你们幸福是假的,祝你幸福是真的 ',
35 | '懒得重新认识一个人 再问名字 再问年龄 再聊天 再了解对方 再磨合 一想就烦',
36 | '不要太早为一个人倾尽全部,因为你太年轻了',
37 | ];
38 |
39 | class Loader extends Component {
40 | static propTypes = {
41 | show: PropTypes.bool,
42 | text: PropTypes.string,
43 | };
44 |
45 | static defaultProps = {
46 | show: false,
47 | };
48 |
49 | shouldComponentUpdate(nextProps) {
50 | if (nextProps.show === this.props.show) {
51 | return false;
52 | }
53 |
54 | return true;
55 | }
56 |
57 | render() {
58 | var classes = this.props.classes;
59 | var text = this.props.text || randomText[Math.floor(Math.random() * randomText.length)];
60 |
61 | return (
62 |
66 | {text}
67 |
68 | );
69 | }
70 | }
71 |
72 | export default injectSheet(classes)(Loader);
73 |
--------------------------------------------------------------------------------
/src/js/ui/Offline/classes.js:
--------------------------------------------------------------------------------
1 |
2 | import colors from 'utils/colors';
3 |
4 | export default theme => {
5 | return {
6 | container: {
7 | position: 'fixed',
8 | top: 0,
9 | left: 0,
10 | display: 'flex',
11 | width: '100vw',
12 | height: '100vh',
13 | justifyContent: 'center',
14 | flexDirection: 'column',
15 | alignItems: 'center',
16 | background: 'white',
17 | fontFamily: 'HelveticaNeue-UltraLight',
18 |
19 | '& h1': {
20 | fontSize: 24,
21 | fontWeight: '100',
22 | color: colors.pallet.dribbble
23 | },
24 |
25 | '& button': {
26 | marginTop: 20,
27 | padding: '8px 12px',
28 | border: 0,
29 | fontSize: 14,
30 | fontWeight: 'lighter',
31 | color: '#278cf7',
32 | background: 'white',
33 | letterSpacing: .5,
34 | textTransform: 'uppercase',
35 | borderBottom: 'thin solid #278cf7',
36 | outline: 0,
37 | },
38 |
39 | '&:before': {
40 | content: 'url(assets/loading.gif)',
41 | position: 'absolute',
42 | width: '140px',
43 | height: '140px',
44 | top: '30%',
45 | left: '50%',
46 | display: 'block',
47 | transform: 'translateX(-25%)',
48 | },
49 | },
50 | };
51 | };
52 |
--------------------------------------------------------------------------------
/src/js/ui/Offline/index.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component, PropTypes } from 'react';
3 | import injectSheet from 'react-jss';
4 |
5 | import classes from './classes';
6 |
7 | class Offline extends Component {
8 | static propTypes = {
9 | show: PropTypes.bool.isRequired,
10 | };
11 |
12 | render() {
13 | var { classes, show } = this.props;
14 |
15 | if (!show) {
16 | return false;
17 | }
18 |
19 | return (
20 |
21 |
┭┮﹏┭┮ 网络连接失败,点击下方按钮重新加载。。。
22 |
23 |
24 |
25 | );
26 | }
27 | }
28 |
29 | export default injectSheet(classes)(Offline);
30 |
--------------------------------------------------------------------------------
/src/js/ui/ProgressImage/classes.js:
--------------------------------------------------------------------------------
1 |
2 | export default theme => ({
3 | container: {
4 | position: 'relative',
5 | padding: 0,
6 | margin: 0,
7 | background: '#ddd',
8 | overflow: 'hidden',
9 |
10 | '& img': {
11 | height: 'auto',
12 | pointerEvents: 'none',
13 | },
14 | },
15 |
16 | main: {
17 | display: 'block',
18 | opacity: 0,
19 | transition: 'opacity 0.5s ease-out',
20 | },
21 |
22 | thumb: {
23 | '& img': {
24 | position: 'absolute',
25 | top: 0,
26 | left: 0,
27 | transition: 'opacity 0.3s ease-out',
28 | filter: 'blur(30px)',
29 | },
30 |
31 | '&$loaded img': {
32 | opacity: 1,
33 | },
34 | },
35 |
36 | loaded: {
37 | '& $main': {
38 | opacity: 1,
39 | },
40 |
41 | '& $thumb img': {
42 | opacity: 0,
43 | }
44 | },
45 | });
46 |
--------------------------------------------------------------------------------
/src/js/ui/ProgressImage/index.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component, PropTypes } from 'react';
3 | import injectSheet from 'react-jss';
4 |
5 | import classes from './classes';
6 |
7 | class ProgressImage extends Component {
8 | static propTypes = {
9 | src: PropTypes.string,
10 | thumb: PropTypes.string,
11 | height: PropTypes.number,
12 | width: PropTypes.number,
13 | fallback: PropTypes.string,
14 | };
15 |
16 | static defaultProps = {
17 | fallback: 'https://source.unsplash.com/random',
18 | };
19 |
20 | componentWillReceiveProps(nextProps) {
21 | if (true
22 | && this.refs.container
23 | && nextProps.src !== this.props.src) {
24 | // Immediate render the new image
25 | this.refs.container.classList.remove(this.props.classes.loaded);
26 | }
27 | }
28 |
29 | handleError(e) {
30 | e.target.src = this.props.fallback;
31 | }
32 |
33 | handleLoad(e) {
34 | var ele = this.refs.container;
35 | this.refs.thumb.style.paddingBottom = '0%';
36 |
37 | // Fix bug, sometiems this dom has been destroyed
38 | if (ele) {
39 | setTimeout(() => {
40 | ele.classList.add(this.props.classes.loaded);
41 | }, 50);
42 | }
43 | }
44 |
45 | render() {
46 | var { classes, src, thumb, height, width } = this.props;
47 |
48 | if (!src) return false;
49 |
50 | if (!thumb) {
51 | // Get the thumb image src
52 | thumb = src.replace(/\?.*$/, '') + '?param=20y20';
53 | }
54 |
55 | return (
56 |
63 |
this.handleError(e)}
66 | onLoad={e => this.handleLoad(e)}
67 | ref="image"
68 | src={this.props.src}
69 | style={{
70 | height,
71 | width,
72 | }} />
73 |
80 |
![]()
91 |
92 |
93 | );
94 | }
95 | }
96 |
97 | export default injectSheet(classes)(ProgressImage);
98 |
--------------------------------------------------------------------------------
/src/js/ui/Switch/classes.js:
--------------------------------------------------------------------------------
1 |
2 | export default theme => ({
3 | container: {
4 | display: 'inline-block',
5 | margin: 0,
6 | padding: 0,
7 | cursor: 'pointer',
8 |
9 | '& input': {
10 | display: 'none',
11 |
12 | '&:checked + $fake:before': {
13 | background: 'rgba(157, 166, 216, 1)',
14 | },
15 |
16 | '&:checked + $fake:after': {
17 | left: 'auto',
18 | right: 0,
19 | background: '#3f51b5',
20 | }
21 | },
22 |
23 | '& input:disabled + $fake:before': {
24 | background: 'rgba(0, 0, 0, .12)',
25 | },
26 |
27 | '& input:disabled + $fake:after': {
28 | left: 'auto',
29 | right: 0,
30 | background: '#bdbdbd',
31 | }
32 | },
33 |
34 | fake: {
35 | position: 'relative',
36 | display: 'inline-block',
37 | width: 35,
38 | height: 20,
39 |
40 | '&:before, &:after': {
41 | content: '""',
42 | },
43 |
44 | '&:before': {
45 | display: 'block',
46 | width: 35,
47 | height: 14,
48 | marginTop: 3,
49 | background: 'rgba(0, 0, 0, .26)',
50 | borderRadius: 14,
51 | transition: '.5s ease-in-out',
52 | },
53 |
54 | '&:after': {
55 | position: 'absolute',
56 | left: 0,
57 | top: 0,
58 | width: 20,
59 | height: 20,
60 | borderRadius: 20,
61 | background: '#fff',
62 | boxSizing: 'border-box',
63 | boxShadow: '0 3px 4px 0 rgba(0, 0, 0, .5)',
64 | transition: '.2s',
65 | }
66 | },
67 | });
68 |
--------------------------------------------------------------------------------
/src/js/ui/Switch/index.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { Component } from 'react';
3 | import injectSheet from 'react-jss';
4 |
5 | import classes from './classes';
6 |
7 | class Switch extends Component {
8 | render() {
9 | var { classes, id, checked, onChange } = this.props;
10 |
11 | return (
12 |
13 |
18 |
19 |
20 | );
21 | }
22 | }
23 |
24 | export default injectSheet(classes)(Switch);
25 |
--------------------------------------------------------------------------------
/src/js/utils/colors.js:
--------------------------------------------------------------------------------
1 |
2 | const pallet = {
3 | primary: '#6496f0',
4 | sunflower: '#ffce54',
5 | grape: '#e0245e',
6 | coral: '#ff6470',
7 | mint: '#48cfad',
8 | dribbble: '#ea4c89',
9 | twitter: '#55acee',
10 | google: '#039be5',
11 | };
12 |
13 | const gradients = [
14 | 'linear-gradient(to right, #0099f7, #f11712)',
15 | 'linear-gradient(to bottom, #834d9b, #d04ed6)',
16 | 'linear-gradient(to left, #4da0b0, #d39d38)',
17 | 'linear-gradient(to left, #5614b0, #dbd65c)',
18 | 'linear-gradient(to right, #114357, #f29492)',
19 | 'linear-gradient(to right, #fd746c, #ff9068)',
20 | 'linear-gradient(to left, #6a3093, #a044ff)',
21 | 'linear-gradient(to left, #b24592, #f15f79)',
22 | 'linear-gradient(to top, #403a3e, #be5869)',
23 | 'linear-gradient(to right, #c2e59c, #64b3f4)',
24 | 'linear-gradient(to right, #00c9ff, #92fe9d)',
25 | 'linear-gradient(to right, #e53935, #e35d5b)',
26 | 'linear-gradient(to right, #fc00ff, #00dbde)',
27 | 'linear-gradient(to right, #7b4397, #dc2430)',
28 | ];
29 |
30 | export default {
31 | pallet,
32 |
33 | randomGradient() {
34 | return gradients[Math.floor(Math.random() * gradients.length)];
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/src/js/utils/lastfm.js:
--------------------------------------------------------------------------------
1 |
2 | import API from 'simple-lastfm';
3 |
4 | const API_KEY = 'c1f9a819c03a17083eba0fe9ee41119e';
5 | const SECRET = '3742198243c490bf333d0aa615e5d117';
6 |
7 | var lastfm;
8 |
9 | async function getSession() {
10 | var success = await new Promise((resolve, reject) => {
11 | if (!lastfm) {
12 | resolve(false);
13 | return;
14 | }
15 |
16 | lastfm.getSessionKey(result => {
17 | resolve(result.success);
18 | });
19 | });
20 |
21 | if (!success) {
22 | return;
23 | }
24 |
25 | return lastfm;
26 | }
27 |
28 | async function initialize(username, password) {
29 | if (!username || !password) {
30 | return;
31 | }
32 |
33 | lastfm = new API({
34 | api_key: API_KEY,
35 | api_secret: SECRET,
36 | username,
37 | password,
38 | });
39 |
40 | return getSession();
41 | }
42 |
43 | async function scrobble(song) {
44 | var session = await getSession();
45 |
46 | if (!session) {
47 | return;
48 | }
49 |
50 | return new Promise((resolve, reject) => {
51 | session.scrobbleTrack({
52 | artist: song.artists.map(e => e.name).join(','),
53 | track: song.name,
54 | callback: (result) => resolve(result)
55 | });
56 | });
57 | }
58 |
59 | async function playing(song) {
60 | var session = await getSession();
61 |
62 | if (!session) {
63 | return;
64 | }
65 |
66 | return new Promise((resolve, reject) => {
67 | session.scrobbleNowPlayingTrack({
68 | artist: song.artists.map(e => e.name).join(','),
69 | track: song.name,
70 | callback: (result) => resolve(result)
71 | });
72 | });
73 | }
74 |
75 | async function love(song) {
76 | var session = await getSession();
77 |
78 | if (!session) {
79 | return;
80 | }
81 |
82 | return new Promise((resolve, reject) => {
83 | session.loveTrack({
84 | artist: song.artists.map(e => e.name).join(','),
85 | track: song.name,
86 | callback: (result) => resolve(result)
87 | });
88 | });
89 | }
90 |
91 | async function unlove(song) {
92 | var session = await getSession();
93 |
94 | if (!session) {
95 | return;
96 | }
97 |
98 | return new Promise((resolve, reject) => {
99 | session.unloveTrack({
100 | artist: song.artists.map(e => e.name).join(','),
101 | track: song.name,
102 | callback: (result) => resolve(result)
103 | });
104 | });
105 | }
106 |
107 | export default {
108 | initialize,
109 | scrobble,
110 | playing,
111 | love,
112 | unlove,
113 | };
114 |
--------------------------------------------------------------------------------
/src/js/utils/storage.js:
--------------------------------------------------------------------------------
1 |
2 | import storage from 'electron-json-storage';
3 |
4 | // 类似localStorage的API来保存和读取应用程序的用户设置
5 | export default {
6 | get: (key) => {
7 | return new Promise((resolve, reject) => {
8 | storage.get(key, (err, data) => {
9 | if (err) {
10 | reject(err);
11 | } else {
12 | resolve(data);
13 | }
14 | });
15 | });
16 | },
17 |
18 | set: (key, data) => {
19 | return new Promise((resolve, reject) => {
20 | storage.set(key, data, err => {
21 | if (err) {
22 | reject(err);
23 | } else {
24 | resolve(data);
25 | }
26 | });
27 | });
28 | },
29 |
30 | remove: (key) => {
31 | return new Promise((resolve, reject) => {
32 | storage.remove(key, err => {
33 | if (err) {
34 | reject(err);
35 | } else {
36 | resolve();
37 | }
38 | });
39 | });
40 | }
41 | };
42 |
--------------------------------------------------------------------------------