Instantly share code, notes, and snippets.
Created
August 3, 2017 10:20
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save wxt2005/cd5484b443a694d8488301b797de8e56 to your computer and use it in GitHub Desktop.
NetEase Music Like Button
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| class NetEaseMusicFucker { | |
| constructor() { | |
| this.emj = { | |
| 色: "00e0b", | |
| 流感: "509f6", | |
| 这边: "259df", | |
| 弱: "8642d", | |
| 嘴唇: "bc356", | |
| 亲: "62901", | |
| 开心: "477df", | |
| 呲牙: "22677", | |
| 憨笑: "ec152", | |
| 猫: "b5ff6", | |
| 皱眉: "8ace6", | |
| 幽灵: "15bb7", | |
| 蛋糕: "b7251", | |
| 发怒: "52b3a", | |
| 大哭: "b17a8", | |
| 兔子: "76aea", | |
| 星星: "8a5aa", | |
| 钟情: "76d2e", | |
| 牵手: "41762", | |
| 公鸡: "9ec4e", | |
| 爱意: "e341f", | |
| 禁止: "56135", | |
| 狗: "fccf6", | |
| 亲亲: "95280", | |
| 叉: "104e0", | |
| 礼物: "312ec", | |
| 晕: "bda92", | |
| 呆: "557c9", | |
| 生病: "38701", | |
| 钻石: "14af6", | |
| 拜: "c9d05", | |
| 怒: "c4f7f", | |
| 示爱: "0c368", | |
| 汗: "5b7a4", | |
| 小鸡: "6bee2", | |
| 痛苦: "55932", | |
| 撇嘴: "575cc", | |
| 惶恐: "e10b4", | |
| 口罩: "24d81", | |
| 吐舌: "3cfe4", | |
| 心碎: "875d3", | |
| 生气: "e8204", | |
| 可爱: "7b97d", | |
| 鬼脸: "def52", | |
| 跳舞: "741d5", | |
| 男孩: "46b8e", | |
| 奸笑: "289dc", | |
| 猪: "6935b", | |
| 圈: "3ece0", | |
| 便便: "462db", | |
| 外星: "0a22b", | |
| 圣诞: "8e7", | |
| 流泪: "01000", | |
| 强: "1", | |
| 爱心: "0CoJU", | |
| 女孩: "m6Qyw", | |
| 惊恐: "8W8ju", | |
| 大笑: "d" | |
| }; | |
| this.emjStr = [ | |
| ["流泪", "强"], | |
| [ | |
| "色", | |
| "流感", | |
| "这边", | |
| "弱", | |
| "嘴唇", | |
| "亲", | |
| "开心", | |
| "呲牙", | |
| "憨笑", | |
| "猫", | |
| "皱眉", | |
| "幽灵", | |
| "蛋糕", | |
| "发怒", | |
| "大哭", | |
| "兔子", | |
| "星星", | |
| "钟情", | |
| "牵手", | |
| "公鸡", | |
| "爱意", | |
| "禁止", | |
| "狗", | |
| "亲亲", | |
| "叉", | |
| "礼物", | |
| "晕", | |
| "呆", | |
| "生病", | |
| "钻石", | |
| "拜", | |
| "怒", | |
| "示爱", | |
| "汗", | |
| "小鸡", | |
| "痛苦", | |
| "撇嘴", | |
| "惶恐", | |
| "口罩", | |
| "吐舌", | |
| "心碎", | |
| "生气", | |
| "可爱", | |
| "鬼脸", | |
| "跳舞", | |
| "男孩", | |
| "奸笑", | |
| "猪", | |
| "圈", | |
| "便便", | |
| "外星", | |
| "圣诞" | |
| ], | |
| ["爱心", "女孩", "惊恐", "大笑"] | |
| ]; | |
| this.favListId = null; | |
| this.currentPlayingId = null; | |
| this.favedTracks = []; | |
| this.favButton = null; | |
| this.playbar = null; | |
| this.playbarObserver = null; | |
| } | |
| /** | |
| * Initialize | |
| * @return {Promise} | |
| */ | |
| init() { | |
| this.playbar = document.querySelector('.m-playbar'); | |
| this.syncCurrentPlayingId(); | |
| this.listenPlaybar(() => { | |
| this.syncCurrentPlayingId(); | |
| this.syncFavButton(); | |
| }); | |
| return Promise.resolve() | |
| .then(() => this.getPlayList()) | |
| .then(playlist => playlist.find(item => item.specialType === 5)) | |
| .then(({ id }) => (this.favListId = id)) | |
| .then(() => this.getFavedTracks()) | |
| .then(tracks => this.favedTracks = tracks) | |
| .then(() => this.injectFavButton()); | |
| } | |
| /** | |
| * sync current playing id | |
| * @return {Number} track id | |
| */ | |
| syncCurrentPlayingId() { | |
| this.currentPlayingId = this.getCurrentPlayingId(); | |
| return this.currentPlayingId; | |
| } | |
| /** | |
| * Listen for mutaion of player | |
| */ | |
| listenPlaybar(callback) { | |
| const target = this.playbar.querySelector('.words'); | |
| const observer = new MutationObserver(() => callback()); | |
| const config = { attributes: true, childList: true, characterData: true, subtree: true }; | |
| observer.observe(target, config); | |
| this.playbarObserver = observer; | |
| } | |
| /** | |
| * Conver emoji string array to code string | |
| * @param {Array} [emjArr=[]] emoji string array | |
| * @return {String} code string | |
| */ | |
| convertEmjStr(emjArr = []) { | |
| if (!Array.isArray(emjArr)) { | |
| throw new TypeError( | |
| `emjArr should be an Array instead of ${typeof emjArr}` | |
| ); | |
| } | |
| return emjArr.map(emj => this.emj[emj]).join(""); | |
| } | |
| /** | |
| * Get csrf token from cookies | |
| * @return {String} csrf token | |
| */ | |
| getCsrfToken() { | |
| const REGEXP = /__csrf=(.+?)(?:;|$)/; | |
| const result = REGEXP.exec(document.cookie); | |
| if (!result || !result[1]) { | |
| throw new Error("Get CSRF Token from cookies failed!"); | |
| } | |
| return result[1]; | |
| } | |
| /** | |
| * Encode request data | |
| * @param {Any} data data | |
| * @return {Object} encoded data as object | |
| */ | |
| encodeRequestData(data) { | |
| if (typeof window.asrsea !== "function") { | |
| throw new Error("window.asrsea is not exist! we're all gonna die!"); | |
| } | |
| const encodeParams = this.emjStr.map(this.convertEmjStr.bind(this)); | |
| return window.asrsea(data, ...encodeParams); | |
| } | |
| /** | |
| * Request method | |
| * @param {String} url request url | |
| * @param {Object} [data={}] request data | |
| * @return {Promise} | |
| */ | |
| request({ url, data = {} } = {}) { | |
| if (typeof url !== "string") { | |
| throw new TypeError(`Url should be string, instead got ${typeof url}`); | |
| } | |
| if (!url) { | |
| throw new TypeError("Url should not be empty"); | |
| } | |
| const csrfToken = this.getCsrfToken(); | |
| data.csrf_token = csrfToken; | |
| // console.log(data); | |
| const encodedData = this.encodeRequestData(JSON.stringify(data)); | |
| const myHeaders = new Headers(); | |
| myHeaders.append("Content-Type", "application/x-www-form-urlencoded"); | |
| url += (url.indexOf("?") === -1 ? "?" : "&") + `csrf_token=${csrfToken}`; | |
| const request = new Request(url, { | |
| method: "POST", | |
| body: `params=${encodeURIComponent( | |
| encodedData.encText | |
| )}&encSecKey=${encodeURIComponent(encodedData.encSecKey)}`, | |
| headers: myHeaders, | |
| credentials: "same-origin" | |
| }); | |
| return fetch(request); | |
| } | |
| /** | |
| * Add fav | |
| * @param {Number} trackId track id | |
| * @param {String} operation "del" or "add" | |
| */ | |
| manipulateFav(trackId, operation) { | |
| if (!this.favListId) { | |
| throw new Error("Fav list id is not provied"); | |
| } | |
| if (operation !== 'add' && operation !== 'del') { | |
| throw new TypeError('Operation should be "add" or "del"'); | |
| } | |
| const url = "http://music.163.com/weapi/playlist/manipulate/tracks"; | |
| const data = { | |
| op: operation, | |
| pid: `${this.favListId}`, | |
| trackIds: `[${trackId}]`, | |
| tracks: "[object Object]" | |
| }; | |
| return this.request({ | |
| url, | |
| data | |
| }) | |
| .then(response => response.json()) | |
| .then(json => { | |
| if (operation === 'add') { | |
| this.favedTracks.unshift(trackId); | |
| } else { | |
| this.favedTracks = this.favedTracks.filter(id => id !== trackId); | |
| } | |
| return json; | |
| }); | |
| } | |
| /** | |
| * Manipulate fav for current playing track | |
| */ | |
| manipulateFavForCurrentTrack(operation) { | |
| if (!this.currentPlayingId) { | |
| throw new Error("Cannot found song id"); | |
| } | |
| if (operation !== 'add' && operation !== 'del') { | |
| throw new TypeError('Operation should be "add" or "del"'); | |
| } | |
| const manipulateText = (operation === 'add') ? '添加' : '取消'; | |
| return this.manipulateFav(this.currentPlayingId, operation).then(({ code }) => { | |
| if (code !== 200) { | |
| console.info(`${manipulateText}收藏失败`); | |
| } else { | |
| console.info(`${manipulateText}收藏成功`); | |
| } | |
| }); | |
| } | |
| /** | |
| * Get current playing id | |
| * @return {Number} track id | |
| */ | |
| getCurrentPlayingId() { | |
| const ele = this.playbar.querySelector(".play .name"); | |
| if (!ele || ele.tagName.toLowerCase() !== "a") { | |
| return null; | |
| } | |
| const REGEXP = /id=(\d+?)(?:&|$)/; | |
| const result = REGEXP.exec(ele); | |
| if (!result || !result[1]) { | |
| return null; | |
| } | |
| return parseInt(result[1], 10); | |
| } | |
| /** | |
| * Get playlist for user | |
| * @return {Promise} | |
| */ | |
| getPlayList() { | |
| const url = "http://music.163.com/weapi/user/playlist"; | |
| const data = { | |
| limit: "1001", | |
| offset: "0", | |
| uid: `${window.GAccount.id}` | |
| }; | |
| return this.request({ | |
| url, | |
| data | |
| }) | |
| .then(response => response.json()) | |
| .then(({ code, playlist }) => { | |
| if (code !== 200) { | |
| throw new Error("Get play list failed"); | |
| } | |
| return playlist; | |
| }); | |
| } | |
| /** | |
| * Get faved track ids | |
| * @return {Promise} | |
| */ | |
| getFavedTracks() { | |
| if (!this.favListId) { | |
| throw new Error("Fav list id is not provied"); | |
| } | |
| const url = 'http://music.163.com/weapi/v3/playlist/detail'; | |
| const data = { | |
| id: `${this.favListId}`, | |
| limit: '1000', | |
| n: '1000', | |
| offset: '0', | |
| total: 'true' | |
| }; | |
| return this.request({ | |
| url, | |
| data | |
| }) | |
| .then(response => response.json()) | |
| .then(({ code, playlist }) => { | |
| if (code !== 200) { | |
| throw new Error("Get fav track failed"); | |
| } | |
| return playlist.trackIds.map(track => track.id); | |
| }); | |
| } | |
| /** | |
| * Add fav button to DOM | |
| */ | |
| injectFavButton() { | |
| const container = this.playbar.querySelector(".words"); | |
| const button = document.createElement("a"); | |
| const isFaved = this.isTrackFaved(this.currentPlayingId); | |
| const buttonText = isFaved ? '已喜欢' : '喜欢'; | |
| button.innerText = buttonText; | |
| button.setAttribute("href", "#"); | |
| Object.assign(button.style, { | |
| color: "#fff", | |
| position: "absolute", | |
| right: "32px", | |
| top: "0", | |
| lineHeight: "28px" | |
| }); | |
| button.addEventListener("click", event => { | |
| event.preventDefault(); | |
| const isFaved = this.isTrackFaved(this.currentPlayingId); | |
| this.manipulateFavForCurrentTrack(isFaved ? 'del' : 'add') | |
| .then(() => this.syncFavButton()); | |
| }); | |
| this.favButton = button; | |
| container.insertAdjacentElement("afterend", this.favButton); | |
| } | |
| /** | |
| * Sync fav button status | |
| */ | |
| syncFavButton() { | |
| if (!this.favButton) { | |
| this.injectFavButton(); | |
| } | |
| const isFaved = this.isTrackFaved(this.currentPlayingId); | |
| const buttonText = isFaved ? '已喜欢' : '喜欢'; | |
| this.favButton.innerText = buttonText; | |
| } | |
| /** | |
| * Is track faved | |
| * @param {Number} trackId track id | |
| * @return {Boolean} | |
| */ | |
| isTrackFaved(trackId) { | |
| if (!trackId) { | |
| return false; | |
| } | |
| return !!this.favedTracks.find(id => trackId == id); | |
| } | |
| } | |
| const instance = new NetEaseMusicFucker(); | |
| // init | |
| instance.init(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment