// ==UserScript==
// @name CloudMusic - Show all songs on the playlist
// @name:zh-CN 网易云音乐 - 显示歌单中的所有歌曲
// @namespace https://gf.qytechs.cn/zh-CN/users/193133-pana
// @homepage https://www.sailboatweb.com
// @version 1.0.1
// @description Show all songs in the playlist
// @description:zh-CN 显示歌单中的所有歌曲
// @author pana
// @license GNU General Public License v3.0 or later
// @match *://music.163.com/*
// @connect api.imjad.cn
// @connect music.163.com
// @grant GM_xmlhttpRequest
// ==/UserScript==
(function() {
'use strict';
class Basic_Obj {
constructor(id, name) {
this.id = id;
this.name = name;
}
getId() {
return this.id;
}
getName() {
return this.name;
}
}
class Song_Obj extends Basic_Obj {
constructor(song_id, song_name, play_time, mvid = 0, no_copyright_rcmd = null, alias = null, trans_name = null) {
super(song_id, song_name);
this.playTime = play_time;
this.mvid = mvid;
this.noCopyrightRcmd = no_copyright_rcmd;
this.alias = alias;
this.transName = trans_name;
}
getPlayTime() {
let time = '';
let seconds = Math.round(this.playTime / 1000);
if ((seconds / 3600) >= 1) {
time += Math.floor(seconds / 3600) + ":";
}
seconds %= 3600;
if ((seconds / 60) >= 1) {
if ((seconds / 60) > 9) {
time += Math.floor(seconds / 60) + ":";
} else {
time += "0" + Math.floor(seconds / 60).toString() + ":";
}
}
seconds %= 60;
if (seconds >= 10) {
time += seconds;
} else {
time += "0" + seconds.toString();
}
return time;
}
getMvid() {
return this.mvid;
}
getAliasName() {
if (this.alias) {
return this.alias[0];
}
return null;
}
getTransName() {
return this.transName;
}
isExistsMv() {
if (this.mvid > 0) {
return true;
}
return false;
}
isNoCopyright() {
if (this.noCopyrightRcmd) {
return true;
}
return false;
}
}
class Aritst_Obj extends Basic_Obj {
constructor(aritst_id, aritst_name) {
super(aritst_id, aritst_name);
}
}
class Album_Obj extends Basic_Obj {
constructor(album_id, album_name, pic_url) {
super(album_id, album_name);
this.picUrl = pic_url;
}
getPicUrl() {
return this.picUrl;
}
}
class Music_Obj {
constructor(playlist_id, song_obj, aritst_arr, album_obj) {
this.playlistId = playlist_id;
this.song = song_obj;
this.aritst = aritst_arr;
this.album = album_obj;
}
createTableItem(position, is_even = false) {
let tr = document.createElement('tr');
tr.id = this.song.getId().toString() + Date.now().toString();
if (is_even) {
tr.classList.add('even');
}
if (this.song.isNoCopyright()) {
tr.classList.add('js-dis');
}
let aritst_name = '';
this.aritst.forEach((item) => {
if (aritst_name) {
aritst_name += "/" + item.getName();
} else {
aritst_name = item.getName();
}
});
let td_0 = document.createElement('td');
td_0.className = 'left';
let td_0_div = document.createElement('div');
td_0_div.className = 'hd';
let td_0_div_span_0 = document.createElement('span');
td_0_div_span_0.className = 'ply';
td_0_div_span_0.setAttribute('data-res-id', this.song.getId());
td_0_div_span_0.setAttribute('data-res-type', '18');
td_0_div_span_0.setAttribute('data-res-action', 'play');
td_0_div_span_0.setAttribute('data-res-from', '13');
td_0_div_span_0.setAttribute('data-res-data', this.playlistId);
let td_0_div_span_1 = document.createElement('span');
td_0_div_span_1.className = 'num';
td_0_div_span_1.textContent = position;
td_0_div.appendChild(td_0_div_span_0);
td_0_div.appendChild(td_0_div_span_1);
td_0.appendChild(td_0_div);
let alias_name = '';
let song_title = '';
if (this.song.isExistsMv()) {
song_title = this.song.getName();
} else if (this.song.getTransName()) {
alias_name = this.song.getTransName();
song_title = this.song.getName() + ' - (' + this.song.getTransName() + ')';
} else if (this.song.getAliasName()) {
alias_name = this.song.getAliasName();
song_title = this.song.getName() + ' - (' + this.song.getAliasName() + ')';
} else {
song_title = this.song.getName();
}
let td_1 = document.createElement('td');
let td_1_div = document.createElement('div');
td_1_div.className = 'f-cb';
let td_1_div_div = document.createElement('div');
td_1_div_div.className = 'tt';
let td_1_div_div_div = document.createElement('div');
td_1_div_div_div.className = 'ttc';
let td_1_div_div_div_span = document.createElement('span');
td_1_div_div_div_span.className = 'txt';
let td_1_div_div_div_span_a = document.createElement('a');
td_1_div_div_div_span_a.href = '/song?id=' + this.song.getId();
let td_1_div_div_div_span_a_b = document.createElement('b');
td_1_div_div_div_span_a_b.textContent = this.song.getName();
td_1_div_div_div_span_a_b.title = song_title;
td_1_div_div_div_span_a.appendChild(td_1_div_div_div_span_a_b);
td_1_div_div_div_span.appendChild(td_1_div_div_div_span_a);
td_1_div_div_div.appendChild(td_1_div_div_div_span);
td_1_div_div.appendChild(td_1_div_div_div);
td_1_div.appendChild(td_1_div_div);
td_1.appendChild(td_1_div);
if (this.song.isExistsMv()) {
let td_1_div_div_div_span_span = document.createElement('span');
td_1_div_div_div_span_span.className = 'mv';
td_1_div_div_div_span_span.textContent = 'MV';
td_1_div_div_div_span_span.title = '播放mv';
td_1_div_div_div_span_span.setAttribute('data-res-id', this.song.getId());
td_1_div_div_div_span_span.setAttribute('data-res-action', 'mv');
td_1_div_div_div_span_span.addEventListener('click', () => {
window.parent.location.href = '/mv?id=' + this.song.getMvid();
});
td_1_div_div_div_span.appendChild(td_1_div_div_div_span_span);
} else if (alias_name) {
let td_1_div_div_div_span_span = document.createElement('span');
td_1_div_div_div_span_span.className = 's-fc8';
td_1_div_div_div_span_span.title = alias_name;
td_1_div_div_div_span_span.textContent = ' - (' + alias_name + ')';
td_1_div_div_div_span.appendChild(td_1_div_div_div_span_span);
}
let td_2 = document.createElement('td');
td_2.className = 's-fc3';
let td_2_span = document.createElement('span');
td_2_span.className = 'u-dur';
td_2_span.textContent = this.song.getPlayTime();
let td_2_div = document.createElement('div');
td_2_div.className = 'opt hshow';
let td_2_div_a = document.createElement('a');
td_2_div_a.className = 'u-icn u-icn-81 icn-add';
td_2_div_a.href = 'javascript:;';
td_2_div_a.title = '添加到播放列表';
td_2_div_a.setAttribute('hidefocus', 'true');
td_2_div_a.setAttribute('data-res-type', '18');
td_2_div_a.setAttribute('data-res-id', this.song.getId());
td_2_div_a.setAttribute('data-res-action', 'addto');
td_2_div_a.setAttribute('data-res-from', '13');
td_2_div_a.setAttribute('data-res-data', this.playlistId);
let td_2_div_span_0 = document.createElement('span');
td_2_div_span_0.className = 'icn icn-fav';
td_2_div_span_0.title = '收藏';
td_2_div_span_0.setAttribute('data-res-id', this.song.getId());
td_2_div_span_0.setAttribute('data-res-type', '18');
td_2_div_span_0.setAttribute('data-res-action', 'fav');
let td_2_div_span_1 = document.createElement('span');
td_2_div_span_1.className = 'icn icn-share';
td_2_div_span_1.title = '分享';
td_2_div_span_1.textContent = '分享';
td_2_div_span_1.setAttribute('data-res-id', this.song.getId());
td_2_div_span_1.setAttribute('data-res-type', '18');
td_2_div_span_1.setAttribute('data-res-action', 'share');
td_2_div_span_1.setAttribute('data-res-name', this.song.getName());
td_2_div_span_1.setAttribute('data-res-author', aritst_name);
td_2_div_span_1.setAttribute('data-res-pic', this.album.getPicUrl());
let td_2_div_span_2 = document.createElement('span');
td_2_div_span_2.className = 'icn icn-dl';
td_2_div_span_2.title = '下载';
td_2_div_span_2.setAttribute('data-res-id', this.song.getId());
td_2_div_span_2.setAttribute('data-res-type', '18');
td_2_div_span_2.setAttribute('data-res-action', 'download');
td_2_div.appendChild(td_2_div_a);
td_2_div.appendChild(td_2_div_span_0);
td_2_div.appendChild(td_2_div_span_1);
td_2_div.appendChild(td_2_div_span_2);
td_2.appendChild(td_2_span);
td_2.appendChild(td_2_div);
let td_3 = document.createElement('td');
let td_3_div = document.createElement('div');
td_3_div.className = 'text';
td_3_div.title = aritst_name;
let td_3_div_span = document.createElement('span');
td_3_div_span.title = aritst_name;
this.aritst.forEach((item, index) => {
let td_3_div_span_a = document.createElement('a');
td_3_div_span_a.href = '/aritst?id=' + item.getId();
td_3_div_span_a.textContent = item.getName();
td_3_div_span_a.setAttribute('hidefocus', 'true');
td_3_div_span.appendChild(td_3_div_span_a);
if (index > 0) {
td_3_div_span_a.before("/");
}
});
td_3_div.appendChild(td_3_div_span);
td_3.appendChild(td_3_div);
let td_4 = document.createElement('td');
let td_4_div = document.createElement('div');
td_4_div.className = 'text';
let td_4_div_a = document.createElement('a');
td_4_div_a.title = this.album.getName();
td_4_div_a.textContent = this.album.getName();
td_4_div_a.href = '/album?id=' + this.album.getId();
td_4_div.appendChild(td_4_div_a);
td_4.appendChild(td_4_div);
tr.appendChild(td_0);
tr.appendChild(td_1);
tr.appendChild(td_2);
tr.appendChild(td_3);
tr.appendChild(td_4);
return tr;
}
}
function get_Music_Info(music_id) {
return new Promise(function(resolve, _reject) {
let music_details = {
'method': 'GET',
'url': 'http://music.163.com/api/song/detail/?id=' + music_id + '&ids=%5B' + music_id + '%5D',
'headers': {
'Content-Type': 'application/x-www-form-urlencoded'
},
'responseType': 'json',
'onload': function(res) {
if (res.status == 200 && res.readyState == 4) {
resolve(res.response);
} else {
console.warn('返回的歌曲信息状态码异常。code: ' + res.status + ' readyState: ' + res.readyState);
resolve(null);
}
},
'onerror': function() {
console.error('获取歌曲信息出错!id:', music_id);
resolve(null);
}
};
GM_xmlhttpRequest(music_details);
});
}
async function main_Cloud_Music(playlist_obj, callback) {
let record_arr = [];
document.querySelectorAll('#song-list-pre-cache tbody tr').forEach((item) => {
record_arr.push(item.id.replace(/\d{13}$/, ''));
});
let position = record_arr.length;
for (let ele of playlist_obj.playlist.trackIds) {
if (! record_arr.includes(ele.id.toString())) {
await get_Music_Info(ele.id).then((data) => {
if (data) {
if (data.code == 200) {
position ++;
let music_info = data.songs[0];
let song_obj = new Song_Obj(music_info.id ,music_info.name, music_info.duration, music_info.mvid, music_info.noCopyrightRcmd, music_info.alias, music_info.transName);
let aritst_arr = [];
for (let a_ele of music_info.artists) {
let artist_obj = new Aritst_Obj(a_ele.id, a_ele.name);
aritst_arr.push(artist_obj);
}
let album_obj = new Album_Obj(music_info.album.id, music_info.album.name, music_info.album.picUrl);
let music_obj = new Music_Obj(ele.id ,song_obj, aritst_arr, album_obj);
if (document.querySelector('#song-list-pre-cache tbody')) {
document.querySelector('#song-list-pre-cache tbody').appendChild(music_obj.createTableItem(position, (position % 2 === 1)));
}
}
}
});
}
}
if (typeof(callback) === 'function') {
callback();
}
}
function init_Cloud_Music() {
if (location.href.indexOf('/playlist?id=') !== -1) {
if (document.getElementById('m-playlist')) {
document.querySelectorAll('.soil').forEach((item) => {
item.parentNode.removeChild(item);
});
let playlist_id = '';
if (document.getElementById('content-operation')) {
playlist_id = document.getElementById('content-operation').getAttribute('data-rid');
}
if (playlist_id) {
let show_text = null;
if (document.querySelector('.m-playlist-see-more .text')) {
show_text = document.querySelector('.m-playlist-see-more .text');
show_text.textContent = '正在获取歌单信息,请稍等...';
}
let details = {
'method': 'GET',
'url': 'https://api.imjad.cn/cloudmusic/?type=playlist&id=' + playlist_id,
'headers': {
'Content-Type': 'application/x-www-form-urlencoded'
},
'responseType': 'json',
'onload': function(res) {
if (res.status == 200 && res.readyState == 4) {
let playlist_obj = res.response;
if (playlist_obj.code == 200) {
if (show_text) {
show_text.textContent = '已经获取歌单信息,正在创建歌曲条目,请稍等...';
}
main_Cloud_Music(playlist_obj, () => {
if (document.querySelector('.m-playlist-see-more')) {
let show_module = document.querySelector('.m-playlist-see-more');
show_module.parentNode.removeChild(show_module);
}
});
}
} else {
if (show_text) {
show_text.textContent = '返回的歌单信息状态码异常。code: ' + res.status + ' readyState: ' + res.readyState;
}
console.warn('返回的歌单信息状态码异常。code: ' + res.status + ' readyState: ' + res.readyState);
}
},
'onerror': function() {
if (show_text) {
show_text.textContent = '获取歌单信息失败了!可能是网络不佳或者 API 失效了。';
}
console.error('获取歌单信息失败了!\n可能是网络不佳或者 API 失效了。');
}
};
GM_xmlhttpRequest(details);
}
}
}
}
init_Cloud_Music();
})();