// ==UserScript==
// @name Tuna browser script
// @namespace univrsal
// @version 1.0.1
// @description Get song information from web players, based on NowSniper by Kıraç Armağan Önal
// @author univrsal
// @match *://open.spotify.com/*
// @match *://soundcloud.com/*
// @grant unsafeWindow
// ==/UserScript==
(function() {
'use strict';
// Configuration
var port = 1608;
var refresh_rate_ms = 500;
var cooldown_ms = 10000;
// Tuna isn't running we sleep, because every failed request will log into the console
// so we don't want to spam it
var failure_count = 0;
var cooldown = 0;
function post(data){
var url = 'http://localhost:' + port + '/';
var xhr = new XMLHttpRequest();
xhr.open('POST', url);
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Access-Control-Allow-Headers', '*');
xhr.setRequestHeader('Access-Control-Allow-Origin', '*');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status !== 200) {
failure_count++;
}
}
};
xhr.send(JSON.stringify({data,hostname:window.location.hostname,date:Date.now()}));
}
// Safely query something, and perform operations on it
function query(target, fun, alt = null) {
var element = document.querySelector(target);
if (element !== null) {
return fun(element);
}
return alt;
}
function timestamp_to_ms(ts) {
var splits = ts.split(':');
if (splits.length == 2) {
return splits[0] * 60 * 1000 + splits[1] * 1000;
} else if (splits.length == 3) {
return splits[0] * 60 * 60 * 1000 + splits[1] * 60 * 1000 + splits[0] * 1000;
}
return 0;
}
function StartFunction() {
setInterval(()=>{
if (failure_count > 3) {
console.log('Failed to connect multiple times, waiting a few seconds');
cooldown = cooldown_ms;
failure_count = 0;
}
if (cooldown > 0) {
cooldown -= refresh_rate_ms;
return;
}
let hostname = window.location.hostname;
// TODO: maybe add more?
if (hostname == 'soundcloud.com') {
let status = query('.playControl', e => e.classList.contains('playing') ? "playing" : "stopped", 'unknown');
let cover_url = query('.playbackSoundBadge span.sc-artwork', e => e.style.backgroundImage.slice(5, -2).replace('t50x50','t500x500'));
let title = query('.playbackSoundBadge__titleLink', e => e.title);
let artists = [ query('.playbackSoundBadge__lightLink', e => e.title) ];
let progress = query('.playbackTimeline__timePassed span:nth-child(2)', e => timestamp_to_ms(e.textContent));
let duration = query('.playbackTimeline__duration span:nth-child(2)', e => timestamp_to_ms(e.textContent));
let album_url = query('.playbackSoundBadge__titleLink', e => e.href);
if (title !== null) {
post({ cover_url, title, artists, status, progress, duration, album_url });
}
} else if (hostname == 'open.spotify.com') {
let status = query('.player-controls [data-testid="control-button-pause"]', e => !!e ? 'playing' : 'stopped', 'unknown');
let cover_url = query('[data-testid="CoverSlotExpanded__container"] .cover-art-image', e => e.style.backgroundImage.slice(5, -2));
let title = query('[data-testid="nowplaying-track-link"]', e => e.textContent);
let artists = query('span[draggable] a[href*="artist"]', e => Array.from(e));
let progress = query('.playback-bar .playback-bar__progress-time', e => timestamp_to_ms(e[0].textContent));
let duration = query('.playback-bar .playback-bar__progress-time', e => timestamp_to_ms(e[1].textContent));
let album_url = query('[data-testid="nowplaying-track-link"]', e => e.href);
if (title !== null) {
post({ cover_url, title, artists, status, progress, duration, album_url });
}
}
}, 500);
}
StartFunction();
})();