Last active
March 24, 2024 20:56
-
-
Save Alloyed/900e4ba569bdc8728e4c86403f77b249 to your computer and use it in GitHub Desktop.
Embeds a whole winamp instance on your cohost feed, when audio embeds are detected. All audio files on the page are added to a playlist and can be queued one after the other, or shuffled. (+any other winamp features like eq/panning)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Winamp on Cohost | |
// @namespace Violentmonkey Scripts | |
// @match https://cohost.org/* | |
// @noframes | |
// @require https://unpkg.com/webamp | |
// @grant GM.addElement | |
// @version 0.2 | |
// @author https://cohost.org/alloyed | |
// @description 3/9/2024, 6:06:50 PM: Embeds a whole winamp instance on your cohost feed, when audio embeds are detected. All audio files on the page are added to a playlist and can be queued one after the other, or shuffled. (+any other winamp features like eq/panning) | |
// @license MIT | |
// ==/UserScript== | |
// state variables | |
let sWebamp = null; | |
let sWebampReady = false; | |
const sTracksToAdd = []; | |
const sDiscoveredTracks = new Set(); | |
function debounce(func, timeoutMs) { | |
timeoutMs = timeoutMs || 16; | |
let timer; | |
return (...args) => { | |
clearTimeout(timer); | |
timer = setTimeout(() => { func.apply(this, args); }, timeoutMs); | |
}; | |
} | |
function retrieveTracksFromAudioTags() { | |
const tracks = []; | |
const audioNodes = document.querySelectorAll('audio'); | |
audioNodes.forEach((node)=> { | |
// From screen reader data, retrieve artist and title | |
let artist = (node.parentNode.querySelector('figcaption').firstChild.textContent); | |
if (artist === "(unknown artist)") { | |
artist = undefined; // TODO: get artist name from cohost profile? | |
} | |
const title = (node.parentNode.querySelector('figcaption').lastChild.textContent); | |
tracks.push({ | |
url: node.src, | |
metaData: { | |
title: title, | |
artist: artist, | |
}, | |
}); | |
}); | |
return tracks; | |
} | |
function updateWebamp() | |
{ | |
if(sWebamp) { | |
if(sWebampReady) { | |
sWebamp.appendTracks(sTracksToAdd); | |
sTracksToAdd.length = 0; | |
} else { | |
// webamp is currently rendering, we'll add anything added now when it's ready. | |
} | |
} else if (sTracksToAdd.length > 0) { | |
// we've discovered some tracks, but haven't made a webamp instance yet. let's do that. | |
sWebamp = new Webamp({ | |
enableDoubleSizeMode: true, // TODO: doesn't seem to work | |
}); | |
sWebamp.renderWhenReady(GM.addElement(document.body, 'div', {id: 'webamp_startingLocation', style: 'position: absolute; right: 80px; top: 300px; width: 100px; height: 100px;'})).then(async() => { | |
// keep all webamp elements on screen | |
document.querySelector("#main-window").parentNode.parentNode.parentNode.style.position = 'fixed'; | |
// add all queued tracks | |
sWebampReady = true; | |
updateWebamp(); | |
}) | |
} | |
} | |
const discoverNewTracks = debounce(function () { | |
const allTracks = retrieveTracksFromAudioTags(); | |
for(const track of allTracks) { | |
if(!sDiscoveredTracks.has(track.url)) { | |
sTracksToAdd.push(track); | |
sDiscoveredTracks.add(track.url); | |
} | |
} | |
updateWebamp(); | |
}) | |
// Yes this triggers _any_ time the dom changes in the entire app. I couldn't find a better way to do this so I'll just debounce the results | |
new MutationObserver(() => { | |
discoverNewTracks(); | |
}).observe(document.querySelector('#app'), {subtree: true, childList: true}); | |
discoverNewTracks(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment