├── .gitignore ├── README.md ├── index.js ├── package.json ├── server.js ├── src ├── index.js ├── provider │ ├── netease.js │ ├── qq.js │ └── xiami.js └── vendor │ └── crypto.js ├── test ├── netease.html ├── netease.js ├── qq-time.js ├── server-test.json ├── test.js ├── xiami.html └── xiami.js ├── webpack.config.js └── zh-README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea 3 | .vscode 4 | *.suo 5 | *.ntvs* 6 | *.njsproj 7 | *.sln 8 | dist -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # music-api-next 2 | 3 | > Music API for search results, songs, comments from QQ, Xiami and Netease. 4 | 5 | For more information on how to get started and how to use `music-api-next`, please see [author's blog](https://godbmw.com/) and comment there. For source files and issues, please visit [the github repo](https://github.com/dongyuanxin/music-api-next). 6 | 7 | [**DOCS**](https://godbmw.com/passage/62) 8 | 9 | [**中文文档**](https://godbmw.com/passage/63) 10 | 11 | ## Install 12 | 13 | ``` 14 | npm install music-api-next --save 15 | ``` 16 | 17 | If you are in China, please use: 18 | 19 | ``` 20 | cnpm install music-api-next --save 21 | ``` 22 | 23 | ## Quick Start 24 | 25 | ```javascript 26 | const musicAPI = require("music-api-next"); 27 | 28 | // Search API: search keywords on qq, xiami or netease 29 | musicAPI 30 | .searchSong({ 31 | key: "周杰伦", 32 | page: 1, 33 | limit: 10, 34 | vendor: "qq" 35 | }) 36 | .then(songs => console.log(songs)) 37 | .catch(error => console.log(error.message)); 38 | 39 | // Song API: get music meta including URL 40 | musicAPI 41 | .getSong({ 42 | id: "003OUlho2HcRHC", 43 | vendor: "qq" 44 | }) 45 | .then(meta => console.log(meta)) 46 | .catch(error => console.log(error.message)); 47 | 48 | // Comment API: get comments for the specified song 49 | musicAPI 50 | .getComment({ 51 | id: "003OUlho2HcRHC", 52 | page: 1, 53 | limit: 20, 54 | vendor: "qq" 55 | }) 56 | .then(comments => console.log(comments)) 57 | .catch(error => console.log(error.message)); 58 | ``` 59 | 60 | ## Run with a server 61 | 62 | ```shell 63 | git clone git@github.com:dongyuanxin/music-api-next.git 64 | cd music-api-next 65 | npm install 66 | // run server on port: 5050 67 | node server.js 68 | ``` 69 | 70 | You can see the results of music APIs by accessing the url. 71 | 72 | For example: 73 | 74 | - Search API: `http://localhost:5050/search/song?key=周杰伦&page=1&limit=10&vendor=qq` 75 | - Song API: `http://localhost:5050/get/song?id=003OUlho2HcRHC&vendor=qq` 76 | - Comment API: `http://localhost:5050/get/comment?id=003OUlho2HcRHC&page=1&limit=10&vendor=qq` 77 | 78 | ## Run with webpack 79 | 80 | First, package with webpack. 81 | 82 | ```shell 83 | git clone git@github.com:dongyuanxin/music-api-next.git 84 | cd music-api-next 85 | npm install 86 | // use webpack to package program 87 | // pacakged file named 'music-api-next.js' is placed in ./dist/ 88 | webpack 89 | ``` 90 | 91 | Then, you can move `music-api-next.js` very conveniently and use it in the following ways: 92 | 93 | ```javascript 94 | const musicAPI = require("./music-api-next"); 95 | 96 | // ... 97 | ``` 98 | 99 | ## API 100 | 101 | - `musicAPI.searchSong(query)`: 102 | 103 | ``` 104 | query: { 105 | key: String, 106 | page: Number, 107 | limit: Number, 108 | vendor: one of ['netease', 'xiami', 'qq'] 109 | } 110 | ``` 111 | 112 | - `musicAPI.getSong(query)`: 113 | 114 | ``` 115 | query: { 116 | id: String or Number, 117 | vendor: one of ['netease', 'xiami', 'qq'] 118 | } 119 | ``` 120 | 121 | - `musicAPI.getComment(query)`: 122 | 123 | ``` 124 | query: { 125 | id: String or Number, 126 | page: Number, 127 | limit: Number, 128 | vendor: one of ['netease', 'xiami', 'qq'] 129 | } 130 | ``` 131 | 132 | ## Warning 133 | 134 | 1. **It cannot be used for commercial purposes.** 135 | 2. **It runs only on NodeJS instead of browser.** 136 | 3. **Please use it politely and don't put too much pressure on music platforms.** 137 | 138 | ## Thanks 139 | 140 | The code for this project refers to the following open source projects and has been fixed, improved and added on NodeJS. 141 | 142 | 1. [listen1_chrome_extension](https://github.com/listen1/listen1_chrome_extension): Has received a lawyer's letter from Tencent. May stop maintenance at the end of the year 2018. 143 | 2. [musicAPI](https://github.com/LIU9293/musicAPI): It has stopped maintenance one year ago. So its APIs are invalid. 144 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./src/index"); 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "cheerio": "^1.0.0-rc.2", 4 | "crypto-js": "^3.1.9-1", 5 | "moment": "^2.22.2", 6 | "request": "^2.87.0" 7 | }, 8 | "devDependencies": { 9 | "koa": "^2.5.2", 10 | "koa-bodyparser": "^4.2.1", 11 | "koa-router": "^7.4.0", 12 | "koa-xml-body": "^2.0.0", 13 | "webpack": "^4.16.3" 14 | }, 15 | "scripts": { 16 | "start": "node server.js" 17 | }, 18 | "name": "music-api-next", 19 | "description": "Music API for search results, songs, comments from QQ, Xiami and Netease. ", 20 | "version": "1.0.0", 21 | "main": "index.js", 22 | "directories": { 23 | "test": "test" 24 | }, 25 | "keywords": [ 26 | "Music", 27 | "Xiami", 28 | "Netease", 29 | "QQ", 30 | "API", 31 | "Node" 32 | ], 33 | "repository": { 34 | "type": "git", 35 | "url": "git+https://github.com/dongyuanxin/music-api-next.git" 36 | }, 37 | "author": "godbmw", 38 | "license": "MIT", 39 | "bugs": { 40 | "url": "https://github.com/dongyuanxin/music-api-next/issues" 41 | }, 42 | "homepage": "https://github.com/dongyuanxin/music-api-next#readme" 43 | } 44 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const PORT = 5050; 2 | 3 | const koa = require("koa"); 4 | const router = require("koa-router")(); 5 | const bodyParser = require("koa-bodyparser"); 6 | const xmlParser = require("koa-xml-body"); 7 | 8 | const path = require("path"); 9 | const http = require("http"); 10 | 11 | const musicAPI = require(path.resolve("src", "index.js")); 12 | 13 | let app = new koa(); 14 | app.use(xmlParser()); 15 | app.use(bodyParser()); 16 | 17 | router.get("/search/song", async (ctx, next) => { 18 | let response = await musicAPI.searchSong(ctx.request.query); 19 | ctx.response.body = response; 20 | return; 21 | }); 22 | router.post("/search/song", async (ctx, next) => { 23 | let response = await musicAPI.searchSong(ctx.request.body); 24 | ctx.response.body = response; 25 | return; 26 | }); 27 | 28 | router.get("/get/song", async (ctx, next) => { 29 | let response = await musicAPI.getSong(ctx.request.query); 30 | ctx.response.body = response; 31 | return; 32 | }); 33 | router.post("/get/song", async (ctx, next) => { 34 | let response = await musicAPI.getSong(ctx.request.body); 35 | ctx.response.body = response; 36 | return; 37 | }); 38 | 39 | router.get("/get/comment", async (ctx, next) => { 40 | let response = await musicAPI.getComment(ctx.request.query); 41 | ctx.response.body = response; 42 | return; 43 | }); 44 | router.post("/get/comment", async (ctx, next) => { 45 | let response = await musicAPI.getComment(ctx.request.body); 46 | ctx.response.body = response; 47 | return; 48 | }); 49 | 50 | app.use(router.routes()); 51 | 52 | app.use(async (ctx, next) => { 53 | ctx.status = 404; 54 | }); 55 | 56 | http.createServer(app.callback()).listen(PORT); 57 | 58 | console.log("music-api started at port " + PORT); 59 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const Netease = require("./provider/netease"); 2 | const QQ = require("./provider/qq"); 3 | const Xiami = require("./provider/xiami"); 4 | 5 | const api = { 6 | netease: new Netease(), 7 | qq: new QQ(), 8 | xiami: new Xiami() 9 | }; 10 | 11 | const isEmptyStr = str => str === "" || str === undefined || str === null; 12 | const isUnvalidVendor = vendor => 13 | isEmptyStr(vendor) || Object.keys(api).indexOf(vendor) < 0; 14 | 15 | const searchSong = async options => { 16 | let { key, page, limit, vendor } = options; 17 | if (isEmptyStr(key) || isUnvalidVendor(vendor)) { 18 | return { 19 | success: false, 20 | msg: "Missing parameter" 21 | }; 22 | } 23 | page = page === undefined ? 1 : page; 24 | limit = limit === undefined ? 20 : limit; 25 | return await api[vendor].searchSong(key, page, limit); 26 | }; 27 | 28 | const getSong = async options => { 29 | let { id, vendor } = options; 30 | if (isUnvalidVendor(vendor) || id === undefined) { 31 | return { success: false, msg: "Missing parameter" }; 32 | } 33 | return await api[vendor].getSong(id); 34 | }; 35 | 36 | const getComment = async options => { 37 | let { vendor, id, page, limit } = options; 38 | if (isUnvalidVendor(vendor) || id === undefined) { 39 | return { success: false, msg: "Missing parameter" }; 40 | } 41 | page = page === undefined ? 1 : page; 42 | limit = limit === undefined ? 20 : limit; 43 | return await api[vendor].getComment(id, page, limit); 44 | }; 45 | 46 | module.exports = { 47 | searchSong, 48 | getSong, 49 | getComment 50 | }; 51 | -------------------------------------------------------------------------------- /src/provider/netease.js: -------------------------------------------------------------------------------- 1 | const request = require("request"); 2 | const moment = require("moment"); 3 | 4 | const querystring = require("querystring"); 5 | const { asrsea } = require("./../vendor/crypto"); 6 | 7 | class Music { 8 | constructor() { 9 | this.e = "010001"; 10 | this.f = 11 | "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"; 12 | this.g = "0CoJUm6Qyw8W8jud"; 13 | } 14 | searchSong(key, page, limit) { 15 | let url = "http://music.163.com/weapi/cloudsearch/get/web?csrf_token="; 16 | let form = { 17 | s: key, 18 | type: 1, 19 | limit, 20 | offset: limit * (page - 1) 21 | }; 22 | let { encText, encSecKey } = asrsea( 23 | JSON.stringify(form), 24 | this.e, 25 | this.f, 26 | this.g 27 | ); 28 | let options = { 29 | url, 30 | method: "POST", 31 | body: querystring.stringify({ 32 | params: encText, 33 | encSecKey: encSecKey 34 | }), 35 | headers: { "Content-Type": "application/x-www-form-urlencoded" } 36 | }; 37 | let promise = new Promise(resolve => { 38 | request(options, (err, res, body) => { 39 | if (err) { 40 | return resolve({ 41 | success: false, 42 | msg: err.message 43 | }); 44 | } 45 | try { 46 | let data = JSON.parse(body); 47 | return resolve({ 48 | success: true, 49 | results: data.result.songs.map(item => { 50 | return { 51 | id: item.id, 52 | name: item.name, 53 | artist: item.ar[0].name, 54 | album: item.al.name, 55 | cover: item.al.picUrl, 56 | needPay: item.privilege.st < 0 57 | }; 58 | }) 59 | }); 60 | } catch (error) { 61 | return resolve({ success: false, msg: error.message }); 62 | } 63 | }); 64 | }); 65 | return promise; 66 | } 67 | getSong(id) { 68 | return new Promise(resolve => { 69 | resolve({ 70 | success: true, 71 | results: { 72 | url: "http://music.163.com/song/media/outer/url?id=" + id + ".mp3" 73 | } 74 | }); 75 | }); 76 | } 77 | filterComment(comment) { 78 | let rule = /(\[.*?\])|\n|\\n/gm; 79 | return comment.replace(rule, ""); 80 | } 81 | getComment(id, page, limit) { 82 | let url = "https://music.163.com/weapi/v1/resource/comments/R_SO_4_" + id; 83 | let form = { 84 | rid: "R_SO_4_" + id, 85 | offset: limit * (page - 1), 86 | total: true, 87 | limit, 88 | csrf_token: "" 89 | }; 90 | let { encText, encSecKey } = asrsea( 91 | JSON.stringify(form), 92 | this.e, 93 | this.f, 94 | this.g 95 | ); 96 | let options = { 97 | url, 98 | method: "POST", 99 | body: querystring.stringify({ params: encText, encSecKey: encSecKey }), 100 | headers: { "Content-Type": "application/x-www-form-urlencoded" } 101 | }; 102 | let promise = new Promise(resolve => { 103 | request(options, (err, res, body) => { 104 | if (err) { 105 | return resolve({ success: false, msg: err.message }); 106 | } 107 | try { 108 | let data = JSON.parse(body); 109 | resolve({ 110 | success: true, 111 | results: data.comments.map(item => { 112 | return { 113 | time: moment(item.time).format("YYYY-MM-DD H:mm:ss"), 114 | content: this.filterComment(item.content), 115 | user: { 116 | headImgUrl: item.user.avatarUrl, 117 | nickname: item.user.nickname 118 | } 119 | }; 120 | }) 121 | }); 122 | } catch (error) { 123 | resolve({ success: false, msg: error.message }); 124 | } 125 | }); 126 | }); 127 | return promise; 128 | } 129 | } 130 | 131 | module.exports = Music; 132 | -------------------------------------------------------------------------------- /src/provider/qq.js: -------------------------------------------------------------------------------- 1 | const request = require("request"); 2 | const moment = require("moment"); 3 | 4 | const querystring = require("querystring"); 5 | 6 | class Music { 7 | constructor() {} 8 | searchSong(key, page, limit) { 9 | let jsonpCallback = "jsonp4"; 10 | let url = 11 | "http://c.y.qq.com/soso/fcgi-bin/search_for_qq_cp?" + 12 | querystring.stringify({ 13 | jsonpCallback, 14 | loginUin: 0, 15 | hostUin: 0, 16 | format: "jsonp", 17 | inCharset: "utf-8", 18 | outCharset: "utf-8", 19 | notice: 0, 20 | platform: "qq", 21 | needNewCode: 0, 22 | p: page, 23 | n: limit, 24 | w: key 25 | }); 26 | let options = { 27 | url, 28 | method: "GET", 29 | headers: { 30 | referer: "https://y.qq.com/portal/search.html", 31 | "user-agent": 32 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36" 33 | } 34 | }; 35 | let promise = new Promise(resolve => { 36 | request(options, (err, res, body) => { 37 | if (err) 38 | return resolve({ 39 | success: false 40 | }); 41 | try { 42 | let data = body.substr(jsonpCallback.length + 1); 43 | data = data.substr(0, data.length - 1); 44 | data = JSON.parse(data); 45 | return resolve({ 46 | success: true, 47 | results: data.data.song.list.map(item => { 48 | return { 49 | id: item.songmid, 50 | name: item.songname, 51 | artist: item.singer[0].name, 52 | album: item.albumname, 53 | cover: `https://y.gtimg.cn/music/photo_new/T002R300x300M000${ 54 | item.albummid 55 | }.jpg`, 56 | needPay: item.pay.payplay > 0 57 | }; 58 | }) 59 | }); 60 | } catch (error) { 61 | return resolve({ 62 | success: false, 63 | msg: error.message 64 | }); 65 | } 66 | }); 67 | }); 68 | return promise; 69 | } 70 | getSong(id) { 71 | let jsonpCallback = "MusicJsonCallback7156632135681187"; 72 | let url = 73 | "https://c.y.qq.com/base/fcgi-bin/fcg_music_express_mobile3.fcg?" + 74 | querystring.stringify({ 75 | g_tk: 1959393642, 76 | jsonpCallback, 77 | loginUin: 2181111110, 78 | hostUin: 0, 79 | format: "json", 80 | inCharset: "utf8", 81 | outCharset: "utf-8", 82 | notice: 0, 83 | platform: "yqq", 84 | needNewCode: 0, 85 | cid: 205361747, 86 | callback: jsonpCallback, 87 | uin: 2181111110, 88 | songmid: id, 89 | filename: `C400${id}.m4a`, 90 | guid: 9870159400 91 | }); 92 | let options = { 93 | url, 94 | method: "GET", 95 | headers: { 96 | referer: "https://y.qq.com/portal/player.html", 97 | "user-agent": 98 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36" 99 | } 100 | }; 101 | let promise = new Promise(resolve => { 102 | request(options, (err, res, body) => { 103 | if (err) { 104 | return resolve({ success: false, msg: err.message }); 105 | } 106 | 107 | try { 108 | let data = body.substr(jsonpCallback.length + 1); 109 | data = data.substr(0, data.length - 1); 110 | data = JSON.parse(data); 111 | data = data.data.items[0]; 112 | return resolve({ 113 | success: true, 114 | results: { 115 | url: 116 | `http://dl.stream.qqmusic.qq.com/C400${id}.m4a?` + 117 | querystring.stringify({ 118 | vkey: data.vkey, 119 | guid: 9870159400, // 和上方一定要一样 120 | uin: 2181111110, 121 | fromtag: 66 122 | }) 123 | } 124 | }); 125 | } catch (error) { 126 | return resolve({ success: false, msg: error.message }); 127 | } 128 | }); 129 | }); 130 | return promise; 131 | } 132 | __getTopId(id) { 133 | let jsonpCallback = "getOneSongInfoCallback"; 134 | let url = 135 | "https://c.y.qq.com/v8/fcg-bin/fcg_play_single_song.fcg?" + 136 | querystring.stringify({ 137 | songmid: id, 138 | tpl: "yqq_song_detail", 139 | loginUin: 0, 140 | hostUin: 0, 141 | format: "jsonp", 142 | callback: jsonpCallback, 143 | jsonpCallback, 144 | inCharset: "utf8", 145 | outCharset: "utf-8", 146 | notice: 0, 147 | platform: "yqq", 148 | needNewCode: 0 149 | }); 150 | let options = { 151 | url, 152 | method: "GET", 153 | headers: { 154 | referer: "https://y.qq.com/n/yqq/song/" + id + ".html", 155 | "user-agent": 156 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36" 157 | } 158 | }; 159 | let promise = new Promise(resolve => { 160 | request(options, (err, res, body) => { 161 | if (err) { 162 | return resolve({ success: false, msg: err.message }); 163 | } 164 | try { 165 | let data = body.substr(jsonpCallback.length + 1); 166 | data = data.substr(0, data.length - 1); 167 | data = JSON.parse(data); 168 | let topId = data.data[0].id; 169 | if (topId === null || topId === undefined) { 170 | return resolve({ success: false, msg: "Not found" }); 171 | } else { 172 | return resolve({ success: true, results: topId }); 173 | } 174 | } catch (error) { 175 | return resolve({ success: false, msg: error.message }); 176 | } 177 | }); 178 | }); 179 | return promise; 180 | } 181 | filterComment(comment) { 182 | let rule = /(\[em\].*?\[\/em\])|\n|\\n/gm; 183 | return comment.replace(rule, ""); 184 | } 185 | async getComment(id, page, limit) { 186 | let results = await this.__getTopId(id); 187 | let promise = new Promise(resolve => { 188 | if (results.success === false) { 189 | return resolve(results); 190 | } 191 | let topId = results.results; 192 | let jsonpCallback = "jsoncallback21880487934016424"; 193 | let url = 194 | "https://c.y.qq.com/base/fcgi-bin/fcg_global_comment_h5.fcg?" + 195 | querystring.stringify({ 196 | g_tk: 5381, 197 | jsonpCallback, 198 | loginUin: 0, 199 | hostUin: 0, 200 | format: "jsonp", 201 | inCharset: "utf8", 202 | outCharset: "GB2312", 203 | notice: 0, 204 | platform: "yqq", 205 | needNewCode: 0, 206 | cid: 205360772, 207 | reqtype: 2, 208 | biztype: 1, 209 | topid: topId, 210 | cmd: 8, 211 | needmusiccrit: 0, 212 | pagenum: page - 1, // 注意这里page从0开始计算 213 | pagesize: limit, 214 | lasthotcommentid: "", 215 | callback: jsonpCallback, 216 | domain: "qq.com", 217 | ct: 24, 218 | cv: 101010 219 | }); 220 | let options = { 221 | url, 222 | method: "GET", 223 | headers: { 224 | referer: "https://y.qq.com/n/yqq/song/" + id + ".html", 225 | "user-agent": 226 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36" 227 | } 228 | }; 229 | request(options, (err, res, body) => { 230 | if (err) { 231 | return resolve({ success: false, msg: err.message }); 232 | } 233 | try { 234 | let data = body.substr(jsonpCallback.length + 1); 235 | data = data.substr(0, data.length - 3); // 最后有2个换行符和1个")" 236 | data = JSON.parse(data); 237 | return resolve({ 238 | success: true, 239 | results: data.comment.commentlist.map(item => { 240 | return { 241 | time: moment(1e3 * item.time).format("YYYY-MM-DD H:mm:ss"), 242 | content: this.filterComment(item.rootcommentcontent), 243 | user: { headImgUrl: item.avatarurl, nickname: item.nick } 244 | }; 245 | }) 246 | }); 247 | } catch (error) { 248 | return resolve({ success: false, msg: error.message }); 249 | } 250 | }); 251 | }); 252 | return promise; 253 | } 254 | } 255 | 256 | module.exports = Music; 257 | -------------------------------------------------------------------------------- /src/provider/xiami.js: -------------------------------------------------------------------------------- 1 | const request = require("request"); 2 | const cheerio = require("cheerio"); 3 | 4 | const querystring = require("querystring"); 5 | 6 | class Music { 7 | constructor() {} 8 | _caesar(location) { 9 | var num = location[0]; 10 | var avg_len = Math.floor(location.slice(1).length / num); 11 | var remainder = location.slice(1).length % num; 12 | 13 | var result = []; 14 | for (var i = 0; i < remainder; i++) { 15 | var line = location.slice( 16 | i * (avg_len + 1) + 1, 17 | (i + 1) * (avg_len + 1) + 1 18 | ); 19 | result.push(line); 20 | } 21 | 22 | for (var i = 0; i < num - remainder; i++) { 23 | var line = location 24 | .slice((avg_len + 1) * remainder) 25 | .slice(i * avg_len + 1, (i + 1) * avg_len + 1); 26 | result.push(line); 27 | } 28 | 29 | var s = []; 30 | for (var i = 0; i < avg_len; i++) { 31 | for (var j = 0; j < num; j++) { 32 | s.push(result[j][i]); 33 | } 34 | } 35 | 36 | for (var i = 0; i < remainder; i++) { 37 | s.push(result[i].slice(-1)); 38 | } 39 | 40 | return unescape(s.join("")).replace(/\^/g, "0"); 41 | } 42 | _handleProtocolRelativeUrl(url) { 43 | let regex = /^.*?\/\//; 44 | let result = url.replace(regex, "http://"); 45 | return result; 46 | } 47 | _xmRetinaUrl(s) { 48 | if (s.slice(-6, -4) == "_1") { 49 | return s.slice(0, -6) + s.slice(-4); 50 | } 51 | return s; 52 | } 53 | searchSong(key, page, limit) { 54 | let url = 55 | "http://api.xiami.com/web?" + 56 | querystring.stringify({ 57 | v: "2.0", 58 | key, 59 | limit, 60 | page, 61 | r: "search/songs", 62 | app_key: 1 63 | }); 64 | let options = { 65 | url, 66 | method: "POST", 67 | headers: { 68 | referer: "http://h.xiami.com/", // must options 69 | user_agent: 70 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36" 71 | } 72 | }; 73 | let promise = new Promise(resolve => { 74 | request(options, (err, res, body) => { 75 | if (err) 76 | return resolve({ 77 | success: false 78 | }); 79 | try { 80 | let data = JSON.parse(body); 81 | return resolve({ 82 | success: true, 83 | results: data.data.songs.map(item => { 84 | return { 85 | id: item.song_id, 86 | name: item.song_name, 87 | artist: item.artist_name, 88 | album: item.album_name, 89 | cover: item.album_pic, 90 | needPay: item.need_pay_flag === 1, 91 | plus: { file: item.listen_file } 92 | }; 93 | }) 94 | }); 95 | } catch (error) { 96 | return resolve({ 97 | success: false, 98 | msg: error.message 99 | }); 100 | } 101 | }); 102 | }); 103 | return promise; 104 | } 105 | getSong(id) { 106 | let url = `http://www.xiami.com/song/playlist/id/${id}/object_name/default/object_id/0/cat/json`; 107 | let options = { 108 | url, 109 | method: "GET", 110 | headers: { 111 | user_agent: 112 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36" 113 | } 114 | }; 115 | let promise = new Promise(resolve => { 116 | request(options, (err, res, body) => { 117 | if (err) 118 | return resolve({ 119 | success: false 120 | }); 121 | try { 122 | let data = JSON.parse(body); 123 | let location = data.data.trackList[0].location; 124 | return resolve({ 125 | success: true, 126 | results: { 127 | url: this._handleProtocolRelativeUrl(this._caesar(location)), 128 | lyric: this._handleProtocolRelativeUrl( 129 | data.data.trackList[0].lyric_url 130 | ), 131 | name: data.data.trackList[0].name, 132 | album: data.data.trackList[0].album_name, 133 | artist: data.data.trackList[0].artist_name, 134 | cover: data.data.trackList[0].album_pic 135 | } 136 | }); 137 | } catch (error) { 138 | return resolve({ 139 | success: false, 140 | msg: error.message 141 | }); 142 | } 143 | }); 144 | }); 145 | return promise; 146 | } 147 | __getPageComment(id, page) { 148 | let url = `https://www.xiami.com/commentlist/turnpage/id/${id}/page/${page}/ajax/1`; 149 | let options = { 150 | url, 151 | method: "POST", 152 | body: querystring.stringify({ type: "4" }), 153 | headers: { 154 | "Content-Type": "application/x-www-form-urlencoded", 155 | origin: "https://www.xiami.com", 156 | referer: "https://www.xiami.com/song/" + id, 157 | user_agent: 158 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36" 159 | } 160 | }; 161 | 162 | let promise = new Promise(resolve => { 163 | request(options, (err, res, body) => { 164 | if (err) return resolve({ success: false, msg: err.message }); 165 | try { 166 | let $ = cheerio.load(body), 167 | liArr = $("ul li"), 168 | results = []; 169 | for (let i = 0; i < liArr.length; ++i) { 170 | let li = $(liArr[i]), 171 | id = li.attr("id"); 172 | results.push({ 173 | time: li.find(".info span.time").text() + ":00", 174 | content: li 175 | .find("#brief_" + id) 176 | .clone() 177 | .children() 178 | .remove() 179 | .end() 180 | .text() 181 | .replace(/(\s*$)/g, ""), 182 | user: { 183 | headImgUrl: li.find("img").attr("src"), 184 | nickname: li.find("img").attr("alt") 185 | } 186 | }); 187 | } 188 | return resolve({ success: true, results }); 189 | } catch (error) { 190 | return resolve({ success: false, msg: error.message }); 191 | } 192 | }); 193 | }); 194 | return promise; 195 | } 196 | async getComment(id, page, limit) { 197 | const pageSize = 10; 198 | 199 | let startPage = parseInt(page * limit, 10) / pageSize, 200 | endPage = parseInt((page + 1) * limit, 10) / pageSize, 201 | offset = startPage * pageSize; 202 | 203 | let left = page * limit - offset, 204 | right = (page + 1) * limit - offset, 205 | results = []; 206 | 207 | let promise = new Promise(async resolve => { 208 | for (let i = startPage; i <= endPage; i++) { 209 | if (i === 0) { 210 | continue; 211 | } 212 | let res = await this.__getPageComment(id, i); 213 | if (res.success === false) { 214 | return resolve(res); 215 | } 216 | results = results.concat(res.results); 217 | } 218 | return resolve({ 219 | success: true, 220 | results: results.slice(left, right) 221 | }); 222 | }); 223 | 224 | return promise; 225 | } 226 | } 227 | 228 | module.exports = Music; 229 | -------------------------------------------------------------------------------- /src/vendor/crypto.js: -------------------------------------------------------------------------------- 1 | const CryptoJS = require("crypto-js"); 2 | 3 | (function() { 4 | function b(a, b) { 5 | var c = CryptoJS.enc.Utf8.parse(b), 6 | d = CryptoJS.enc.Utf8.parse("0102030405060708"), 7 | e = CryptoJS.enc.Utf8.parse(a), 8 | f = CryptoJS.AES.encrypt(e, c, { 9 | iv: d, 10 | mode: CryptoJS.mode.CBC 11 | }); 12 | return f.toString(); 13 | } 14 | function d(d, e, f, g) { 15 | var h = {}, 16 | i = "u3wFl5eFwTWI7dHF", 17 | encSecKey = 18 | "1eb2800c1605520f6c62e45a3e7eb9a3d331a4f1491618e4c52c029fd29a2b8535dc58708ce099817dd52b4bb1c9b5243f734dd0236849fd0b2c912aa49fab35659cd72d6633850d121b824237b18b3485e2c36cef52a270fb177aa17b2c7a865a836263a6db440eb1e6cd4a6066a0e379715d78b4b1caacaec76f45ce8a4e28"; 19 | return ( 20 | (h.encText = b(d, g)), 21 | (h.encText = b(h.encText, i)), 22 | (h.encSecKey = encSecKey), 23 | h 24 | ); 25 | } 26 | module.exports = { 27 | asrsea: d 28 | }; 29 | })(); 30 | -------------------------------------------------------------------------------- /test/netease.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/qq-time.js: -------------------------------------------------------------------------------- 1 | define("js/common/comment.js", function(t) { 2 | var e = t("js/common/music.js"), 3 | n = t("js/common/music/lib/base.js"), 4 | o = e.$, 5 | c = e.widget.user, 6 | i = e.popup, 7 | r = e.jQueryAjax, 8 | a = 0, 9 | s = 0, 10 | m = !1, 11 | l = { ct: 24, cv: 101010 }, 12 | p = e.statistics.pgvClickStat, 13 | d = n.extend({ 14 | attrs: { 15 | cur_type: 0, 16 | type: 3, 17 | topid: 0, 18 | cur_page: 0, 19 | per_page: 25, 20 | total_page: 1, 21 | total_num: 0, 22 | hot_cur_page: 0, 23 | hot_per_page: 10, 24 | hot_total_page: 1, 25 | hot_total_num: 0, 26 | commentid: "", 27 | lasthotcommentid: "", 28 | score: 0, 29 | lastscore: 0, 30 | container: ".js_cmt_cont", 31 | outer_container: ".js_layout", 32 | subSource: "", 33 | opCallback: o.noop, 34 | isOnPage: !1, 35 | muscritComment: null, 36 | muscrittotal: 0, 37 | hotComment: null, 38 | hotCommenttotal: 0, 39 | isMuscritself: 0, 40 | scrollBar: document, 41 | auth: 0, 42 | commentStyle: null, 43 | replySongName: "" 44 | }, 45 | initialize: function(t) { 46 | var e = this, 47 | n = t || {}; 48 | a++, 49 | o.extend(n, t), 50 | o(t.container).data("comment", e), 51 | (s = t.topid), 52 | d.superclass.initialize.call(this, n), 53 | e.setReplyContent(), 54 | e.firstShowComment(n.cb), 55 | e.bindEvents(), 56 | e.initEmojj(); 57 | }, 58 | stringEncode: function(t) { 59 | return t 60 | .replace(/\\n/g, "\n") 61 | .HtmlEncode() 62 | .replace(/\\n|\n/g, "
"); 63 | }, 64 | formatDate: function(t) { 65 | var e = new Date(), 66 | n = e.getFullYear(), 67 | o = e.getMonth() + 1, 68 | c = e.getDate(); 69 | if (t > 0) { 70 | if (((t = new Date(1e3 * t)), "Invalid Date" == t)) return ""; 71 | var i = t.getFullYear(), 72 | r = t.getMonth() + 1, 73 | a = t.getDate(), 74 | s = t.getHours(), 75 | m = t.getMinutes(), 76 | l = ""; 77 | return ( 78 | i != n && (l += i + "年"), 79 | (i != n || r != o || a != c) && (l += r + "月" + a + "日 "), 80 | l + (10 > s ? "0" + s : s) + ":" + (10 > m ? "0" + m : m) 81 | ); 82 | } 83 | return ""; 84 | }, 85 | getCmt: function(t, e) { 86 | var n = this, 87 | a = n.get("type") || 0, 88 | s = "jsoncallback" + (Math.random() + "").replace("0.", ""), 89 | m = { 90 | cid: 205360772, 91 | reqtype: 2, 92 | biztype: a, 93 | topid: n.get("topid") || 0, 94 | cmd: 8, 95 | needmusiccrit: 0, 96 | pagenum: 0, 97 | pagesize: 6 == t.cmd ? 10 : n.get("per_page") || 25, 98 | lasthotcommentid: 99 | 6 == t.cmd 100 | ? n.get("lasthotcommentid") || "" 101 | : n.get("lastcommentid") || "", 102 | callback: s, 103 | format: "fs", 104 | domain: "qq.com" 105 | }; 106 | o.extend(m, m, t); 107 | var _ = 108 | location.protocol + 109 | "//c.y.qq.com/base/fcgi-bin/fcg_global_comment_h5.fcg"; 110 | o.extend(m, l), 111 | r.jsonp({ 112 | url: _, 113 | data: m, 114 | jsonpCallback: s, 115 | type: "post", 116 | success: function(t) { 117 | return t && "object" == typeof t 118 | ? (t.uin || (t.uin = c.getUin()), e && e(t), void 0) 119 | : (i.show(t.msg, 2e3, 1), !1); 120 | }, 121 | error: function() { 122 | i.show("网络繁忙,请稍候重试。", 2e3, 1); 123 | } 124 | }); 125 | }, 126 | setPager: function(e, n, c, i) { 127 | { 128 | var r = this, 129 | a = r.get("per_page"), 130 | s = 0, 131 | m = r.get("cur_page"), 132 | l = r.get("cur_type"); 133 | o(r.get("container")); 134 | } 135 | (c || e != l || m != n) && 136 | (l != e && r.set("lasthotcommentid", ""), 137 | r.showPage(e, n, c, function(m) { 138 | (s = r.get("total_num")), 139 | setTimeout(function() { 140 | s > a 141 | ? t.async("js/common/music/pager.js", function() { 142 | o(".js_pager_comment").pager({ 143 | container: ".js_mod_all", 144 | page: ".js_pager_comment", 145 | total: s, 146 | per: a, 147 | cur: n + 1, 148 | index: 3, 149 | ns: "comment", 150 | callback: function(t) { 151 | r.pgvClickStat("pageturn"), r.setPager(e, t - 1, !1); 152 | } 153 | }); 154 | }) 155 | : !!o(".js_pager_comment") && o(".js_pager_comment").hide(); 156 | }, 0), 157 | r.set("cur_page", n), 158 | r.set("cur_type", e), 159 | o("img.js_lazy_comment_pic").lazyload({ event: "scroll" }), 160 | i && i(m), 161 | c || o(".js_mod_all")[0].scrollIntoView(!0); 162 | })); 163 | }, 164 | showPage: function(t, e, n, c) { 165 | var i = this, 166 | r = !1, 167 | a = 0, 168 | s = (i.get("scrollBar"), o(".js_reply_grade")), 169 | m = o(".js_hot_title"), 170 | l = m.position(), 171 | _ = ((l && l.top) || 0, o(i.get("container"))), 172 | p = {}; 173 | n 174 | ? ((p.cmd = 8), (p.needmusiccrit = 0)) 175 | : ((p.needmusiccrit = 0), 1 == t && (p.cmd = 9)), 176 | (p.pagenum = e), 177 | i.getCmt(p, function(m) { 178 | if ( 179 | m && 180 | m.comment && 181 | m.comment.commentlist && 182 | m.comment.commentlist.length 183 | ) { 184 | var l = o.extend(!0, [], m.comment.commentlist).pop(); 185 | i.set("lastcommentid", l && l.commentid), 186 | i.set("cur_page", e), 187 | i.set("cur_type", t); 188 | } 189 | if ( 190 | m && 191 | m.hot_comment && 192 | m.hot_comment.commentlist && 193 | m.hot_comment.commentlist.length 194 | ) { 195 | var l = o.extend(!0, [], m.hot_comment.commentlist).pop(); 196 | i.set("lasthotcommentid", l && l.commentid); 197 | } 198 | if (n) { 199 | (a = parseInt(m.lastscore, 10)), 200 | i.set("auth", m.auth), 201 | i.set("blackuin", m.blackuin), 202 | i.set("superadmin", m.superadmin), 203 | i.set("score", a), 204 | i.set("lastscore", a), 205 | s.data("cur", a); 206 | var p = 20 * a; 207 | p > 100 && (p = 100), 208 | s.width(p + "%"), 209 | m.muscritdouban || m.muscritzhihu 210 | ? (i.set("isMuscritself", 0), 211 | i.set( 212 | "muscritComment", 213 | o.extend(!0, [], m.muscritdouban || m.muscritzhihu) 214 | )) 215 | : m.muscritself && 216 | m.muscritself.length > 0 && 217 | (i.set("isMuscritself", 1), 218 | i.set("muscritComment", o.extend(!0, [], m.muscritself))), 219 | m.muscrittotal > 0 && 220 | (i.set("muscrittotal", m.muscrittotal), i.renderMusicCmt(r)), 221 | m.hot_comment && 222 | m.hot_comment.commentlist && 223 | m.hot_comment.commentlist.length > 0 && 224 | i.renderHotComment(m.hot_comment, m.auth); 225 | } 226 | i.set("total_num", m.comment && m.comment.commenttotal), 227 | m.comment && 228 | m.comment.commentlist && 229 | m.comment.commentlist.length > 0 230 | ? i.renderComment(m.comment, m.auth) 231 | : !o("#js_rule_tip").length && 232 | _.append( 233 | '
还没有人评论,快来抢沙发吧~

QQ音乐评论指北

' 234 | ), 235 | c && c(m); 236 | }); 237 | }, 238 | getContent: function(t, n, c) { 239 | function r(t) { 240 | var n = o.extend(!0, {}, t), 241 | c = e.string.getRealLen(n.content); 242 | return ( 243 | (c = Math.ceil(c / 2)), 244 | c > 300 ? (a = "超出300字上限") : t.content || (a = "请填评论内容"), 245 | a && i.show(a, 2e3, 1), 246 | a 247 | ); 248 | } 249 | var a = r(t); 250 | a ? c && c() : n && n(t); 251 | }, 252 | checkLogin: function() { 253 | var t = c.getUin(); 254 | return t ? !0 : (c.openLogin(function() {}), !1); 255 | }, 256 | sendCmt: function(t, n, a) { 257 | function m(t) { 258 | var m = "jsoncallback" + (Math.random() + "").replace("0.", ""); 259 | if (!t || "" == t.content || "期待你的神评论" == o.trim(t.content)) 260 | return i.show("评论不能为空", 2e3, 1), a && a(), void 0; 261 | var p = o.extend( 262 | { 263 | cid: 205360772, 264 | cmd: 1, 265 | reqtype: 2, 266 | biztype: d.get("type") || 0, 267 | topid: d.get("topid") || 0, 268 | format: "fs", 269 | callback: m, 270 | domain: "qq.com" 271 | }, 272 | t 273 | ), 274 | h = 275 | location.protocol + 276 | "//c.y.qq.com/base/fcgi-bin/fcg_global_comment_h5.fcg"; 277 | o.extend(p, l), 278 | r.jsonp({ 279 | url: h, 280 | data: p, 281 | jsonpCallback: m, 282 | type: "post", 283 | success: function(r) { 284 | if (0 == r.code) { 285 | o(".mod_comment_none").remove(), 286 | t.commentid 287 | ? e.popup.show("回复成功!") 288 | : e.popup.show("评论发表成功!"), 289 | (p.content = d.stringEncode(p.content)), 290 | (r.enable_delete = 1), 291 | (r.uin = u), 292 | (r.avatarurl = r.avatar), 293 | (r.content = d.stringEncode(t.content)), 294 | (r.score = p.score), 295 | (r.commentid = r.newcommentid), 296 | (r.ispraise = 0), 297 | (r.commit_state = 298 | "undefined" == typeof r.commit_state 299 | ? 2 300 | : r.commit_state), 301 | (r.is_hot = 1), 302 | (r.praisenum = 0), 303 | (r.praisenum = 0), 304 | t.rootcommentcontent 305 | ? ((r.rootcommentcontent = d.parseEmoji( 306 | d.stringEncode(t.rootcommentcontent) 307 | )), 308 | o.each(t.middlecommentcontent, function(t, e) { 309 | r.middlecommentcontent || 310 | (r.middlecommentcontent = []), 311 | (e.subcommentcontent = d.parseEmoji( 312 | d.stringEncode(e.subcommentcontent) 313 | )), 314 | r.middlecommentcontent.push(e); 315 | })) 316 | : (r.rootcommentcontent = d.parseEmoji( 317 | d.stringEncode(t.content) 318 | )); 319 | var m = r.newcommentid.split("_")[3]; 320 | (r.time = d.formatDate(m)), (r.loadImmediate = !0); 321 | var l = o(".js_cmt_input"), 322 | h = o(".js_mod_yueping"), 323 | g = o(".js_mod_hot"), 324 | f = o(".js_mod_all"), 325 | j = l; 326 | h.length > 0 && (j = h), 327 | g.length > 0 && (j = g), 328 | 0 == f.length && 329 | (j.after( 330 | (function(t) { 331 | { 332 | var e, 333 | n = ""; 334 | Array.prototype.join; 335 | } 336 | return ( 337 | (n += 338 | '
\r\n
\r\n

最新评论(' + 339 | (null == (e = t.len) ? "" : e) + 340 | ')

\r\n
\r\n \r\n '), 341 | (n += 342 | t.len <= 25 343 | ? '\r\n
—— 以上为全部评论 ——
\r\n ' 344 | : '\r\n
\r\n '), 345 | (n += "\r\n
") 346 | ); 347 | })({ len: 1 }) 348 | ), 349 | o(".js_all_comment_num").html("共1条评论")), 350 | s == d.get("topid") && 351 | o(".js_all_list").prepend( 352 | (function(t) { 353 | { 354 | var n, 355 | c = ""; 356 | Array.prototype.join; 357 | } 358 | c += ""; 359 | for ( 360 | var i = t && t.content, r = 0, a = i.length; 361 | a > r; 362 | r++ 363 | ) { 364 | var s = i[r], 365 | m = 366 | s.middlecommentcontent && 367 | s.middlecommentcontent.length > 0; 368 | if ( 369 | (s.nick || (s.nick = s.uin), 370 | (s.score = Math.round(s.score)), 371 | (c += 372 | '\r\n
  • \r\n \r\n ' +
 385 |                                 (null == (n = s.nick) ? \r\n '), 402 | (c += 403 | '\r\n \r\n

    ' + 406 | (null == (n = o.trim(s.nick)) 407 | ? "" 408 | : _.escape(n)) + 409 | "\r\n "), 410 | s.vipicon && 411 | (c += 412 | '\r\n 绿钻\r\n '), 415 | (c += "\r\n "), 416 | s.is_stick && 417 | (c += 418 | '\r\n \r\n '), 419 | (c += "\r\n

    \r\n\r\n"), 420 | m) 421 | ) { 422 | c += 423 | '\r\n

    \r\n '; 424 | for ( 425 | var l = s.middlecommentcontent.length, p = 0; 426 | l > p; 427 | p++ 428 | ) { 429 | var d = s.middlecommentcontent[p]; 430 | d.replynick || (d.replynick = d.replyuin), 431 | d.replyednick || 432 | (d.replyednick = d.replyeduin); 433 | var u = "", 434 | h = ""; 435 | l > 1 && (u = " // "), 436 | (h = l > 2 && p != l - 1 ? " // " : ""), 437 | (c += "\r\n "), 438 | (c += 439 | 0 == p 440 | ? '回复 ' + 443 | (null == (n = d.replyednick) 444 | ? "" 445 | : _.escape(n)) + 446 | ': ' + 447 | (null == 448 | (n = d.subcommentcontent 449 | .replace(/\n/gi, "
    ") 450 | .replace(/\\n/gi, "
    ")) 451 | ? "" 452 | : n) + 453 | "
    " + 454 | (null == (n = u) ? "" : n) + 455 | " " 456 | : '\r\n ' + 459 | (null == (n = d.replynick) 460 | ? "" 461 | : _.escape(n)) + 462 | ' 回复 ' + 465 | (null == (n = d.replyednick) 466 | ? "" 467 | : _.escape(n)) + 468 | ' : ' + 469 | (null == 470 | (n = d.subcommentcontent 471 | .replace(/\n/gi, "
    ") 472 | .replace(/\\n/gi, "
    ")) 473 | ? "" 474 | : n) + 475 | "
    " + 476 | (null == (n = h) ? "" : n) + 477 | "\r\n "); 478 | } 479 | c += "\r\n

    \r\n"; 480 | } else 481 | (c += 482 | '\r\n

    '), 483 | s.rootcommentcontent && 484 | (c += 485 | "" + 486 | (null == 487 | (n = s.rootcommentcontent 488 | .replace(/\n/gi, "
    ") 489 | .replace(/\\n/gi, "
    ")) 490 | ? "" 491 | : n)), 492 | (c += "

    \r\n"); 493 | (c += "\r\n"), 494 | m && 495 | ((c += 496 | '\r\n

    '), 497 | s.rootcommentcontent && 498 | (c += 499 | "" + 500 | (null == 501 | (n = s.rootcommentcontent 502 | .replace(/\n/gi, "
    ") 503 | .replace(/\\n/gi, "
    ")) 504 | ? "" 505 | : n)), 506 | (c += "

    \r\n")), 507 | (c += 508 | '\r\n
    \r\n ' + 511 | (null == (n = s.time) ? "" : _.escape(n)) + 512 | "\r\n \r\n "), 513 | "undefined" == typeof s.commit_state && 514 | (s.commit_state = 2), 515 | 0 == s.commit_state 516 | ? (c += 517 | '\r\n 投稿\r\n ') 520 | : 1 == s.commit_state && 521 | (c += 522 | '\r\n 已投稿\r\n '), 525 | (c += "\r\n\r\n "), 526 | 1 == t.superadmin && 527 | (c += 528 | '' + 529 | (null == 530 | (n = s.is_stick ? "取消置顶" : "置顶") 531 | ? "" 532 | : n) + 533 | ""), 534 | (c += "\r\n "), 535 | 1 == s.enable_delete && 536 | (c += 537 | '\r\n 删除\r\n '), 538 | (c += "\r\n \r\n "), 539 | 1 == s.is_hot && 540 | (c += 541 | '\r\n ' + 548 | (null == (n = s.praisenum) ? "" : n) + 549 | '\r\n \r\n '), 556 | (c += 557 | '\r\n
    \r\n
    \r\n
  • \r\n'); 558 | } 559 | return (c += "\r\n"); 560 | })({ 561 | content: [r], 562 | type: d.get("type"), 563 | auth: d.get("auth"), 564 | uin: c.getUin() 565 | }) 566 | ), 567 | o("img.js_lazy_comment_pic").lazyload({ event: "scroll" }), 568 | "function" == typeof n && n(); 569 | } else 570 | 1e3 == r.code 571 | ? c.openLogin() 572 | : (i.show(r.msg, 2e3, 1), a && a()); 573 | }, 574 | error: function() { 575 | i.show("发表评论失败!网络繁忙,请稍候重试。", 2e3, 1), 576 | a && a(); 577 | } 578 | }); 579 | } 580 | var p = t.content; 581 | if ("" == o.trim(p.replace(/\\n/gi, ""))) 582 | return i.show("评论不能为空", 2e3, 1), a && a(), !1; 583 | var d = this, 584 | u = c.getUin(); 585 | return u 586 | ? (d.getContent(t, m, a), void 0) 587 | : (c.openLogin(function() {}), void 0); 588 | }, 589 | delCmt: function(t) { 590 | var e = c.getUin(); 591 | if (!e) return c.openLogin(function() {}), void 0; 592 | var n = "jsoncallback" + (Math.random() + "").replace("0.", ""), 593 | a = { 594 | cid: 205360772, 595 | cmd: 3, 596 | commentid: t || "", 597 | callback: n, 598 | format: "fs", 599 | domain: "qq.com" 600 | }, 601 | s = 602 | location.protocol + 603 | "//c.y.qq.com/base/fcgi-bin/fcg_global_comment_h5.fcg"; 604 | o.extend(a, l), 605 | r.jsonp({ 606 | url: s, 607 | data: a, 608 | jsonpCallback: n, 609 | type: "post", 610 | success: function(e) { 611 | 0 == e.code 612 | ? (o('.js_comment_opt[data-commentid="' + t + '"]') 613 | .closest(".js_cmt_li") 614 | .remove(), 615 | 0 == o(".js_all_list").children().length && 616 | o(".js_mod_all").remove(), 617 | 0 == o(".js_hot_list").children().length && 618 | o(".js_mod_hot").remove(), 619 | i.show("删除成功", 2e3, 300)) 620 | : 1e3 == e.code 621 | ? c.openLogin() 622 | : i.show(e.msg, 2e3, 1); 623 | }, 624 | error: function() { 625 | i.show("网络繁忙,请稍候重试。", 2e3, 1); 626 | } 627 | }); 628 | }, 629 | praiseCmt: function(t, e) { 630 | var n = this, 631 | a = c.getUin(); 632 | if (!a) return c.openLogin(function() {}), void 0; 633 | var s = "jsoncallback" + (Math.random() + "").replace("0.", ""), 634 | m = { 635 | cid: 205360774, 636 | cmd: e, 637 | reqtype: 2, 638 | biztype: n.get("type") || 0, 639 | topid: n.get("topid") || 0, 640 | commentid: t, 641 | qq: c.getUin(), 642 | callback: s, 643 | domain: "qq.com" 644 | }; 645 | n.praiseCmt_sending = !0; 646 | var _ = 647 | location.protocol + 648 | "//c.y.qq.com/base/fcgi-bin/fcg_global_comment_praise_h5.fcg"; 649 | o.extend(m, l), 650 | r.jsonp({ 651 | url: _, 652 | data: m, 653 | jsonpCallback: s, 654 | type: "post", 655 | success: function(r) { 656 | if (0 == r.code) { 657 | var a = o( 658 | '.js_comment_opt .js_cmt_praise[data-commentid="' + t + '"]' 659 | ), 660 | s = a.find(".js_praise_num").eq(0), 661 | m = parseInt(s.text()) + (1 == e ? 1 : -1); 662 | a.html( 663 | '' + 664 | (m > 0 ? m : 0) + 665 | "" 666 | ), 667 | a.toggleClass("done", 1 == e); 668 | } else 1e3 == r.code ? c.openLogin() : i.show(r.msg, 2e3, 1); 669 | n.praiseCmt_sending = !1; 670 | }, 671 | error: function() { 672 | (n.praiseCmt_sending = !1), 673 | i.show("网络繁忙,请稍候重试。", 2e3, 1); 674 | } 675 | }); 676 | }, 677 | approveCmt: function(t, e) { 678 | var n = this, 679 | a = c.getUin(); 680 | if (!a) return c.openLogin(function() {}), void 0; 681 | var s = "jsoncallback" + (Math.random() + "").replace("0.", ""), 682 | m = { 683 | cmd: e, 684 | comment_id: t, 685 | qq: c.getUin(), 686 | callback: s, 687 | domain: "qq.com" 688 | }; 689 | n.approveCmt_sending = !0; 690 | var _ = 691 | location.protocol + 692 | "//c.y.qq.com/base/fcgi-bin/fcg_comment_notice.fcg"; 693 | o.extend(m, l), 694 | r.jsonp({ 695 | url: _, 696 | data: m, 697 | jsonpCallback: s, 698 | type: "post", 699 | success: function(r) { 700 | if (0 == r.code) { 701 | var a = o( 702 | '.js_comment_opt[data-commentid="' + t + '"] .js_approve' 703 | ); 704 | a.html(4 == e ? "取消通过审核" : "通过审核"); 705 | } else 1e3 == r.code ? c.openLogin() : i.show(r.msg, 2e3, 1); 706 | n.approveCmt_sending = !1; 707 | }, 708 | error: function() { 709 | (n.approveCmt_sending = !1), 710 | i.show("网络繁忙,请稍候重试。", 2e3, 1); 711 | } 712 | }); 713 | }, 714 | upCmt: function(t, e) { 715 | var n = c.getUin(), 716 | a = this; 717 | if (!n) return c.openLogin(function() {}), void 0; 718 | a.upCmt_sending = !0; 719 | var s = "jsoncallback" + (Math.random() + "").replace("0.", ""), 720 | m = { 721 | cid: 205360772, 722 | cmd: 12, 723 | commentid: t || "", 724 | stick_type: e || 1, 725 | callback: s, 726 | format: "fs", 727 | domain: "qq.com" 728 | }, 729 | _ = 730 | location.protocol + 731 | "//c.y.qq.com/base/fcgi-bin/fcg_global_comment_h5.fcg"; 732 | o.extend(m, l), 733 | r.jsonp({ 734 | url: _, 735 | data: m, 736 | jsonpCallback: s, 737 | type: "post", 738 | success: function(n) { 739 | if (0 == n.code) { 740 | var r = o( 741 | '.js_comment_opt[data-commentid="' + t + '"] .js_up_comment' 742 | ); 743 | r.html(1 == e ? "取消置顶" : "置顶"); 744 | } else 1e3 == n.code ? c.openLogin() : i.show(n.msg, 2e3, 1); 745 | a.upCmt_sending = !1; 746 | }, 747 | error: function() { 748 | (a.upCmt_sending = !1), i.show("网络繁忙,请稍候重试。", 2e3, 1); 749 | } 750 | }); 751 | }, 752 | reportCmt: function(t, e, n, a) { 753 | var s = c.getUin(), 754 | m = this; 755 | if (!s) return c.openLogin(function() {}), void 0; 756 | m.reportCmt_sending = !0; 757 | var _ = "jsoncallback" + (Math.random() + "").replace("0.", ""), 758 | p = { 759 | cid: 205360772, 760 | cmd: 10, 761 | commentid: t || "", 762 | rptcmd: a || 1, 763 | rptmsg: e || "其它", 764 | detailmsg: n || "", 765 | format: "fs", 766 | callback: _, 767 | domain: "qq.com" 768 | }, 769 | d = 770 | location.protocol + 771 | "//c.y.qq.com/base/fcgi-bin/fcg_global_comment_h5.fcg"; 772 | o.extend(p, l), 773 | r.jsonp({ 774 | url: d, 775 | data: p, 776 | jsonpCallback: _, 777 | type: "post", 778 | success: function(t) { 779 | 0 == t.code || 780 | (1e3 == t.code ? c.openLogin() : i.show(t.msg, 2e3, 1)), 781 | (m.reportCmt_sending = !1); 782 | }, 783 | error: function() { 784 | (m.reportCmt_sending = !1), 785 | i.show("网络繁忙,请稍候重试。", 2e3, 1); 786 | } 787 | }); 788 | }, 789 | formatComments: function(t) { 790 | for (var e = this, n = null, o = 0, c = t.length; c > o; o++) 791 | (n = t[o]), (n.time = e.formatDate(n.time)); 792 | return t || []; 793 | }, 794 | setReplyContent: function() { 795 | var t = this, 796 | e = t.get("container"); 797 | o(e).html(function() { 798 | { 799 | var t = ""; 800 | Array.prototype.join; 801 | } 802 | return (t += 803 | '
    \r\n

    评论

    \r\n
    \r\n
    \r\n\r\n
    \r\n
    \r\n
    \r\n \r\n \r\n
    期待你的神评论……
    \r\n \r\n
    \r\n \r\n
    剩余300
    \r\n
    \r\n \r\n \r\n \r\n
    \r\n
    '); 804 | }); 805 | }, 806 | renderMusicCmt: function(t) { 807 | var n = this, 808 | c = n.get("muscritComment"), 809 | i = n.get("muscrittotal"), 810 | r = n.get("isMuscritself"), 811 | a = n.get("type"), 812 | s = o.extend(!0, [], c) || [], 813 | m = o(".js_cmt_input"), 814 | l = o(".js_mod_yueping"), 815 | p = [], 816 | d = 0, 817 | u = "乐评"; 818 | (d = t ? i : 3), 819 | (p = s.slice(0, d)), 820 | !r && (u = 1 == a ? "知乎乐评" : "豆瓣乐评"), 821 | 0 == l.length && 822 | m.after( 823 | (function(t) { 824 | { 825 | var e, 826 | n = ""; 827 | Array.prototype.join; 828 | } 829 | n += 830 | '
    \r\n
    \r\n

    ' + 831 | (null == (e = t.title) ? "" : _.escape(e)) + 832 | "(" + 833 | (null == (e = t.len) ? "" : e) + 834 | ')

    \r\n \r\n
    \r\n \r\n \r\n '; 835 | var o = "全部乐评(" + t.len + ")"; 836 | return ( 837 | t.len > 3 && 838 | (n += 839 | '\r\n
    \r\n ' + 840 | (null == (e = o) ? "" : _.escape(e)) + 841 | "\r\n
    \r\n "), 842 | (n += "\r\n
    ") 843 | ); 844 | })({ len: i, isall: t, title: u }) 845 | ); 846 | for (var h = 0, g = p.length; g > h; h++) 847 | p[h].muscritcontent && 848 | "string" == typeof p[h].muscritcontent && 849 | (p[h].muscritcontent = n.parseEmoji( 850 | n.stringEncode(p[h].muscritcontent) 851 | )); 852 | o(".js_yueping_list").html( 853 | (function(t) { 854 | { 855 | var n, 856 | o = ""; 857 | Array.prototype.join; 858 | } 859 | o += ""; 860 | for (var c = t.content, i = 0, r = c && c.length; r > i; i++) { 861 | var a = c[i]; 862 | o += 863 | '\r\n
  • \r\n
    \r\n ' +
 866 |                 (null == (n = a.author) ? \r\n
    \r\n

    ' + 870 | (null == (n = a.title) ? "" : _.escape(n)) + 871 | '

    \r\n

    ' + 872 | (null == (n = a.muscritcontent) ? "" : n) + 873 | '

    \r\n

    查看全文\r\n

    \r\n
  • \r\n'; 882 | } 883 | return (o += "\r\n\r\n"); 884 | })({ content: p, isMuscritself: r }) 885 | ), 886 | o("img.js_lazy_comment_pic").lazyload({ event: "scroll" }); 887 | }, 888 | getMoreHotComment: function() { 889 | var t = this; 890 | t.getCmt( 891 | { 892 | cmd: 6, 893 | pagesize: t.get("hot_per_page"), 894 | pagenum: t.get("hot_cur_page") + 1 895 | }, 896 | function(e) { 897 | o(".js_get_more_hot").data("loading", 0), 898 | (e.comment.more = e.morecomment), 899 | t.renderHotComment(e.comment, e.auth), 900 | o("img.js_lazy_comment_pic").lazyload({ event: "scroll" }), 901 | t.set("hot_cur_page", t.get("hot_cur_page") + 1); 902 | } 903 | ); 904 | }, 905 | renderHotComment: function(t, n) { 906 | { 907 | var i = this, 908 | r = (o.extend(!0, [], t), t.commenttotal), 909 | a = o.extend(!0, [], t.commentlist) || [], 910 | s = o(".js_cmt_input"), 911 | m = o(".js_mod_yueping"), 912 | l = s, 913 | p = o(".js_mod_hot"), 914 | d = null; 915 | o(i.get("container")); 916 | } 917 | (a = i.formatComments(a)), 918 | m.length > 0 && (l = m), 919 | i.set("hot_total_num", r); 920 | var u = i.get("hot_total_num"), 921 | h = i.get("hot_cur_page"), 922 | g = i.get("hot_per_page"); 923 | 0 == p.length && 924 | l.after( 925 | (function(t) { 926 | { 927 | var e, 928 | n = ""; 929 | Array.prototype.join; 930 | } 931 | return (n += 932 | '
    \r\n
    \r\n
    \r\n

    精彩评论(' + 933 | (null == (e = t.len) ? "" : e) + 934 | ')

    \r\n
    \r\n \r\n \r\n \r\n
    \r\n
    '); 935 | })({ 936 | len: r, 937 | more: "undefined" == typeof t.more ? u > (h + 1) * g : 1 == t.more 938 | }) 939 | ), 940 | r > 0 && 941 | i.get("$mod_title") && 942 | i.get("$mod_title").text("精彩评论(" + r + ")"); 943 | for (var f = 0, j = 0, v = a.length; v > j; j++) 944 | if ( 945 | ((d = a[j]), 946 | d.is_hot && f++, 947 | d.rootcommentcontent && 948 | "string" == typeof d.rootcommentcontent && 949 | (d.rootcommentcontent = i.parseEmoji( 950 | i.stringEncode(d.rootcommentcontent) 951 | )), 952 | d.middlecommentcontent) 953 | ) 954 | for (var y = 0, k = d.middlecommentcontent.length; k > y; y++) { 955 | var b = d.middlecommentcontent[y].subcommentcontent; 956 | b && 957 | "string" == typeof b && 958 | (b = i.parseEmoji( 959 | i.stringEncode(d.middlecommentcontent[y].subcommentcontent) 960 | )), 961 | (d.middlecommentcontent[y].subcommentcontent = b); 962 | } 963 | o(".js_hot_list").append( 964 | (function(t) { 965 | { 966 | var n, 967 | c = ""; 968 | Array.prototype.join; 969 | } 970 | c += ""; 971 | for (var i = t && t.content, r = 0, a = i.length; a > r; r++) { 972 | var s = i[r], 973 | m = s.middlecommentcontent && s.middlecommentcontent.length > 0; 974 | if ( 975 | (s.nick || (s.nick = s.uin), 976 | (s.score = Math.round(s.score)), 977 | (c += 978 | '\r\n
  • \r\n \r\n ' +
 988 |                   (null == (n = s.nick) ? \r\n '), 1000 | (c += 1001 | '\r\n \r\n

    ' + 1004 | (null == (n = o.trim(s.nick)) ? "" : _.escape(n)) + 1005 | "\r\n "), 1006 | s.vipicon && 1007 | (c += 1008 | '\r\n 绿钻\r\n '), 1011 | (c += "\r\n \r\n "), 1012 | s.is_stick && 1013 | (c += 1014 | '\r\n \r\n '), 1015 | (c += "\r\n

    \r\n\r\n"), 1016 | m) 1017 | ) { 1018 | c += 1019 | '\r\n

    \r\n '; 1020 | for (var l = s.middlecommentcontent.length, p = 0; l > p; p++) { 1021 | var d = s.middlecommentcontent[p]; 1022 | d.replynick || (d.replynick = d.replyuin), 1023 | d.replyednick || (d.replyednick = d.replyeduin); 1024 | var u = "", 1025 | h = ""; 1026 | l > 1 && (u = " // "), 1027 | (h = l > 2 && p != l - 1 ? " // " : ""), 1028 | (c += "\r\n "), 1029 | (c += 1030 | 0 == p 1031 | ? '回复 ' + 1034 | (null == (n = d.replyednick) ? "" : _.escape(n)) + 1035 | ': ' + 1036 | (null == 1037 | (n = d.subcommentcontent 1038 | .replace(/\n/gi, "
    ") 1039 | .replace(/\\n/gi, "
    ")) 1040 | ? "" 1041 | : n) + 1042 | "
    " + 1043 | (null == (n = u) ? "" : n) + 1044 | " " 1045 | : '\r\n ' + 1048 | (null == (n = d.replynick) ? "" : _.escape(n)) + 1049 | ' 回复 ' + 1052 | (null == (n = d.replyednick) ? "" : _.escape(n)) + 1053 | ' : ' + 1054 | (null == 1055 | (n = d.subcommentcontent 1056 | .replace(/\n/gi, "
    ") 1057 | .replace(/\\n/gi, "
    ")) 1058 | ? "" 1059 | : n) + 1060 | "
    " + 1061 | (null == (n = h) ? "" : n) + 1062 | "\r\n "); 1063 | } 1064 | c += "\r\n

    \r\n"; 1065 | } else 1066 | (c += '\r\n

    '), 1067 | s.rootcommentcontent && 1068 | (c += 1069 | "" + 1070 | (null == 1071 | (n = s.rootcommentcontent 1072 | .replace(/\n/gi, "
    ") 1073 | .replace(/\\n/gi, "
    ")) 1074 | ? "" 1075 | : n)), 1076 | (c += "

    \r\n"); 1077 | (c += "\r\n"), 1078 | m && 1079 | ((c += 1080 | '\r\n

    '), 1081 | s.rootcommentcontent && 1082 | (c += 1083 | "" + 1084 | (null == 1085 | (n = s.rootcommentcontent 1086 | .replace(/\n/gi, "
    ") 1087 | .replace(/\\n/gi, "
    ")) 1088 | ? "" 1089 | : n)), 1090 | (c += "

    \r\n")), 1091 | (c += 1092 | '\r\n
    \r\n ' + 1095 | (null == (n = s.time) ? "" : _.escape(n)) + 1096 | "\r\n \r\n "), 1097 | "undefined" == typeof s.commit_state && (s.commit_state = 2), 1098 | 0 == s.commit_state 1099 | ? (c += 1100 | '\r\n 投稿\r\n ') 1103 | : 1 == s.commit_state && 1104 | (c += 1105 | '\r\n 已投稿\r\n '), 1108 | (c += "\r\n\r\n "), 1109 | 1 == t.superadmin && 1110 | (c += 1111 | '' + 1112 | (null == (n = s.is_stick ? "取消置顶" : "置顶") ? "" : n) + 1113 | ""), 1114 | (c += "\r\n "), 1115 | 1 == s.enable_delete && 1116 | (c += 1117 | '\r\n 删除\r\n '), 1118 | (c += "\r\n "), 1119 | 1 == s.is_hot && 1120 | (c += 1121 | '\r\n ' + 1126 | (null == (n = s.praisenum) ? "" : n) + 1127 | '\r\n \r\n '), 1134 | (c += 1135 | '\r\n
    \r\n
    \r\n
  • \r\n'); 1136 | } 1137 | return (c += "\r\n"); 1138 | })({ 1139 | content: a, 1140 | type: i.get("type"), 1141 | auth: n, 1142 | uin: c.getUin(), 1143 | blackuin: i.get("blackuin"), 1144 | superadmin: i.get("superadmin") 1145 | }) 1146 | ), 1147 | ("undefined" == typeof t.more 1148 | ? u > (h + 1) * g 1149 | : 1 == t.more) 1150 | ? o(".js_get_more_hot").show() 1151 | : setTimeout(function() { 1152 | o(".js_get_more_hot").show(), 1153 | o(".js_get_more_hot") 1154 | .parent() 1155 | .html( 1156 | '精彩评论已加载完毕' 1157 | ); 1158 | }, 800); 1159 | }, 1160 | renderComment: function(t, n) { 1161 | o(".mod_comment_none").remove(); 1162 | var i = this, 1163 | r = (o.extend(!0, [], t), t.commenttotal), 1164 | a = o.extend(!0, [], t.commentlist) || [], 1165 | s = o(".js_cmt_input"), 1166 | m = o(".js_mod_yueping"), 1167 | l = o(".js_mod_hot"), 1168 | p = o(".js_mod_all"), 1169 | d = s, 1170 | u = null, 1171 | h = o(i.get("container")); 1172 | m.length > 0 && (d = m), 1173 | l.length > 0 && (d = l), 1174 | (a = i.formatComments(a)), 1175 | 0 == p.length && 1176 | (d.after( 1177 | (function(t) { 1178 | { 1179 | var e, 1180 | n = ""; 1181 | Array.prototype.join; 1182 | } 1183 | return ( 1184 | (n += 1185 | '
    \r\n
    \r\n

    最新评论(' + 1186 | (null == (e = t.len) ? "" : e) + 1187 | ')

    \r\n
    \r\n \r\n '), 1188 | (n += 1189 | t.len <= 25 1190 | ? '\r\n
    —— 以上为全部评论 ——
    \r\n ' 1191 | : '\r\n
    \r\n '), 1192 | (n += "\r\n
    ") 1193 | ); 1194 | })({ len: r }) 1195 | ), 1196 | o(".js_all_comment_num").html("共" + r + "条评论")), 1197 | r > 0 && 1198 | i.get("$mod_title") && 1199 | i.get("$mod_title").text("全部评论(" + r + ")"); 1200 | for (var g = 0, f = 0, j = a.length; j > f; f++) 1201 | if ( 1202 | ((u = a[f]), 1203 | u.is_hot && g++, 1204 | u.rootcommentcontent && 1205 | "string" == typeof u.rootcommentcontent && 1206 | (u.rootcommentcontent = i.parseEmoji( 1207 | i.stringEncode(u.rootcommentcontent) 1208 | )), 1209 | u.middlecommentcontent) 1210 | ) 1211 | for (var v = 0, y = u.middlecommentcontent.length; y > v; v++) { 1212 | var k = u.middlecommentcontent[v].subcommentcontent; 1213 | k && 1214 | "string" == typeof k && 1215 | (k = i.parseEmoji( 1216 | i.stringEncode(u.middlecommentcontent[v].subcommentcontent) 1217 | )), 1218 | (u.middlecommentcontent[v].subcommentcontent = k); 1219 | } 1220 | o(".js_all_list").html( 1221 | (function(t) { 1222 | { 1223 | var n, 1224 | c = ""; 1225 | Array.prototype.join; 1226 | } 1227 | c += ""; 1228 | for (var i = t && t.content, r = 0, a = i.length; a > r; r++) { 1229 | var s = i[r], 1230 | m = s.middlecommentcontent && s.middlecommentcontent.length > 0; 1231 | if ( 1232 | (s.nick || (s.nick = s.uin), 1233 | (s.score = Math.round(s.score)), 1234 | (c += 1235 | '\r\n
  • \r\n \r\n ' +
1245 |                   (null == (n = s.nick) ? \r\n '), 1257 | (c += 1258 | '\r\n \r\n

    ' + 1261 | (null == (n = o.trim(s.nick)) ? "" : _.escape(n)) + 1262 | "\r\n "), 1263 | s.vipicon && 1264 | (c += 1265 | '\r\n 绿钻\r\n '), 1268 | (c += "\r\n "), 1269 | s.is_stick && 1270 | (c += 1271 | '\r\n \r\n '), 1272 | (c += "\r\n

    \r\n\r\n"), 1273 | m) 1274 | ) { 1275 | c += 1276 | '\r\n

    \r\n '; 1277 | for (var l = s.middlecommentcontent.length, p = 0; l > p; p++) { 1278 | var d = s.middlecommentcontent[p]; 1279 | d.replynick || (d.replynick = d.replyuin), 1280 | d.replyednick || (d.replyednick = d.replyeduin); 1281 | var u = "", 1282 | h = ""; 1283 | l > 1 && (u = " // "), 1284 | (h = l > 2 && p != l - 1 ? " // " : ""), 1285 | (c += "\r\n "), 1286 | (c += 1287 | 0 == p 1288 | ? '回复 ' + 1291 | (null == (n = d.replyednick) ? "" : _.escape(n)) + 1292 | ': ' + 1293 | (null == 1294 | (n = d.subcommentcontent 1295 | .replace(/\n/gi, "
    ") 1296 | .replace(/\\n/gi, "
    ")) 1297 | ? "" 1298 | : n) + 1299 | "
    " + 1300 | (null == (n = u) ? "" : n) + 1301 | " " 1302 | : '\r\n ' + 1305 | (null == (n = d.replynick) ? "" : _.escape(n)) + 1306 | ' 回复 ' + 1309 | (null == (n = d.replyednick) ? "" : _.escape(n)) + 1310 | ' : ' + 1311 | (null == 1312 | (n = d.subcommentcontent 1313 | .replace(/\n/gi, "
    ") 1314 | .replace(/\\n/gi, "
    ")) 1315 | ? "" 1316 | : n) + 1317 | "
    " + 1318 | (null == (n = h) ? "" : n) + 1319 | "\r\n "); 1320 | } 1321 | c += "\r\n

    \r\n"; 1322 | } else 1323 | (c += '\r\n

    '), 1324 | s.rootcommentcontent && 1325 | (c += 1326 | "" + 1327 | (null == 1328 | (n = s.rootcommentcontent 1329 | .replace(/\n/gi, "
    ") 1330 | .replace(/\\n/gi, "
    ")) 1331 | ? "" 1332 | : n)), 1333 | (c += "

    \r\n"); 1334 | (c += "\r\n"), 1335 | m && 1336 | ((c += 1337 | '\r\n

    '), 1338 | s.rootcommentcontent && 1339 | (c += 1340 | "" + 1341 | (null == 1342 | (n = s.rootcommentcontent 1343 | .replace(/\n/gi, "
    ") 1344 | .replace(/\\n/gi, "
    ")) 1345 | ? "" 1346 | : n)), 1347 | (c += "

    \r\n")), 1348 | (c += 1349 | '\r\n
    \r\n ' + 1352 | (null == (n = s.time) ? "" : _.escape(n)) + 1353 | "\r\n \r\n "), 1354 | "undefined" == typeof s.commit_state && (s.commit_state = 2), 1355 | 0 == s.commit_state 1356 | ? (c += 1357 | '\r\n 投稿\r\n ') 1360 | : 1 == s.commit_state && 1361 | (c += 1362 | '\r\n 已投稿\r\n '), 1365 | (c += "\r\n\r\n "), 1366 | 1 == t.superadmin && 1367 | (c += 1368 | '' + 1369 | (null == (n = s.is_stick ? "取消置顶" : "置顶") ? "" : n) + 1370 | ""), 1371 | (c += "\r\n "), 1372 | 1 == s.enable_delete && 1373 | (c += 1374 | '\r\n 删除\r\n '), 1375 | (c += "\r\n \r\n "), 1376 | 1 == s.is_hot && 1377 | (c += 1378 | '\r\n ' + 1383 | (null == (n = s.praisenum) ? "" : n) + 1384 | '\r\n \r\n '), 1391 | (c += 1392 | '\r\n
    \r\n
    \r\n
  • \r\n'); 1393 | } 1394 | return (c += "\r\n"); 1395 | })({ 1396 | content: a, 1397 | type: i.get("type"), 1398 | auth: n, 1399 | uin: c.getUin(), 1400 | blackuin: i.get("blackuin"), 1401 | superadmin: i.get("superadmin") 1402 | }) 1403 | ), 1404 | o(".js_rule_btn") 1405 | .parent() 1406 | .remove(), 1407 | !o("#js_rule_tip").length && 1408 | h.append( 1409 | '

    QQ音乐评论指北

    ' 1410 | ); 1411 | }, 1412 | firstShowComment: function(t) { 1413 | var e = this, 1414 | n = e.get("cur_type"), 1415 | o = e.get("cur_page"); 1416 | e.setPager(n, o, !0, t); 1417 | }, 1418 | lastEditRange: null, 1419 | getCursor: function() { 1420 | return this.lastEditRange ? this.lastEditRange.startOffset : 0; 1421 | }, 1422 | setEmojiCursor: function(t, e, n) { 1423 | n && n(); 1424 | var o = t[0], 1425 | c = getSelection(); 1426 | if ("#text" != c.anchorNode.nodeName) { 1427 | var i = document.createTextNode(e); 1428 | if (o.childNodes.length > 0) 1429 | for (var r = 0; r < o.childNodes.length; r++) 1430 | r == c.anchorOffset && o.insertBefore(i, o.childNodes[r]); 1431 | else o.appendChild(i); 1432 | var a = document.createRange(); 1433 | a.selectNodeContents(i), 1434 | a.setStart(i, i.length), 1435 | a.collapse(!0), 1436 | c.removeAllRanges(), 1437 | c.addRange(a); 1438 | } else { 1439 | var a = this.lastEditRange || c.getRangeAt(0), 1440 | s = a.startContainer, 1441 | m = a.startOffset; 1442 | s.insertData(m, e), 1443 | a.setStart(s, m + e.length), 1444 | a.collapse(!0), 1445 | c.removeAllRanges(), 1446 | c.addRange(a); 1447 | } 1448 | }, 1449 | showTitleNum: function(t, n) { 1450 | var o = n.data("max"), 1451 | c = e.string.getRealLen(t.text()); 1452 | (c = Math.ceil(c / 2)), 1453 | o >= c 1454 | ? (n.addClass("c_tx_thin"), 1455 | n.removeClass("comment__tips--warn_tx"), 1456 | n.html( 1457 | '剩余' + (o - c) + "字" 1458 | )) 1459 | : (n.removeClass("c_tx_thin"), 1460 | n.addClass("comment__tips--warn_tx"), 1461 | n.html( 1462 | '超过' + (c - o) + "字" 1463 | )); 1464 | var i = getSelection(); 1465 | this.lastEditRange = i.getRangeAt(0); 1466 | }, 1467 | showMusicianDetail: function() { 1468 | console.log(detailHtml); 1469 | }, 1470 | showConfirm: function(e, n, o, c, i) { 1471 | t.async("js/common/dialog.js", function(t) { 1472 | t.show({ 1473 | mode: "common", 1474 | title: "QQ音乐", 1475 | icon_type: 2, 1476 | sub_title: e, 1477 | desc: n, 1478 | button_info1: { 1479 | highlight: 1, 1480 | title: o || "确定", 1481 | fn: function() { 1482 | t.hide(), c && c(); 1483 | } 1484 | }, 1485 | button_info2: { 1486 | highlight: 0, 1487 | title: i || "取消", 1488 | fn: function() { 1489 | t.hide(); 1490 | } 1491 | } 1492 | }); 1493 | }); 1494 | }, 1495 | parseEmoji: function(t) { 1496 | return t 1497 | .replace( 1498 | /[em]e(\d{1,8})(?:,\d{1,3},\d{1,3})?[/em]/gi, 1499 | function(t, e) { 1500 | return ( 1501 | "" 1504 | ); 1505 | } 1506 | ) 1507 | .replace( 1508 | /\[em(?:2)?\]e(\d{1,8})(?:,\d{1,3},\d{1,3})?\[\/em(?:2)?\]/gi, 1509 | function(t, e) { 1510 | return ( 1511 | "" 1514 | ); 1515 | } 1516 | ); 1517 | }, 1518 | pgvClickStat: function(t) { 1519 | var e = this; 1520 | p("y_new." + e.get("subSource") + "." + t); 1521 | }, 1522 | remove: function() { 1523 | var t = this, 1524 | e = o(t.get("container")), 1525 | n = o(t.get("outer_container")); 1526 | try { 1527 | e.data("comment", null), 1528 | e.remove(), 1529 | n.remove(".js_emoji_dialog"), 1530 | t.destroy(); 1531 | } catch (c) {} 1532 | }, 1533 | initEmojj: function() { 1534 | var t = "", 1535 | e = this; 1536 | if (!m) { 1537 | t = (function() { 1538 | { 1539 | var t, 1540 | e = ""; 1541 | Array.prototype.join; 1542 | } 1543 | e += 1544 | '\r\n\r\n"); 1553 | })({}); 1554 | var n = { 1555 | 1: "e400846", 1556 | 2: "e400874", 1557 | 3: "e400825", 1558 | 4: "e400847", 1559 | 5: "e400835", 1560 | 6: "e400873", 1561 | 7: "e400836", 1562 | 8: "e400867", 1563 | 9: "e400832", 1564 | 10: "e400837", 1565 | 11: "e400875", 1566 | 12: "e400831", 1567 | 13: "e400855", 1568 | 14: "e400823", 1569 | 15: "e400862", 1570 | 16: "e400844", 1571 | 17: "e400841", 1572 | 18: "e400830", 1573 | 19: "e400828", 1574 | 20: "e400833", 1575 | 21: "e400822", 1576 | 22: "e400843", 1577 | 23: "e400829", 1578 | 24: "e400824", 1579 | 25: "e400834", 1580 | 26: "e400877", 1581 | 27: "e400132", 1582 | 28: "e400181", 1583 | 29: "e401067", 1584 | 30: "e400186", 1585 | 31: "e400343", 1586 | 32: "e400116", 1587 | 33: "e400126", 1588 | 34: "e400613", 1589 | 35: "e401236", 1590 | 36: "e400622", 1591 | 37: "e400637", 1592 | 38: "e400643", 1593 | 39: "e400773", 1594 | 40: "e400102", 1595 | 41: "e401328", 1596 | 42: "e400420", 1597 | 43: "e400914", 1598 | 44: "e400408", 1599 | 45: "e400414", 1600 | 46: "e401121", 1601 | 47: "e400396", 1602 | 48: "e400384", 1603 | 49: "e401115", 1604 | 50: "e400402", 1605 | 51: "e400905", 1606 | 52: "e400906", 1607 | 53: "e400907", 1608 | 54: "e400562", 1609 | 55: "e400932", 1610 | 56: "e400644", 1611 | 57: "e400611", 1612 | 58: "e400185", 1613 | 59: "e400655", 1614 | 60: "e400325", 1615 | 61: "e400612", 1616 | 62: "e400198", 1617 | 63: "e401685", 1618 | 64: "e400631", 1619 | 65: "e400768", 1620 | 66: "e400432" 1621 | }; 1622 | o("body") 1623 | .off("click", ".js_emoji_dialog .js_emoji") 1624 | .on("click", ".js_emoji_dialog .js_emoji", function() { 1625 | var t = o(this).parents(".comment__input"), 1626 | c = o(this).data("key"); 1627 | o(".js_reply_text", t).length > 0 1628 | ? (e.setEmojiCursor( 1629 | o("#reply_text"), 1630 | "[em]" + (c in n ? n[c] : c) + "[/em]", 1631 | function() { 1632 | o("#reply_text_blur").hide(), 1633 | o("#reply_text").show(), 1634 | o("#reply_text").focus(); 1635 | } 1636 | ), 1637 | e.showTitleNum(o(".js_reply_text"), o(".js_comment_tips"))) 1638 | : (e.setEmojiCursor( 1639 | o("#replyed_text"), 1640 | "[em]" + (c in n ? n[c] : c) + "[/em]", 1641 | function() { 1642 | o("#replyed_text_blur").hide(), 1643 | o("#replyed_text").show(), 1644 | o("#replyed_text").focus(); 1645 | } 1646 | ), 1647 | e.showTitleNum( 1648 | o(".js_replyed_text"), 1649 | o(".js_replyed_comment_tips") 1650 | )), 1651 | o(".js_emoji_dialog").hide(), 1652 | o(".js_cmt_face").removeClass("comment__face--select"); 1653 | }) 1654 | .on("click", function(t) { 1655 | var e = o(t.target); 1656 | e.hasClass("js_cmt_face") || 1657 | e.parents(".js_cmt_face").length || 1658 | e.hasClass("icon_comment_face") || 1659 | (e.parents(".js_emoji_dialog").length && 1660 | !e.hasClass("js_emojj_close") && 1661 | !e.parents(".js_emojj_close").length) || 1662 | (o(".js_emoji_dialog").hide(), 1663 | o(".js_cmt_face").removeClass("comment__face--select")); 1664 | }) 1665 | .off("click", ".js_cmt_face") 1666 | .on("click", ".js_cmt_face", function() { 1667 | o(this).addClass("comment__face--select"); 1668 | var e = o(this).parents(".comment__input"); 1669 | 0 == o(".js_emoji_dialog", e).length && o(this).after(t), 1670 | o(".js_emoji_dialog", e).css({ 1671 | top: "152px", 1672 | left: e.width() - o(".js_emoji_dialog", e).width() + "px" 1673 | }), 1674 | o(".js_emoji_dialog", e).show(); 1675 | }), 1676 | (m = !0); 1677 | } 1678 | }, 1679 | bindEvents: function() { 1680 | function n(t, e, n) { 1681 | o.trim(t.text()) ? e && e() : n && n(); 1682 | } 1683 | function a(t) { 1684 | var e = []; 1685 | return ( 1686 | o.each(t.children(), function(t, n) { 1687 | var c = o(n).html(); 1688 | o(n).html( 1689 | c 1690 | .replace(/
    /gi, "\\n") 1691 | .replace(/
    /gi, "\\n") 1692 | .replace(/<\/div>/gi, "") 1693 | ), 1694 | e.push(o(n).html()); 1695 | }), 1696 | "" == o.trim(e.join("\\n")) 1697 | ? t 1698 | .html() 1699 | .replace(/
    /gi, "\\n") 1700 | .replace(/
    /gi, "\\n") 1701 | .replace(/<\/div>/gi, "") 1702 | : t.html().split("
    ")[0] + e.join("\\n") 1703 | ); 1704 | } 1705 | function s(t) { 1706 | var e = l.get("container"), 1707 | n = o(e).offset().top, 1708 | c = 1709 | document.body.scrollTop || 1710 | document.documentElement.scrollTop || 1711 | window.pageYOffset, 1712 | i = window.innerHeight; 1713 | !window.commentReportShow && 1714 | (2 * i) / 3 > n - c && 1715 | (t 1716 | ? ((window.commentReportShow = !0), l.pgvClickStat("show")) 1717 | : window.commentReportShowStart || 1718 | setTimeout(function() { 1719 | (window.commentReportShowStart = !0), s(!0); 1720 | }, 1e3)), 1721 | t && (window.commentReportShowStart = !1); 1722 | } 1723 | function m() { 1724 | var t = 100, 1725 | n = o("#report_comment_popup textarea"), 1726 | c = o("#report_comment_popup .popup_report_write__num"), 1727 | i = e.string.getRealLen(n.val()); 1728 | (i = Math.ceil(i / 2)), 1729 | c.html(i + "/" + t), 1730 | i > t 1731 | ? (c.css({ color: "red" }), c.data("over", 1)) 1732 | : (c.css({ color: "" }), c.data("over", 0)); 1733 | } 1734 | var l = this, 1735 | p = l.get("container"); 1736 | o(p) 1737 | .off("click", ".js_grade_score a") 1738 | .on("click", ".js_grade_score a", function() { 1739 | var t = o(this).data("score") || 0, 1740 | e = o(".js_reply_grade"), 1741 | n = e.data("cur"), 1742 | c = 0; 1743 | (c = 20 * t), 1744 | n == t && (c = t = 0), 1745 | e.data("cur", t), 1746 | l.set("score", parseInt(t, 10)), 1747 | c > 100 && (c = 100), 1748 | e.width(c + "%"), 1749 | l.pgvClickStat("grade"); 1750 | }) 1751 | .off("mouseover", ".js_grade_score a") 1752 | .on("mouseover", ".js_grade_score a", function() { 1753 | var t = o(this).data("score") || 0, 1754 | e = o(".js_reply_grade"), 1755 | n = 0; 1756 | (n = 20 * t), n > 100 && (n = 100), e.width(n + "%"); 1757 | }) 1758 | .off("mouseout", ".js_grade_score a") 1759 | .on("mouseout", ".js_grade_score a", function() { 1760 | var t = l.get("score") || 0, 1761 | e = o(".js_reply_grade"), 1762 | n = 0; 1763 | (n = 20 * t), n > 100 && (n = 100), e.width(n + "%"); 1764 | }) 1765 | .off("click", ".js_nick") 1766 | .on("click", ".js_nick", function() { 1767 | l.pgvClickStat("people"), 1768 | MUSIC.util.gotoUser({ 1769 | target: "_blank", 1770 | uin: o(this).data("uin") 1771 | }); 1772 | }) 1773 | .off("click", ".js_approve") 1774 | .on("click", ".js_approve", function() { 1775 | if (l.checkLogin()) { 1776 | var t = o(this), 1777 | e = t.parent(".js_comment_opt").data("commentid"); 1778 | l.set("commentid", e), 1779 | l.approveCmt_sending || 1780 | (l.approveCmt(e, "通过审核" == t.html() ? 4 : 5), 1781 | l.pgvClickStat("approve")); 1782 | } 1783 | }) 1784 | .off("click", ".js_up_comment") 1785 | .on("click", ".js_up_comment", function() { 1786 | if (!l.checkLogin()) return !1; 1787 | var t = o(this), 1788 | e = t.parent(".js_comment_opt").data("commentid"); 1789 | l.set("commentid", e), 1790 | "取消置顶" == t.html() 1791 | ? l.upCmt_sending || 1792 | (l.upCmt(e, "取消置顶" == t.html() ? 2 : 1), 1793 | l.pgvClickStat("up")) 1794 | : l.showConfirm( 1795 | "确认置顶此评论?", 1796 | "置顶后,评论作者将收到通知。", 1797 | "置顶", 1798 | function() { 1799 | l.upCmt_sending || 1800 | (l.upCmt(e, "取消置顶" == t.html() ? 2 : 1), 1801 | l.pgvClickStat("up")); 1802 | }, 1803 | "关闭" 1804 | ); 1805 | }) 1806 | .off("click", ".js_feedback") 1807 | .on("click", ".js_feedback", function() { 1808 | if (!l.checkLogin()) return !1; 1809 | { 1810 | var t = o(this); 1811 | t.data("cmtid"), l.get("commentStyle"); 1812 | } 1813 | l.set("commentid", t.parent(".js_comment_opt").data("commentid")), 1814 | o(".js_cmt_replyed").remove(), 1815 | t 1816 | .closest(".js_cmt_li") 1817 | .find(".js_reply_container") 1818 | .html( 1819 | (function(t) { 1820 | { 1821 | var e, 1822 | n = ""; 1823 | Array.prototype.join; 1824 | } 1825 | return (n += 1826 | '
    \r\n
    \r\n
    \r\n
    \r\n \r\n
    回复@' + 1831 | (null == (e = t.nick) ? "" : _.escape(e)) + 1832 | '
    \r\n \r\n
    \r\n
    剩余300
    \r\n
    \r\n \r\n
    \r\n 回复\r\n 取消\r\n
    \r\n
    '); 1833 | })({ 1834 | nick: t.data("nick"), 1835 | uin: t.data("uin"), 1836 | cmtid: t.data("cmtid"), 1837 | subSource: l.get("subSource") 1838 | }) 1839 | ); 1840 | }) 1841 | .off("click", ".js_cmt_praise") 1842 | .on("click", ".js_cmt_praise", function() { 1843 | if (!l.checkLogin()) return !1; 1844 | var t = o(this); 1845 | l.set("commentid", t.data("commentid")), 1846 | l.praiseCmt_sending || 1847 | (l.praiseCmt(l.get("commentid"), t.hasClass("done") ? 2 : 1), 1848 | l.pgvClickStat("like")); 1849 | }) 1850 | .off("click", ".js_cmt_contribute") 1851 | .on("click", ".js_cmt_contribute", function() { 1852 | if (!l.checkLogin()) return !1; 1853 | var t = o(this); 1854 | if (-1 != t.html().indexOf("已投稿")) 1855 | return i.show("小编已收到你的投稿啦", 3e3, 1), !1; 1856 | var e = t.data("commentid"); 1857 | l.set("commentid", e); 1858 | var n = {}; 1859 | (n.comment = { 1860 | method: "UpdateHotComment", 1861 | module: "GlobalComment.GlobalCommentWriteServer", 1862 | param: { comment_id: e, type: 9, uin: c.getUin() + "" } 1863 | }), 1864 | r.post({ 1865 | url: "//u.y.qq.com/cgi-bin/musicu.fcg", 1866 | data: JSON.stringify(n), 1867 | charset: "utf-8", 1868 | success: function(n) { 1869 | (n = JSON.parse(n)), 1870 | 0 == n.code && 1871 | n.comment && 1872 | 0 == n.comment.code && 1873 | (t.before( 1874 | '已投稿' 1877 | ), 1878 | t.remove()), 1879 | i.show("投稿成功"); 1880 | }, 1881 | error: function() {} 1882 | }); 1883 | }) 1884 | .off("click", ".js_cmt_delete") 1885 | .on("click", ".js_cmt_delete", function() { 1886 | if (!l.checkLogin()) return !1; 1887 | var t = o(this); 1888 | l.showConfirm( 1889 | "确认删除评论? ", 1890 | null, 1891 | "删除", 1892 | function() { 1893 | l.delCmt(t.parent(".js_comment_opt").data("commentid")); 1894 | }, 1895 | "关闭" 1896 | ), 1897 | l.pgvClickStat("delete"); 1898 | }) 1899 | .off("click", ".js_all_good_comments") 1900 | .on("click", ".js_all_good_comments", function() { 1901 | var t = !0; 1902 | l.renderMusicCmt(t), 1903 | o(".js_all_good_comments").hide(), 1904 | l.pgvClickStat( 1905 | 1 == l.get("isMuscritself") 1906 | ? "qqcriticsall" 1907 | : 1 == l.get("type") 1908 | ? "zhihucriticsall" 1909 | : "doubancriticsall" 1910 | ); 1911 | }) 1912 | .off("click", ".js_show_all") 1913 | .on("click", ".js_show_all", function() { 1914 | var t = o(this), 1915 | e = t.data("self"), 1916 | n = t.data("detail"), 1917 | c = {}; 1918 | if (e) { 1919 | if ( 1920 | ((c = o.extend(!0, {}, n)), "string" == typeof c.muscritcontent) 1921 | ) { 1922 | var i = c.muscritcontent.split(/\r\n/g) || []; 1923 | c.muscritcontent = o.extend(!0, [], i); 1924 | } 1925 | l.pgvClickStat("qqcritics"), l.showMusicianDetail(c); 1926 | } else l.pgvClickStat(1 == l.get("type") ? "zhihucritics" : "doubancritics"), (window.location.href = n); 1927 | }) 1928 | .off("keyup propertychange input", ".js_reply_text") 1929 | .on("keyup propertychange input", ".js_reply_text", function() { 1930 | l.showTitleNum(o(".js_reply_text"), o(".js_comment_tips")); 1931 | }) 1932 | .off("keyup propertychange input", ".js_replyed_text") 1933 | .on("keyup propertychange input", ".js_replyed_text", function() { 1934 | l.showTitleNum( 1935 | o(".js_replyed_text"), 1936 | o(".js_replyed_comment_tips") 1937 | ); 1938 | }) 1939 | .off("keyup propertychange input", ".js_comment__textarea") 1940 | .on( 1941 | "keyup propertychange input", 1942 | ".js_comment__textarea", 1943 | function() { 1944 | l.showTitleNum( 1945 | o(".comment__textarea_input", o(this)), 1946 | o(".comment__tips", o(this)) 1947 | ); 1948 | } 1949 | ) 1950 | .on("click", "", function(t) { 1951 | var e = o(t.target), 1952 | c = null; 1953 | e.hasClass("js_comment__textarea") || 1954 | e.parents(".js_comment__textarea").length > 0 1955 | ? ((c = e.hasClass("js_comment__textarea") 1956 | ? e 1957 | : e.parents(".js_comment__textarea")), 1958 | o(".comment__textarea_default", c).hide(), 1959 | o(".comment__textarea_input", c).show(), 1960 | o(".comment__textarea_input", c).focus(), 1961 | l.showTitleNum( 1962 | o(".comment__textarea_input", c), 1963 | o(".comment__tips", c) 1964 | )) 1965 | : n( 1966 | o(".comment__textarea_input"), 1967 | function() {}, 1968 | function() { 1969 | o(".comment__textarea_default").show(), 1970 | o(".comment__textarea_input").hide(); 1971 | } 1972 | ); 1973 | }) 1974 | .on("blur", ".js_reply_text", function() { 1975 | n( 1976 | o("#reply_text"), 1977 | function() {}, 1978 | function() { 1979 | o("#reply_text_blur").show(), o("#reply_text").hide(); 1980 | } 1981 | ); 1982 | }) 1983 | .on("blur", ".js_replyed_text", function() { 1984 | setTimeout(function() { 1985 | n( 1986 | o("#replyed_text"), 1987 | function() {}, 1988 | function() { 1989 | o("#replyed_text_blur").show(), o("#replyed_text").hide(); 1990 | } 1991 | ); 1992 | }, 100); 1993 | }) 1994 | .off("click", ".js_send_reply") 1995 | .on("click", ".js_send_reply", function() { 1996 | if (!l.checkLogin()) return !1; 1997 | var t = o("#reply_text"); 1998 | t.html(a(t)); 1999 | var e = t.text(), 2000 | n = t, 2001 | c = (o(".js_reply_text"), 2002 | { content: e || "", score: l.get("score") }); 2003 | l.sendCmt( 2004 | c, 2005 | function() { 2006 | n.html(""), 2007 | o("#reply_text_blur").show(), 2008 | o("#reply_text").hide(); 2009 | }, 2010 | function() { 2011 | t.html(t.html().replace(/\\n/gi, "

    ")); 2012 | } 2013 | ), 2014 | l.pgvClickStat("comment"); 2015 | }) 2016 | .off("click", ".js_send_replyed") 2017 | .on("click", ".js_send_replyed", function() { 2018 | if (!l.checkLogin()) return !1; 2019 | var t = o("#replyed_text"), 2020 | e = t.text(); 2021 | t.html(a(t)); 2022 | var n = t, 2023 | c = n.parents(".js_cmt_li"), 2024 | i = { 2025 | content: e || "", 2026 | commentid: t.parents(".js_cmt_replyed").data("cmtid"), 2027 | score: l.get("score") 2028 | }; 2029 | i.middlecommentcontent = []; 2030 | var r = c.find(".js_nick_only").html(), 2031 | s = c.find(".js_nick_only").data("uin"), 2032 | m = { 2033 | replynick: !0, 2034 | replyuin: !0, 2035 | replyednick: r, 2036 | replyeduin: s, 2037 | subcommentcontent: i.content 2038 | }; 2039 | if ( 2040 | (i.middlecommentcontent.push(m), 2041 | c.find(".comment__text--history").length > 0) 2042 | ) { 2043 | i.rootcommentcontent = c.find(".comment__text--history").html(); 2044 | var _ = c.find(".js_middle .js_nick"); 2045 | (m = { replynick: r, replyuin: s }), 2046 | _.each(function(t, e) { 2047 | m.replynick && 2048 | m.replyednick && 2049 | (i.middlecommentcontent.push(m), (m = {})), 2050 | o(e).hasClass("js_replyed_nick") 2051 | ? ((m.replyednick = o(this).html()), 2052 | (m.replyeduin = o(this).data("uin")), 2053 | m.subcommentcontent || 2054 | (m.subcommentcontent = o(this) 2055 | .next(".js_subcomment") 2056 | .html())) 2057 | : o(e).hasClass("js_reply_nick") && 2058 | ((m.replynick = o(this).html()), 2059 | (m.replyuin = o(this).data("uin"))), 2060 | m.replynick && 2061 | m.replyednick && 2062 | (i.middlecommentcontent.push(m), (m = {})); 2063 | }); 2064 | } else i.rootcommentcontent = c.find(".js_hot_text").html(); 2065 | l.sendCmt( 2066 | i, 2067 | function() { 2068 | t.html(""), 2069 | setTimeout(function() { 2070 | o("#replyed_text_blur").show(), 2071 | o("#replyed_text").hide(), 2072 | n.closest(".js_cmt_replyed").hide(); 2073 | }, 350); 2074 | }, 2075 | function() { 2076 | $text.html($text.html().replace(/\\n/gi, "

    ")); 2077 | } 2078 | ), 2079 | l.pgvClickStat("reply"); 2080 | }) 2081 | .off("mouseover", ".js_cmt_li") 2082 | .on("mouseover", ".js_cmt_li", function() { 2083 | var t = o(this); 2084 | t.addClass("hover"); 2085 | }) 2086 | .off("mouseout", ".js_cmt_li") 2087 | .on("mouseout", ".js_cmt_li", function() { 2088 | var t = o(this); 2089 | t.removeClass("hover"); 2090 | }) 2091 | .off("click", ".js_send_cancel") 2092 | .on("click", ".js_send_cancel", function() { 2093 | var t = o(this); 2094 | t.closest(".js_cmt_replyed").remove(), 2095 | l.pgvClickStat("replycancle"); 2096 | }) 2097 | .off("click", ".js_order_type") 2098 | .on("click", ".js_order_type", function() { 2099 | var t = o(this); 2100 | o(".js_order_type").removeClass("c_tx_current"), 2101 | t.addClass("c_tx_current"); 2102 | var e = t.data("type"); 2103 | l.setPager(e, 0, !1); 2104 | }) 2105 | .off("click", ".js_get_more_hot") 2106 | .on("click", ".js_get_more_hot", function() { 2107 | var t = parseInt(o(this).data("loading")); 2108 | return 1 == t 2109 | ? !1 2110 | : (o(this).data("loading", 1), l.getMoreHotComment(), void 0); 2111 | }), 2112 | o(document) 2113 | .off("click", ".js_rule_btn") 2114 | .on("click", ".js_rule_btn", function() { 2115 | var t = window.open( 2116 | "https://c.y.qq.com/node/m/client/music_headline/index.html?_hidehd=1&_button=2&zid=120574&mmkey=", 2117 | "_blank" 2118 | ); 2119 | t && (t.opener = null); 2120 | }) 2121 | .off("click", ".js_close_comment_rule") 2122 | .on("click", ".js_close_comment_rule", function() { 2123 | o("#comment_popup").hide(); 2124 | }) 2125 | .on("click", "", function(t) { 2126 | var e = o(t.target); 2127 | e.hasClass("js_rule_btn") || o("#comment_popup").hide(), 2128 | e.hasClass("js_report_comment") || 2129 | 0 != e.parents("#report_comment_popup").length || 2130 | o("#report_comment_popup").hide(); 2131 | }) 2132 | .off("click", ".js_report_comment") 2133 | .on("click", ".js_report_comment", function(e) { 2134 | var n = o(this), 2135 | c = n.parent(".js_comment_opt").data("commentid"); 2136 | l.set("commentid", c), 2137 | 0 == o("#report_comment_popup").length && 2138 | (o("body").append( 2139 | (function() { 2140 | { 2141 | var t = ""; 2142 | Array.prototype.join; 2143 | } 2144 | return (t += 2145 | '\r\n \r\n'); 2146 | })({ cmtid: c }) 2147 | ), 2148 | o("#report_comment_popup textarea").on( 2149 | "keyup input propertychange", 2150 | function() { 2151 | m(); 2152 | } 2153 | )), 2154 | t.load( 2155 | "//y.gtimg.cn/mediastyle/macmusic_v4/popup.css?max_age=25920000&v=20171115", 2156 | function() { 2157 | o("#report_comment_popup").show(); 2158 | var t = 2159 | e.clientY - 2160 | o("#report_comment_popup .c_popup").height() / 2 + 2161 | document.body.scrollTop, 2162 | n = 2163 | window.innerWidth / 2 - 2164 | o("#report_comment_popup .c_popup").width() / 2; 2165 | o("#report_comment_popup .c_popup").css({ 2166 | left: (n > 0 ? n : 0) + "px", 2167 | top: (t > 0 ? t : 0) + "px" 2168 | }); 2169 | } 2170 | ); 2171 | }) 2172 | .off("click", "#report_comment_popup input") 2173 | .on("click", "#report_comment_popup input", function() { 2174 | o(this) 2175 | .parents(".popup_report_list__item") 2176 | .hasClass("check") || 2177 | (o( 2178 | "#report_comment_popup .popup_report_list__item" 2179 | ).removeClass("check"), 2180 | o(this) 2181 | .parents(".popup_report_list__item") 2182 | .addClass("check")); 2183 | }) 2184 | .off("click", ".js_close_report_comment_popup") 2185 | .on("click", ".js_close_report_comment_popup", function() { 2186 | o("#report_comment_popup").hide(), 2187 | o("#report_comment_popup textarea").val(""), 2188 | o("#report_comment_popup .popup_report_list__item").removeClass( 2189 | "check" 2190 | ), 2191 | o("#item6") 2192 | .parents(".popup_report_list__item") 2193 | .addClass("check"); 2194 | }) 2195 | .off("click", ".js_submit_report_comment_popup") 2196 | .on("click", ".js_submit_report_comment_popup", function() { 2197 | var t = o.trim(o("#report_comment_popup .check").text()), 2198 | n = o.trim(o("#report_comment_popup textarea").val()); 2199 | return "其它" != t || n 2200 | ? 1 == 2201 | o("#report_comment_popup .popup_report_write__num").data( 2202 | "over" 2203 | ) 2204 | ? (e.popup.show("字数超过100字限制", 3e3, 1), !1) 2205 | : (o("#report_comment_popup").hide(), 2206 | o("#report_comment_popup textarea").val(""), 2207 | o( 2208 | "#report_comment_popup .popup_report_list__item" 2209 | ).removeClass("check"), 2210 | o("#item6") 2211 | .parents(".popup_report_list__item") 2212 | .addClass("check"), 2213 | m(), 2214 | l.reportCmt(l.get("commentid"), t, n), 2215 | void 0) 2216 | : (e.popup.show("请正确填写举报内容!", 3e3, 1), !1); 2217 | }) 2218 | .on("scroll", function() { 2219 | s(); 2220 | }), 2221 | s(); 2222 | }, 2223 | Statics: { 2224 | init: function(t) { 2225 | try { 2226 | var e = o(t.container).data("comment"); 2227 | e = null; 2228 | } catch (n) {} 2229 | return new d(t); 2230 | } 2231 | } 2232 | }); 2233 | return d; 2234 | }); 2235 | -------------------------------------------------------------------------------- /test/server-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "http://193.112.241.232:5050/search/song": [ 3 | { 4 | "key": "周杰伦", 5 | "vendor": "qq", 6 | "page": 1, 7 | "limit": 20 8 | }, 9 | { "key": "林俊杰", "vendor": "netease", "page": 1, "limit": 20 } 10 | ], 11 | "http://193.112.241.232:5050/get/song": [ 12 | { "id": 108138, "vendor": "netease" } 13 | ], 14 | "http://193.112.241.232:5050/get/comment": [ 15 | { 16 | "id": 108138, 17 | "vendor": "netease", 18 | "page": 1, 19 | "limit": 20 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | // const musicApi = require("./../src/index"); 2 | var musicApi = require("./../dist/music-api"); 3 | console.log(musicApi.searchSong); 4 | 5 | !(async function() { 6 | let res = await musicApi.searchSong({ 7 | vendor: "xiami", 8 | key: "林俊杰", 9 | page: 1, 10 | limit: 1 11 | }); 12 | if (res.success === false) return; 13 | let data = res.results; 14 | for (let music of data) { 15 | if (music.needPay) { 16 | console.log(music); 17 | continue; 18 | } 19 | // let info = await musicApi.getSong({ 20 | // id: music.id, 21 | // vendor: "netease" 22 | // }); 23 | // console.log({ ...music, ...info.results }); 24 | console.log(music.id); 25 | let info = await musicApi.getComment({ 26 | id: music.id, 27 | vendor: "xiami", 28 | page: 1, 29 | limit: 1 30 | }); 31 | console.log(info); 32 | } 33 | })(); 34 | -------------------------------------------------------------------------------- /test/xiami.html: -------------------------------------------------------------------------------- 1 |
      2 |
    • 3 |
      4 |

      5 | 6 | 小英英 8 |

      9 |
      10 | 11 | 小英英(过去进行时) 12 | 2018-08-03 14:36 13 | 14 | 15 | 赞(1) 16 | 17 | 18 | 弱(0) 19 | 20 | 21 |
      22 |
      23 |
      虾米音乐 24 |
      25 |
      26 | 来自android客户端 27 |
      28 |
      29 |
      30 |
    • 31 |
    • 32 |
      33 |

      34 | 35 | 迷津9311 37 |

      38 |
      39 | 40 | 迷津9311(谢谢) 41 | 2018-08-03 13:49 42 | 43 | 44 | 赞(0) 45 | 46 | 47 | 弱(0) 48 | 49 | 50 |
      51 |
      52 |
      20016 53 |
      54 |
      55 | 来自iPhone客户端 56 |
      57 |
      58 |
      59 |
    • 60 |
    • 61 |
      62 |

      63 | 64 | wangkui5063 66 |

      67 |
      68 | 69 | wangkui5063 70 | 71 | 2018-08-03 13:25 72 | 73 | 74 | 赞(0) 75 | 76 | 77 | 弱(0) 78 | 79 | 80 |
      81 |
      82 |
      。。。 83 |
      84 |
      85 | 来自android客户端 86 |
      87 |
      88 |
      89 |
    • 90 |
    • 91 |
      92 |

      93 | 94 | 驴的丶 96 |

      97 |
      98 | 99 | 驴的丶 100 | 101 | 2018-08-03 08:19 102 | 103 | 104 | 赞(0) 105 | 106 | 107 | 弱(0) 108 | 109 | 110 |
      111 |
      112 |
      。 113 |
      114 |
      115 | 来自android客户端 116 |
      117 |
      118 |
      119 |
    • 120 |
    • 121 |
      122 |

      123 | 124 | 释然欧巴、 126 |

      127 |
      128 | 129 | 释然欧巴、(记得微笑) 130 | 2018-08-03 07:41 131 | 132 | 133 | 赞(0) 134 | 135 | 136 | 弱(0) 137 | 138 | 139 |
      140 |
      141 |
      早安 142 | 143 |
      144 |
      145 | 来自iPhone客户端 146 |
      147 |
      148 |
      149 |
    • 150 |
    • 151 |
      152 |

      153 | 154 | 彼岸 156 |

      157 |
      158 | 159 | 彼岸(花开无叶,叶生无花) 160 | 2018-08-03 06:34 161 | 162 | 163 | 赞(0) 164 | 165 | 166 | 弱(0) 167 | 168 | 169 |
      170 |
      171 |
      吗!~我有情人网~av挂特图饿个月啊 172 |
      173 |
      174 | 来自android客户端 175 |
      176 |
      177 |
      178 |
    • 179 |
    • 180 |
      181 |

      182 | 183 | mingyun516 185 |

      186 |
      187 | 188 | mingyun516 189 | 190 | 2018-08-03 03:40 191 | 192 | 193 | 赞(0) 194 | 195 | 196 | 弱(0) 197 | 198 | 199 |
      200 |
      201 |
      可惜没去成现场 202 |
      203 |
      204 | 来自android客户端 205 |
      206 |
      207 |
      208 |
    • 209 |
    • 210 |
      211 |

      212 | 213 | 方方 215 |

      216 |
      217 | 218 | 方方 219 | 220 | 2018-08-03 00:34 221 | 222 | 223 | 赞(0) 224 | 225 | 226 | 弱(0) 227 | 228 | 229 |
      230 |
      231 |
      以前的自己没有经历过感情的纠结,只是单纯的快乐,现在遇到自己 自己喜欢的人,所以的歌才听出自己的回忆,可能我还没有学会怎样爱别人,希望今天开始,好好珍惜自己的那个他 232 | 233 | 234 |
      235 |
      236 | 来自android客户端 237 |
      238 |
      239 |
      240 |
    • 241 |
    • 242 |
      243 |

      244 | 245 | 戚桦烨❤ 247 |

      248 |
      249 | 250 | 戚桦烨❤ 251 | 252 | 2018-08-02 23:10 253 | 254 | 255 | 赞(1) 256 | 257 | 258 | 弱(0) 259 | 260 | 261 |
      262 |
      263 |
      第一万零八条评论 264 |
      我的童年就是周杰伦了 265 |
      266 |
      267 | 来自android客户端 268 |
      269 |
      270 |
      271 |
    • 272 |
    • 273 |
      274 |

      275 | 276 | 婆娑 278 |

      279 |
      280 | 281 | 婆娑(我虽外向但还是孤独) 282 | 2018-08-02 21:49 283 | 284 | 285 | 赞(1) 286 | 287 | 288 | 弱(0) 289 | 290 | 291 |
      292 |
      293 |
      好听 294 |
      295 |
      296 | 来自iPhone客户端 297 |
      298 |
      299 |
      300 |
    • 301 |
    302 |
    303 | 上一页 304 | 1 305 | 2 306 | 3 307 | 4 308 | 5 309 | 6 310 | 7 311 | 8 312 | 313 | 下一页 314 | (第2页, 共10000条) 315 |
    -------------------------------------------------------------------------------- /test/xiami.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const cheerio = require("cheerio"); 5 | 6 | let html = fs.readFileSync(path.resolve(".", "xiami.html"), "utf-8"); 7 | 8 | let $ = cheerio.load(html); 9 | 10 | let liArr = $("ul li"), 11 | results = []; 12 | 13 | for (let i = 0; i < liArr.length; ++i) { 14 | let li = $(liArr[i]), 15 | id = li.attr("id"); 16 | results.push({ 17 | time: li.find(".info span.time").text() + ":00", 18 | content: li 19 | .find("#brief_" + id) 20 | .clone() 21 | .children() 22 | .remove() 23 | .end() 24 | .text() 25 | .replace(/(\s*$)/g, ""), 26 | user: { 27 | headImgUrl: li.find("img").attr("src"), 28 | nickname: li.find("img").attr("alt") 29 | } 30 | }); 31 | console.log(results); 32 | break; 33 | } 34 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | 4 | const fs = require("fs"); 5 | 6 | const nodeModules = {}; 7 | fs.readdirSync("node_modules") 8 | .filter(function(x) { 9 | return [".bin"].indexOf(x) === -1; 10 | }) 11 | .forEach(function(mod) { 12 | nodeModules[mod] = "commonjs " + mod; 13 | }); 14 | 15 | module.exports = { 16 | entry: "./src/index.js", 17 | output: { 18 | publicPath: __dirname + "/dist/", 19 | path: path.resolve(__dirname, "dist"), 20 | filename: "music-api-next.js", 21 | chunkFilename: "[name].chunk.js", 22 | libraryTarget: "commonjs" 23 | }, 24 | target: "node" 25 | }; 26 | -------------------------------------------------------------------------------- /zh-README.md: -------------------------------------------------------------------------------- 1 | > 一个可以快速从 QQ 音乐、虾米音乐和网易音乐获得歌曲搜索结果、歌曲链接信息和歌曲评论的第三方库。 2 | 3 | 如果想了解更多关于如何使用`music-api-next`的知识,请去[库的开发者的博客](https://godbmw.com/)进行交流。如果需要提出 issues,欢迎来[仓库地址](https://github.com/dongyuanxin/music-api-next) 4 | 5 | [**DOCS**](https://godbmw.com/passage/62) 6 | 7 | [**中文文档**](https://godbmw.com/passage/63) 8 | 9 | ## 安装 10 | 11 | ``` 12 | npm install music-api-next --save 13 | ``` 14 | 15 | 网速不好的中国用户,请使用`cnpm`安装: 16 | 17 | ``` 18 | cnpm install music-api-next --save 19 | ``` 20 | 21 | ## 快速开始 22 | 23 | ```javascript 24 | const musicAPI = require("music-api-next"); 25 | 26 | // 搜索接口: 返回指定关键词的搜索信息 27 | musicAPI 28 | .searchSong({ 29 | key: "周杰伦", 30 | page: 1, 31 | limit: 10, 32 | vendor: "qq" 33 | }) 34 | .then(songs => console.log(songs)) 35 | .catch(error => console.log(error.message)); 36 | 37 | // 歌曲信息接口: 返回指定歌曲的信息 38 | musicAPI 39 | .getSong({ 40 | id: "003OUlho2HcRHC", 41 | vendor: "qq" 42 | }) 43 | .then(meta => console.log(meta)) 44 | .catch(error => console.log(error.message)); 45 | 46 | // 评论接口: 返回指定歌曲的评论 47 | musicAPI 48 | .getComment({ 49 | id: "003OUlho2HcRHC", 50 | page: 1, 51 | limit: 20, 52 | vendor: "qq" 53 | }) 54 | .then(comments => console.log(comments)) 55 | .catch(error => console.log(error.message)); 56 | ``` 57 | 58 | ## 本地服务器 59 | 60 | ```shell 61 | git clone git@github.com:dongyuanxin/music-api-next.git 62 | cd music-api-next 63 | npm install 64 | // 启动服务器的监听端口: 5050 65 | node server.js 66 | ``` 67 | 68 | 启动服务器后,你可以直接在浏览器中访问 url 来查看结果。 69 | 70 | 例如: 71 | 72 | - Search API: `http://localhost:5050/search/song?key=周杰伦&page=1&limit=10&vendor=qq` 73 | - Song API: `http://localhost:5050/get/song?id=003OUlho2HcRHC&vendor=qq` 74 | - Comment API: `http://localhost:5050/get/comment?id=003OUlho2HcRHC&page=1&limit=10&vendor=qq` 75 | 76 | ## 配合`webpack` 77 | 78 | 首先,配合`webpack`进行打包。 79 | 80 | ```shell 81 | git clone git@github.com:dongyuanxin/music-api-next.git 82 | cd music-api-next 83 | npm install 84 | // use webpack to package program 85 | // pacakged file named 'music-api-next.js' is placed in ./dist/ 86 | webpack 87 | ``` 88 | 89 | 然后,你就可以直接移动打包好的`music-api-next.js`到工作目录。引用方法如下: 90 | 91 | ```javascript 92 | const musicAPI = require("./music-api-next"); 93 | 94 | // ... 95 | ``` 96 | 97 | ## API 98 | 99 | - `musicAPI.searchSong(query)`: 100 | 101 | ``` 102 | query: { 103 | key: String, 104 | page: Number, 105 | limit: Number, 106 | vendor: one of ['netease', 'xiami', 'qq'] 107 | } 108 | ``` 109 | 110 | - `musicAPI.getSong(query)`: 111 | 112 | ``` 113 | query: { 114 | id: String or Number, 115 | vendor: one of ['netease', 'xiami', 'qq'] 116 | } 117 | ``` 118 | 119 | - `musicAPI.getComment(query)`: 120 | 121 | ``` 122 | query: { 123 | id: String or Number, 124 | page: Number, 125 | limit: Number, 126 | vendor: one of ['netease', 'xiami', 'qq'] 127 | } 128 | ``` 129 | 130 | ## 警告 131 | 132 | 1. **请不要用于商业用途** 133 | 2. **目前版本只能运行于 NodeJS** 134 | 3. **请友好地调用 API,不要对这些音乐平台造成过大压力** 135 | 136 | ## 致谢 137 | 138 | 项目代码参考了以下 2 个开源项目。并且做了修复、改进和增加(在 NodeJS 中)。 139 | 140 | 1. [listen1_chrome_extension](https://github.com/listen1/listen1_chrome_extension): 由于接收到了腾讯的律师函,可能会在 2018 年底停止维护。 141 | 2. [musicAPI](https://github.com/LIU9293/musicAPI): 停止维护 1 年多了,很多 API 已经失效。 142 | --------------------------------------------------------------------------------