Tuna browser script

Get song information from web players, based on NowSniper by Kıraç Armağan Önal

当前为 2020-12-20 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Tuna browser script
  3. // @namespace univrsal
  4. // @version 1.0.2
  5. // @description Get song information from web players, based on NowSniper by Kıraç Armağan Önal
  6. // @author univrsal
  7. // @match *://open.spotify.com/*
  8. // @match *://soundcloud.com/*
  9. // @grant unsafeWindow
  10. // @license GPLv2
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15. // Configuration
  16. var port = 1608;
  17. var refresh_rate_ms = 500;
  18. var cooldown_ms = 10000;
  19.  
  20. // Tuna isn't running we sleep, because every failed request will log into the console
  21. // so we don't want to spam it
  22. var failure_count = 0;
  23. var cooldown = 0;
  24.  
  25. function post(data){
  26. var url = 'http://localhost:' + port + '/';
  27. var xhr = new XMLHttpRequest();
  28. xhr.open('POST', url);
  29.  
  30. xhr.setRequestHeader('Accept', 'application/json');
  31. xhr.setRequestHeader('Content-Type', 'application/json');
  32. xhr.setRequestHeader('Access-Control-Allow-Headers', '*');
  33. xhr.setRequestHeader('Access-Control-Allow-Origin', '*');
  34.  
  35. xhr.onreadystatechange = function () {
  36. if (xhr.readyState === 4) {
  37. if (xhr.status !== 200) {
  38. failure_count++;
  39. }
  40. }
  41. };
  42. xhr.send(JSON.stringify({data,hostname:window.location.hostname,date:Date.now()}));
  43. }
  44.  
  45. // Safely query something, and perform operations on it
  46. function query(target, fun, alt = null) {
  47. var element = document.querySelector(target);
  48. if (element !== null) {
  49. return fun(element);
  50. }
  51. return alt;
  52. }
  53.  
  54. function timestamp_to_ms(ts) {
  55. var splits = ts.split(':');
  56. if (splits.length == 2) {
  57. return splits[0] * 60 * 1000 + splits[1] * 1000;
  58. } else if (splits.length == 3) {
  59. return splits[0] * 60 * 60 * 1000 + splits[1] * 60 * 1000 + splits[0] * 1000;
  60. }
  61. return 0;
  62. }
  63.  
  64. function StartFunction() {
  65. setInterval(()=>{
  66. if (failure_count > 3) {
  67. console.log('Failed to connect multiple times, waiting a few seconds');
  68. cooldown = cooldown_ms;
  69. failure_count = 0;
  70. }
  71.  
  72. if (cooldown > 0) {
  73. cooldown -= refresh_rate_ms;
  74. return;
  75. }
  76.  
  77. let hostname = window.location.hostname;
  78.  
  79. // TODO: maybe add more?
  80. if (hostname == 'soundcloud.com') {
  81. let status = query('.playControl', e => e.classList.contains('playing') ? "playing" : "stopped", 'unknown');
  82. let cover_url = query('.playbackSoundBadge span.sc-artwork', e => e.style.backgroundImage.slice(5, -2).replace('t50x50','t500x500'));
  83. let title = query('.playbackSoundBadge__titleLink', e => e.title);
  84. let artists = [ query('.playbackSoundBadge__lightLink', e => e.title) ];
  85. let progress = query('.playbackTimeline__timePassed span:nth-child(2)', e => timestamp_to_ms(e.textContent));
  86. let duration = query('.playbackTimeline__duration span:nth-child(2)', e => timestamp_to_ms(e.textContent));
  87. let album_url = query('.playbackSoundBadge__titleLink', e => e.href);
  88.  
  89. if (title !== null) {
  90. post({ cover_url, title, artists, status, progress, duration, album_url });
  91. }
  92. } else if (hostname == 'open.spotify.com') {
  93. let status = query('.player-controls [data-testid="control-button-pause"]', e => !!e ? 'playing' : 'stopped', 'unknown');
  94. let cover_url = query('[data-testid="CoverSlotExpanded__container"] .cover-art-image', e => e.style.backgroundImage.slice(5, -2));
  95. let title = query('[data-testid="nowplaying-track-link"]', e => e.textContent);
  96. let artists = query('span[draggable] a[href*="artist"]', e => Array.from(e));
  97. let progress = query('.playback-bar .playback-bar__progress-time', e => timestamp_to_ms(e[0].textContent));
  98. let duration = query('.playback-bar .playback-bar__progress-time', e => timestamp_to_ms(e[1].textContent));
  99. let album_url = query('[data-testid="nowplaying-track-link"]', e => e.href);
  100.  
  101. if (title !== null) {
  102. post({ cover_url, title, artists, status, progress, duration, album_url });
  103. }
  104. } else if (hostname == 'music.yandex.ru') {
  105. // Yandex music support by MjKey
  106. let status = query('.player-controls__btn_play', e => e.classList.contains('player-controls__btn_pause') ? "playing" : "stopped", 'unknown');
  107. let cover_url = query('.entity-cover__image', e => e.src.replace('200x200','400x400'));
  108. let title = query('.track__title', e => e.title);
  109. let artists = [ query('.track__artists', e => e.textContent) ];
  110. let progress = query('.progress__left', e => timestamp_to_ms(e.textContent));
  111. let duration = query('.progress__right', e => timestamp_to_ms(e.textContent));
  112. let album_url = query('.track-cover a', e => e.title);
  113.  
  114. if (title !== null) {
  115. post({ cover_url, title, artists, status, progress, duration, album_url });
  116. }
  117. }
  118. }, 500);
  119.  
  120. }
  121.  
  122. StartFunction();
  123. })();