// ==UserScript== // @name Tuna browser script // @namespace univrsal // @version 1.0.2 // @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/* // @match *://music.yandex.com/* // @match *://music.yandex.ru/* // @grant unsafeWindow // @license GPLv2 // @downloadURL none // ==/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 }); } } else if (hostname == 'music.yandex.ru') { // Yandex music support by MjKey let status = query('.player-controls__btn_play', e => e.classList.contains('player-controls__btn_pause') ? "playing" : "stopped", 'unknown'); let cover_url = query('.entity-cover__image', e => e.src.replace('200x200','400x400')); let title = query('.track__title', e => e.title); let artists = [ query('.track__artists', e => e.textContent) ]; let progress = query('.progress__left', e => timestamp_to_ms(e.textContent)); let duration = query('.progress__right', e => timestamp_to_ms(e.textContent)); let album_url = query('.track-cover a', e => e.title); if (title !== null) { post({ cover_url, title, artists, status, progress, duration, album_url }); } } }, 500); } StartFunction(); })();