// ==UserScript==
// @name 网易云:云盘歌曲快传(含周杰伦)|云盘匹配纠正|音乐歌词乐谱下载
// @namespace https://github.com/Cinvin/myuserscripts
// @license MIT
// @version 2.0.0
// @description 个人主页:云盘歌曲快传、云盘匹配纠正、云盘导入导出,歌曲页:音乐、歌词、乐谱下载
// @author cinvin
// @match https://music.163.com/*
// @grant GM_xmlhttpRequest
// @grant GM_download
// @grant unsafeWindow
// @require https://fastly.jsdelivr.net/npm/sweetalert2@11.7.10
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
function getcookie(key) {
var cookies = document.cookie
, text = "\\b" + key + "="
, find = cookies.search(text);
if (find < 0)
{
return ""
}
find += text.length - 2;
var index = cookies.indexOf(";", find);
if (index < 0){
index = cookies.length
}
return cookies.substring(find, index) || ""
};
function weapiRequest(url,config){
let data=config.data
data.csrf_token = getcookie("__csrf");
url = url.replace("api", "weapi");
config.method = "post";
config.cookie = 'os=pc;appver=2.9.7'
delete config.query
config.headers={ "content-type": "application/x-www-form-urlencoded",}
var encRes = unsafeWindow.asrsea(
JSON.stringify(data),
"010001",
"00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7",
"0CoJUm6Qyw8W8jud");
config.data = `params=${ encodeURIComponent(encRes.encText) }&encSecKey=${ encodeURIComponent(encRes.encSecKey) }`
config.url=url+`?csrf_token=${data.csrf_token}`
//console.log(config)
GM_xmlhttpRequest(config)
}
function showConfirmBox(msg){
Swal.fire({
title: '提示',
text: msg,
confirmButtonText: '确定',
})
}
function showTips(tip,type){
//type:1 √ 2:!
unsafeWindow.g_showTipCard({
tip: tip,
type: type
})
}
function fileSizeDesc(fileSize) {
if (fileSize < 1024) {
return fileSize+ 'B'
} else if (fileSize >= 1024 && fileSize < Math.pow(1024, 2)) {
return (fileSize / 1024).toFixed(1).toString() + 'K'
} else if (fileSize >= Math.pow(1024, 2) && fileSize < Math.pow(1024, 3)) {
return (fileSize / Math.pow(1024, 2)).toFixed(1).toString() + 'M';
} else if (fileSize > Math.pow(1024, 3) && fileSize < Math.pow(1024, 4)) {
return (fileSize / Math.pow(1024, 3)).toFixed(2).toString() + 'G';
} else if (fileSize > Math.pow(1024, 4)) {
return (fileSize / Math.pow(1024, 4)).toFixed(2).toString() + 'T';
}
};
function duringTimeDesc(dt) {
let secondTotal=Math.round(dt/1000)
let min=Math.floor(secondTotal/60)
let sec=secondTotal%60
return min.toString().padStart(2,'0')+':'+sec.toString().padStart(2,'0')
};
function levelDesc(level) {
switch (level) {
case 'standard':
return '标准'
case 'higher':
return '较高'
case 'exhigh':
return '极高'
case 'lossless':
return '无损'
case 'hires':
return 'Hi-Res'
case 'jyeffect':
return '鲸云臻音'
case 'jymaster':
return '鲸云母带'
default:
return level
}
};
//歌曲页
if (location.href.includes('song')){
let cvrwrap=document.querySelector(".cvrwrap")
if(cvrwrap){
let songId=Number(location.href.match(/\d+$/g));
let songTitle=document.head.querySelector("[property~='og:title'][content]").content;
let songArtist=document.head.querySelector("[property~='og:music:artist'][content]").content;//split by /
let songAlbum=document.head.querySelector("[property~='og:music:album'][content]").content;
//songdownload
let songDownloadDiv = document.createElement('div');
songDownloadDiv.className="out s-fc3"
let songDownloadP = document.createElement('p');
songDownloadP.innerHTML = '歌曲下载:';
songDownloadDiv.style.display="none"
songDownloadDiv.appendChild(songDownloadP)
cvrwrap.appendChild(songDownloadDiv)
//lyricDownload
let lrcDownloadDiv = document.createElement('div');
lrcDownloadDiv.className="out s-fc3"
let lrcDownloadP = document.createElement('p');
lrcDownloadP.innerHTML = '歌词下载:';
lrcDownloadDiv.style.display="none"
lrcDownloadDiv.appendChild(lrcDownloadP)
cvrwrap.appendChild(lrcDownloadDiv)
let lyricObj={}
//sheetDownload
let sheetDownloadDiv = document.createElement('div');
sheetDownloadDiv.className="out s-fc3"
let sheetDownloadP = document.createElement('p');
sheetDownloadP.innerHTML = '乐谱下载:';
sheetDownloadDiv.style.display="none"
sheetDownloadDiv.appendChild(sheetDownloadP)
cvrwrap.appendChild(sheetDownloadDiv)
//wikiMemory
let wikiMemoryDiv = document.createElement('div');
wikiMemoryDiv.className="out s-fc3"
let wikiMemoryP = document.createElement('p');
wikiMemoryP.innerHTML = '回忆坐标:';
wikiMemoryDiv.style.display="none"
wikiMemoryDiv.appendChild(wikiMemoryP)
cvrwrap.appendChild(wikiMemoryDiv)
//songDownload
class SongFetch{
constructor(songId,title,artists,album) {
this.songId=songId;
this.title=title;
this.artists=artists
this.album=album
};
getNCMSource(){
weapiRequest("/api/v3/song/detail", {
type: "json",
data: {
c: JSON.stringify([{'id':songId}]),
},
onload: (responses)=> {
let songdetail=JSON.parse(responses.response);
//console.log(songdetail)
if (songdetail.privileges[0].cs){
songDownloadP.innerHTML='歌曲下载(云盘版本):'
}
if (songdetail.privileges[0].plLevel!="none"){
weapiRequest("/api/song/enhance/player/url/v1", {
type: "json",
data: {
ids: JSON.stringify([songId]),
level: songdetail.privileges[0].plLevel,
encodeType: 'flac'
},
onload: (responses)=> {
let content=JSON.parse(responses.response);
if(content.data[0].url!=null){
//console.log(content)
let config={
filename:songArtist+'-'+songTitle+'.'+content.data[0].type.toLowerCase(),
url:content.data[0].url,
size:content.data[0].size,
desc:levelDesc(songdetail.privileges[0].plLevel)
}
if (songdetail.privileges[0].dlLevel!="none" && songdetail.privileges[0].plLevel!=songdetail.privileges[0].dlLevel){
config.desc=`试听版本(${config.desc})`
}
this.createButton(config)
}
}
})
}
//example songid:1914447186
if (songdetail.privileges[0].dlLevel!="none" && songdetail.privileges[0].plLevel!=songdetail.privileges[0].dlLevel && unsafeWindow.GUser.userType==0){
weapiRequest("/api/song/enhance/download/url/v1", {
type: "json",
data: {
id: songId,
level: songdetail.privileges[0].dlLevel,
encodeType: 'mp3'
},
onload: (responses)=> {
let content=JSON.parse(responses.response)
if(content.data.url!=null){
//console.log(content)
let config={
filename:songArtist+'-'+songTitle+'.'+content.data.type.toLowerCase(),
url:content.data.url,
size:content.data.size,
desc:`下载版本(${levelDesc(songdetail.privileges[0].dlLevel)})`
}
this.createButton(config)
}
}
})
}
}
})
};
createButton(config){
let btn = document.createElement('a');
btn.text = config.desc;
btn.className="des s-fc7"
btn.style.margin='2px';
btn.addEventListener('click', () => {
dwonloadSong(config.url,config.filename,btn)
})
songDownloadP.appendChild(btn)
songDownloadDiv.style.display="inline"
};
}
let songFetch=new SongFetch(songId,songTitle,songArtist,songAlbum)
//wyy可播放
if(!document.querySelector(".u-btni-play-dis")){
songFetch.getNCMSource()
}
//lyric
weapiRequest("/api/song/lyric", {
type: "json",
data: {
id: songId,
tv: -1,
lv: -1,
rv: -1,
kv: -1,
},
onload: function(responses) {
let content=JSON.parse(responses.response)
lyricObj=content
let lrc = document.createElement('a');
lrc.text = '下载';
lrc.className="des s-fc7"
lrc.style.margin='2px';
lrc.addEventListener('click',() =>{
downloadLyric('lrc',songTitle)
})
lrcDownloadP.appendChild(lrc)
lrcDownloadDiv.style.display="inline"
},
});
//sheet
weapiRequest("/api/music/sheet/list/v1", {
type: "json",
data: {
id: songId,
abTest: 'b',
},
onload: (responses)=> {
//console.log(content)
let content=JSON.parse(responses.response)
if (content.data.errorCode==null){
content.data.musicSheetSimpleInfoVOS.forEach(item=>{
let texts=[item.name,item.playVersion,item.musicKey+"调"]
if (item.difficulty.length>0){
texts.push("难度"+item.difficulty)
}
if (item.chordName.length>0){
texts.push(item.chordName+"和弦")
}
if (item.bpm>0){
texts.push(item.bpm+"bpm")
}
texts.push(item.totalPageSize+"页")
let btn = document.createElement('a');
btn.text = texts.join("-");
btn.className="des s-fc7"
btn.style.margin='5px';
btn.addEventListener('click', () => {
dwonloadSheet(item.id,`${songTitle}-${item.name}-${item.playVersion}`)
})
sheetDownloadP.appendChild(btn)
sheetDownloadDiv.style.display="inline"
})
}
}
})
//wiki
weapiRequest("/api/song/play/about/block/page", {
type: "json",
data: {
songId: songId,
},
onload: function(responses) {
//console.log(content)
let content=JSON.parse(responses.response)
if(content.data.blocks[0].creatives.length>0){
content.data.blocks.forEach(block=>{
if(block.code=='SONG_PLAY_ABOUT_MUSIC_MEMORY' && block.creatives.length>0){
let info=block.creatives[0].resources
let firstTimeP = document.createElement('p');
firstTimeP.innerHTML = `第一次听:${info[0].resourceExt.musicFirstListenDto.date}`;
firstTimeP.className="des s-fc3"
firstTimeP.style.margin='5px';
wikiMemoryP.appendChild(firstTimeP)
let recordP = document.createElement('p');
recordP.innerHTML = `累计播放:${info[1].resourceExt.musicTotalPlayDto.playCount}次 ${info[1].resourceExt.musicTotalPlayDto.duration}分钟 ${info[1].resourceExt.musicTotalPlayDto.text}`;
recordP.className="des s-fc3"
recordP.style.margin='5px';
wikiMemoryP.appendChild(recordP)
wikiMemoryDiv.style.display="inline"
}
})
}
},
});
function downloadLyric(type,songTitle){
let content=lyricObj.lrc.lyric
let lrc = document.createElement('a');
let data=new Blob([content], {type:'type/plain'})
let fileurl = URL.createObjectURL(data)
lrc.href=fileurl
lrc.download=songTitle+'.lrc'
lrc.click()
URL.revokeObjectURL(data);
}
}
function dwonloadSong(url,fileName,dlbtn) {
let btntext=dlbtn.text
GM_download({
url: url,
name: fileName,
onprogress:function(e){
dlbtn.text=btntext+` 正在下载(${Math.round(e.loaded/e.totalSize*10000)/100}%)`
},
onload: function () {
dlbtn.text=btntext
},
onerror :function(){
dlbtn.text=btntext+' 下载失败'
}
});
}
function dwonloadSheet(sheetId,desc) {
//console.log(sheetId,desc)
weapiRequest("/api//music/sheet/preview/info", {
type: "json",
data: {
id: sheetId,
},
onload: (responses)=> {
//console.log(content)
let content=JSON.parse(responses.response)
content.data.forEach(sheetPage=>{
let fileName=`${desc}-${sheetPage.pageNumber}.jpg`
GM_download({
url: sheetPage.url,
name: fileName,
});
})
}
})
}
}
//个人主页
if(location.href.includes('user')){
let urlUserId=Number(location.href.match(/\d+$/g));
let editArea=document.querySelector('#head-box > dd > div.name.f-cb > div > div.edit')
if(editArea && urlUserId==unsafeWindow.GUser.userId){
//歌曲快传
let btnUpload=document.createElement('a')
btnUpload.id='cloudBtn'
btnUpload.className='u-btn2 u-btn2-1'
let btnUploadDesc=document.createElement('i')
btnUploadDesc.innerHTML='加载中'
btnUpload.appendChild(btnUploadDesc)
btnUpload.setAttribute("hidefocus","true");
btnUpload.style.marginRight='10px';
editArea.insertBefore(btnUpload,editArea.lastChild)
var toplist=[]
var selectOptions={"热门":{},"华语男歌手":{},"华语女歌手":{},"华语组合":{},"欧美男歌手":{},"欧美女歌手":{},"欧美组合":{},"日本男歌手":{},"日本女歌手":{},"日本组合":{},"韩国男歌手":{},"韩国女歌手":{},"韩国组合":{},}
var optionMap={0:"热门",1:"华语男歌手",2:"华语女歌手",3:"华语组合",4:"欧美男歌手",5:"欧美女歌手",6:"欧美组合",7:"日本男歌手",8:"日本女歌手",9:"日本组合",10:"韩国男歌手",11:"韩国女歌手",12:"韩国组合"}
var artistmap={}
//https://raw.githubusercontent.com/Cinvin/cdn/main/artist/top.json
//https://fastly.jsdelivr.net/gh/Cinvin/cdn@latest/artist/top.json
fetch('https://fastly.jsdelivr.net/gh/Cinvin/cdn@latest/artist/top.json')
.then(r => r.json())
.then(r=>{
toplist=r;
toplist.forEach(artist=>{
let desc = `${artist.name}(${artist.count}首/${artist.sizeDesc})`;
let id = artist.id
selectOptions[optionMap[artist.categroy]][artist.id]=`${artist.name}(${artist.count}首/${artist.sizeDesc})`
artistmap[artist.id]=artist
})
//console.log(selectOptions)
btnUpload.addEventListener('click',ShowCloudUploadPopUp)
btnUploadDesc.innerHTML='快速上传'
})
function ShowCloudUploadPopUp(){
Swal.fire({
title: '快速上传',
input: 'select',
inputOptions: selectOptions,
inputPlaceholder: '选择歌手',
confirmButtonText: '下一步',
showCloseButton: true,
footer:'',
inputValidator: (value) => {
if (!value) {
return '请选择歌手'
}
},
}).then(result=>{
if(result.isConfirmed){
fetchCDNConfig(result.value)
}
})
}
function fetchCDNConfig(artistId){
showTips(`正在获取资源配置...`,1)
//https://raw.githubusercontent.com/Cinvin/cdn/main/artist/${artistid}.json
//https://cdn.jsdelivr.net/gh/Cinvin/cdn/artist/${artistid}.json
fetch(`https://fastly.jsdelivr.net/gh/Cinvin/cdn@latest/artist/${artistId}.json`)
.then(r => r.json())
.then(r=>{
let uploader=new Uploader(r)
uploader.start()
})
.catch(`获取资源配置失败`)
}
class Uploader {
constructor(config,showAll=false) {
this.songs=[]
this.config=config
this.filter={text:'',noCopyright:true,vip:true,pay:true,lossless:false,all:showAll,songIndexs:[]}
this.page={current:1,max:1,limitCount:50}
this.batchUpload={threadCount:5,working:false,finnishThread:0,songIndexs:[]}
};
start(){
this.showPopup()
}
showPopup(){
Swal.fire({
showCloseButton: true,
showConfirmButton:false,
width: 800,
html:`
操作 | 歌曲标题 | 歌手 | 时长 | 文件信息 | 备注 |
---|