Greasy Fork is available in English.
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。
当前为
// ==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...
})();