// ==UserScript==
// @name Picking Songs With Xiami in iirose.com
// @namespace http://tampermonkey.net/
// @version 2.0
// @description open xiami.com and iirose.com at the same time. Play any song with xiami and it will pushed automatically to iirose.com. Just mute one of them. 使用方法:同时打开虾米Xiami.com和iirose.com,使用虾米收听的任何歌曲都会同步推送至iirose。
// @author KeaneW
// @match https://iirose.com/messages.html
// @match https://emumo.xiami.com/radio/play/*
// @match https://www.xiami.com/*
// @grant GM.setValue
// @grant GM.getValue
// ==/UserScript==
(function() {
'use strict';
//get url of current page
var url = window.location.href;
GM.setValue("song", -5);//default to be -5 means null
var autoPicking = true;
//save a shuffled song list and current pointer
var shuffledSongList = [];
var shufflePointer = 0;
//for xiami radio page
if (url.search("xiami.com/radio")>=0){
var timerXiami = setInterval(function(){
if(document.getElementsByClassName("artist_info fl")[0]!=undefined){
clearInterval(timerXiami);
xiami_old();
}
}, 1000);
}
//for iirose
else if (url.search("iirose.com")>=0){
iirose();
}
else if (url.search("xiami.com")>=0){
xiami_new();
}
else {
console.log("Failed to match the website!");
}
//a quick shuffle algorithm
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
//simple sleep function
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
//xiami Collection starts
function xiamiCollection(){
//generate a button
console.log("51");
var parentDiv = document.getElementById("element_r");
var newNode = document.createElement ('div');
newNode.innerHTML='<form style="font-size:16px">'
+'<br><br>给蔷薇花园点歌!<br><br>'
+'从:<input type="number" name="startNum" max="50" style="border: 2px solid red;font-size: 16px;"> <br><br>到:<input type="number" name="endNum" style="border: 2px solid red;font-size: 16px;"><br><br>'
+'点:<input type="number" name="pickNum" min="1" max="10" style="border: 2px solid red;font-size: 16px;"> 首<br><br>'
+'<input type="checkbox" name="random" value="random" style="border: 2px solid red;font-size: 16px;"> 随机播放<br><br>'
+'<input id="pick!" type="button" style="background-color: #4CAF50;font-size: 16px;text-align:center;" value=" 点歌! "> <br><br>'
+'<input id="pickFive!" type="button" style="background-color: #4CAF50;font-size: 16px;text-align:center;" value=" 点五首歌! "> </form>';
parentDiv.appendChild (newNode);
document.getElementById("pick!").addEventListener("click", pickCollection);
document.getElementById("pickFive!").addEventListener("click", pickCollectionFive);
/*
<form>
从:
<input type="number" name="startNum" min="1" max="5">
到:
<input type="number" name="endNum" min="1" max="5">
<br>
点
<input type="number" name="pickNum" min="1" max="5">
首
<br>
<input type="checkbox" name="random" value="random"> 随机播放<br>
<input type="submit">
</form>
*/
}
//
async function pickCollection(){
//for xiami collection
console.log("84");
var songList = getSongList();
console.log("song size is "+songList.length);
var pickList = [];
var startP = document.getElementsByName("startNum")[0].value;
var endP = document.getElementsByName("endNum")[0].value;
var range = document.getElementsByName("pickNum")[0].value;
var random = document.getElementsByName("random")[0].checked;
console.log("new89"+startP+endP+range+random);
if (isNaN(startP)){ startP=0;
}
if (isNaN(endP)){ endP=50;
}
if (isNaN(range)){ range=5;
}
startP = parseInt(startP);endP = parseInt(endP);range = parseInt(range);
if (range>endP-startP){startP=1;endP=50;
}
pickList = songList.slice(startP, endP);
if (random){
shuffleArray(pickList);
}
var song;
var count = 1;
await sleep(2000);
for (song of pickList){
if (count>range){break;}
song.replace( /\s\s+/g, ' ' );
GM.setValue("song", song);
console.log("sended "+song);
count += 1;
await sleep(2000);
}
}
async function pickCollectionFive(){
//for xiami collection
if (shuffledSongList.length==0){
shuffledSongList = getSongList();
shuffleArray(shuffledSongList);
}
console.log("song size is "+shuffledSongList.length);
var pickList = [];
var range = 5;
console.log("ShufflePointer (before):"+shufflePointer);
if (shufflePointer+range+1>shuffledSongList.length){
pickList=shuffledSongList.slice(shufflePointer).concat(shuffledSongList.slice(0, shufflePointer+range-shuffledSongList.length));
shufflePointer=shufflePointer+range-shuffledSongList.length;
}
else {
pickList = shuffledSongList.slice(shufflePointer, shufflePointer+range);
shufflePointer=shufflePointer+range;
}
console.log("ShufflePointer (after):"+shufflePointer);
var song;
var count = 1;
await sleep(2000);
for (song of pickList){
song.replace( /\s\s+/g, ' ' );
GM.setValue("song", song);
console.log("sended "+song);
await sleep(2000);
}
}
function getSongList(){
var list = document.getElementsByClassName("song_name");
var listSize = list.length;
var tempNode;
var songList = [];
for (tempNode of list){
var songNameList=tempNode.innerText.split("-—")[0].split(" "); // like ["Return", "Of", "The", "Mack", "(...", ""]
var tempSoneNameNode;
var songName="";
for (tempSoneNameNode of songNameList){
if (tempSoneNameNode.search(/\.\.\./)>=0){
//console.log("99"+tempSoneNameNode);
break;
}
tempSoneNameNode=tempSoneNameNode.replace('(','').replace(')','');
songName += tempSoneNameNode;
songName += ' ';
}
var artisitList=tempNode.innerText.split("-—")[1].split(";");//like [" Dale Castell", "Tamia"]
var lastWord = artisitList[artisitList.length-1].split(' ').slice(-1)[0];
if (lastWord=='MV'){
//console.log('before: '+artisitList);
artisitList[artisitList.length-1]=artisitList[artisitList.length-1].split(' ').slice(0,-1).join(' ');
//console.log('after: '+artisitList);
}
var artisitName=artisitList.join(' ');
songList.push(songName+'|'+artisitName);
//console.log(songName+'|'+artisitName);
}
return songList;
}
//play with xiami
function xiami_old(){
//send first message to iirose
xiamiInfoParser(false);
//an api that can be used to monitor changes in the webpage
var mutationObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
//console.log(mutation);
//send message whenever it observers a change
xiamiInfoParser(false);
});
});
//it focuses on the title of the page
mutationObserver.observe(document.querySelector('title'), {
attributes: true,
characterData: true,
childList: true,
subtree: true,
attributeOldValue: true,
characterDataOldValue: true
});
}
function xiami_new(){
sleep(200);
//send first message to iirose, or not
//xiamiSendMessage(xiamiInfoParser(false));
addXMLRequestCallback( function( xhr ) {
//check if is getPlayInfo request
if (xhr.__sufei_url.search("getPlayInfo")>-1){
var songID = xhr.__sufei_url.split('[')[1].split(']')[0];
console.log("the new song's ID is "+songID.toString());
xiamiInfoParser(true, songID);
}
});
}
function addXMLRequestCallback(callback){
var oldSend, i;
if( XMLHttpRequest.callbacks ) {
// we've already overridden send() so just add the callback
XMLHttpRequest.callbacks.push( callback );
} else {
// create a callback queue
XMLHttpRequest.callbacks = [callback];
// store the native send()
oldSend = XMLHttpRequest.prototype.send;
// override the native send()
XMLHttpRequest.prototype.send = function(){
// process the callback queue
// the xhr instance is passed into each callback but seems pretty useless
// you can't tell what its destination is or call abort() without an error
// so only really good for logging that a request has happened
// I could be wrong, I hope so...
// EDIT: I suppose you could override the onreadystatechange handler though
for( i = 0; i < XMLHttpRequest.callbacks.length; i++ ) {
XMLHttpRequest.callbacks[i]( this );
}
// call the native send()
oldSend.apply(this, arguments);
}
}
}
function xiamiInfoParser(isnew, songID=""){
if(isnew){
console.log("sending " + songID.toString());
xiamiSendMessage(songID);
}
else{
var timerXiamiMessage = setInterval(function () {
//this timer waits for the page to load every 0.1s
//if the artist info is loaded we assume the page finished loading cuz that's what we need.
if(document.getElementsByClassName("artist_info fl")[0]!=null){
clearInterval(timerXiamiMessage);
//get song name and artist name
var songName = document.title.split("——")[0];
var artistName = document.getElementsByClassName("artist_info fl")[0].getElementsByTagName("strong")[0].innerHTML;
var message = songName+"|"+artistName;
xiamiSendMessage( message);
//set the "song" value with the above info
}
}, 100);}
}
//collect and send info to iirose
function xiamiSendMessage(msg){
GM.setValue("song", msg);
console.log("sended "+msg);
}
//play with iirose
async function iirose(){
//this timer checks if the window has loaded. Do checking every 3 seconds.
if (autoPicking){
var timer = setInterval(function () {
if(document.getElementById("moveinput")!=null){
clearInterval(timer);
//current song info
var tempSong=-5;
//this timer checks if there is a new message from xiami
var pickTimer = setInterval(async function () {
//get value from xiami. If not changed, do nothing and wait
var realSong = await GM.getValue("song", -5);
if(realSong!=tempSong){
//changed! pick the real song here
console.log("successfully recieve message "+realSong);
await pickingSong(realSong);
//update tempSong
tempSong = realSong;
}
//console.log("one more loop in iirose");
}, 500);
}
}, 3000);
}
//add entryIirose() to console
/*var scriptText='function entryIirose(str){ if(str.length>14){console.log("智障吗,搞那么长?");str="艰苦奋斗严肃活泼";} var rainbow=["C30002", "C30040", "C3007D", "C300BB", "8D00C3", "4F00C3", "1200C3", "002AC3", "0068C3", "00A5C3", "00C3A2", "00C365", "00C327", "15C300", "52C300", "90C300", "C3B800", "C37A00", "C33D00", "C30000", "C30022", "C30060", "C3009D", "AA00C3", "6D00C3", "2F00C3", "000DC3", "004AC3", "0088C3", "00C3C0", "00C382", "00C345", "00C307", "35C300", "72C300", "B0C300", "C39800", "C35A00", "C31D00", "C3001F"];var offset=Math.floor(Math.random()*rainbow.length);var text=str.split("");for(var i=0;i<text.length;i++){if(offset+i<rainbow.length){console.log(\'%c \'+rainbow[offset+i],\'color: #\'+rainbow[offset+i]);socket.send(\'{"m": "\'+text[i]+\'", "mc": "\'+rainbow[offset+i]+\'"}\')} else{socket.send(\'{"m": "\'+text[i]+\'", "mc": "\'+rainbow[offset+i-rainbow.length]+\'"}\');console.log(\'%c \'+rainbow[offset+i], \'color: #\'+rainbow[offset+i]);}}}';
addScript(scriptText);
//whether show rainbow effect
if(entryEffect){
entryIirose(null);
}
if (autoSpamming){
setInterval(function(){
entryIirose("自动刷屏123456789");
},300000);
}
*/
return;
}
//pick a song with the sring
function DirectPicking(url) {
var t = url.trim();
if (t) {
var o = t.match(Variable.regexp.assets.getLink);
o ? ("[" == t[0] && (o[0] = t.replace(Variable.regexp.pregmedia.linkSpaceAround2, "$1")),
Utils.service.moveinputDo("<> " + o[0])) : "#" == t[0] ? Utils.service.moveinputDo(t) : (-1 < Constant.Shortcuts.all.indexOf("@" + t) && (t += " "),
Utils.service.moveinputDo("@" + (" " == e[0] ? " " : "") + t))
}
}
function pickingSong(songInfo){
if (songInfo=='-5') return;
if(!isNaN(songInfo)){
console.log('recieved songID '+songInfo);
var url = "https://www.xiami.com/song/"+songInfo.toString();
DirectPicking(url);
}
else{
var str = songInfo.replace("|"," ");
inputString("@"+str);
//this timer checks whether the search results have loaded
var timer2 = setInterval(function () {
//if find nothing OR have found some songs, end the timer
if((document.getElementsByClassName("emptyShow")[0]!=null)||(document.getElementsByClassName("demandHolderPlayBtn")[0]!=null)){
//if find something
if(document.getElementsByClassName("emptyShow")[0]==null){
var songList = document.getElementsByClassName("demandHolderPlayBtn");
//console.log(songList[0]);
if(songList[0].getElementsByClassName("mainColor")[0].getElementsByClassName("buttonText")[0]!=null){
clearInterval(timer2);
//if successfully pick a song
var flag = 0;
//loop until one button can be clicked
for (var i = 0; i < songList.length; i++) {
var node = null;
for (var j = 0; j < songList[i].childNodes.length; j++) {
if (songList[i].childNodes[j].className == "mainColor") {
node = songList[i].childNodes[j];
break;
}
}
//check if clickable
if (node.hasAttribute("onclick")){
node.click();
console.log("pick "+i);
flag=1;
break;
}
console.log("cannot pick "+i);
}
//no button was clicked. Go back
if (flag==0){
//click return
console.log("failed");
//document.getElementsByClassName("footerItemBgShape_pointer")[0].onclick.apply();
Objs.demandHolder.function.event.call(this,0);
inputString("点歌失败,因为没有 "+str+" 的版权。");
}
}
}
//if find nothing
else {
//这里有bug!!
clearInterval(timer2);
Objs.demandHolder.function.event.call(this,0);
//document.getElementsByClassName("footerItemBgShape_pointer")[0].onclick.apply();
var strList=songInfo.split("|");
if (strList.length>1){
inputString("点歌失败,因为搜索不到 "+str+"。尝试模糊搜索 "+strList[0]);
setTimeout(function(){pickingSong(strList[0]);}, 1000)
}
else{
inputString("模糊搜索也失败了。");
//location.reload();
}
}
}
//
}, 800);
//var newSize = songlist.length;//for future use
}
}
//some tools
//this method types and submit a string in the typearea
function inputString(str){
/*
var inputBox = document.getElementById("moveinput");
var originText = inputBox.value;
var submit = document.getElementsByClassName("moveinputSendBtn")[0];
inputBox.value = str;
submit.click();
inputBox.value = originText;
*/
Utils.service.moveinputDo(str);
}
//this method add a script to html so that u can use the script in console
function addScript(scriptText){
var scriptElem = document.createElement('script');
scriptElem.innerHTML = scriptText;
document.body.appendChild(scriptElem);
}
//show a rainbow when enter a room. Very annoying!
//expired
/*
async function entryIirose(str){
if(str !=null){GM.setValue("entry",str);}
str = await GM.getValue("entry", "我踩着七彩祥云来了~")
if(str.length>13){
console.log("智障吗,搞那么长?");
str="本人专属跑马灯入场";
GM.setValue("entry",str);
}
var rainbow = ["C30002", "C30040", "C3007D", "C300BB", "8D00C3", "4F00C3", "1200C3", "002AC3", "0068C3", "00A5C3", "00C3A2", "00C365", "00C327", "15C300", "52C300", "90C300", "C3B800", "C37A00", "C33D00", "C30000", "C30022", "C30060", "C3009D", "AA00C3", "6D00C3", "2F00C3", "000DC3", "004AC3", "0088C3", "00C3C0", "00C382", "00C345", "00C307", "35C300", "72C300", "B0C300", "C39800", "C35A00", "C31D00", "C3001F"];
// var rainbow = ["00ABE5", "0063E5", "001CE6", "2C00E7", "7400E8", "BE00E9", "EA00CC", "EA0083", "EB003A", "EC0F00", "ED5900", "EEA400", "EEEF00", "A4EF00", "5AF000", "0EF100", "00F23C", "00F389", "00F4D5", "00C7F5","007DF5", "0033F5", "1600F5", "6000F5", "AA00F5", "F400F5", "F500AB", "F50060", "F50016", "F53300", "F57D00", "F5C700", "D8F500", "8DF500", "43F500", "00F506", "00F550", "00F59A", "00F5E4", "00BBF5"];
var offset = Math.floor(Math.random() * rainbow.length);
var text = str.split("");
for (var i=0;i<text.length;i++){
if(offset+i<rainbow.length){
console.log('%c '+rainbow[offset+i], 'color: #'+rainbow[offset+i]);
socket.send('{"m": "'+text[i]+'", "mc": "'+rainbow[offset+i]+'"}');
}
else {
console.log('%c '+rainbow[offset+i], 'color: #'+rainbow[offset+i]);
socket.send('{"m": "'+text[i]+'", "mc": "'+rainbow[offset+i-rainbow.length]+'"}');
}
}
}
*/
// Your code here...
})();