325 lines
12 KiB
HTML
325 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>Song Requests Buddy</title>
|
|
<script>
|
|
/* ========================== */
|
|
/* SetUp your Streamer here */
|
|
/* ========================== */
|
|
const twitchName = "murmelmaus_gina";
|
|
|
|
// API to StreamerSongList
|
|
const streamerSongListApi = "https://api.streamersonglist.com/v1/streamers/";
|
|
|
|
// Invidious instance to use for youtube videos
|
|
// Acts as proxy to work locally
|
|
const invidiousUrl = 'https://inv.nadeko.net';
|
|
</script>
|
|
<style>
|
|
/* ========================== */
|
|
/* Customize your colors here */
|
|
/* ========================== */
|
|
:root {
|
|
--bg-primary: #1a1a1a;
|
|
--bg-secondary: #242424;
|
|
--text-primary: #ebdffa;
|
|
--accent: #c1aed7;
|
|
--accent-primary: #51386e;
|
|
}
|
|
|
|
body {
|
|
background-color: var(--bg-primary);
|
|
color: var(--text-primary);
|
|
line-height: 1.6;
|
|
font-family: system-ui, -apple-system, sans-serif;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
button {
|
|
background-color: var(--accent);
|
|
color: var(--accent-primary);
|
|
border: none;
|
|
padding: 8px 16px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
button:hover {
|
|
background-color: var(--text-primary);
|
|
}
|
|
|
|
input[type="text"] {
|
|
background-color: var(--bg-secondary);
|
|
border: 1px solid var(--accent);
|
|
color: var(--text-primary);
|
|
padding: 8px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
::-webkit-scrollbar {
|
|
width: 0;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div style="max-width: 1024px;margin: 0 auto;padding: 20px;">
|
|
<h1>Song Requests Buddy</h1>
|
|
|
|
<div id="selected-song">
|
|
<h2>No Song selected</h2>
|
|
<div style="width: 1024px; height: 576px; background-color: black"></div>
|
|
</div>
|
|
|
|
<h2>From StreamerSonglist Queue</h2>
|
|
<div id="queue" style="margin-top: 20px"></div>
|
|
|
|
<h2 id="songs-title">Songs</h2>
|
|
<label for="search"></label><input type="text" id="search" style="width: 1024px;align-self: center;height: 30px;"
|
|
placeholder="Search..." onkeyup="search(this.value)">
|
|
<div id="songs" style="margin-top: 20px"></div>
|
|
|
|
<iframe width="100%" height="auto" allowfullscreen style="max-width: 100%;aspect-ratio: 16 / 9;" id="youtube"
|
|
hidden></iframe>
|
|
</div>
|
|
|
|
<script>
|
|
const songs = new Map();
|
|
const attributes = new Map();
|
|
|
|
function updateQueue() {
|
|
const url = streamerSongListApi + twitchName + "/queue";
|
|
let queueRequest = new XMLHttpRequest();
|
|
queueRequest.onreadystatechange = function () {
|
|
if (this.readyState === 4 && this.status === 200) {
|
|
document.getElementById('queue').innerHTML = '';
|
|
const data = JSON.parse(this.responseText);
|
|
data.list.sort(function (a, b) {
|
|
return a.position - b.position;
|
|
}).forEach(function (item) {
|
|
let results = []
|
|
for (const [name, _] of songs) {
|
|
if (name.toLowerCase().includes(item.song.title.toLowerCase())) {
|
|
results.push(songs.values().find(it => it.id === item.song.id));
|
|
}
|
|
}
|
|
if (results.length > 0) {
|
|
for (const song of results) {
|
|
if (!(song.artist + " - " + song.title).toLowerCase().includes(item.song.artist.toLowerCase())) {
|
|
results.remove(songs.values().find(it => it.id === item.song.id));
|
|
}
|
|
}
|
|
}
|
|
if (results.length === 0) {
|
|
results.push(songs.values().find(it => it.id === item.song.id));
|
|
}
|
|
songList(results[0], document.getElementById('queue'));
|
|
})
|
|
if (data.list.length === 0) {
|
|
document.getElementById('queue').appendChild(document.createElement('div')).innerText = 'No songs in queue';
|
|
}
|
|
}
|
|
}
|
|
queueRequest.open("GET", url, true);
|
|
queueRequest.send();
|
|
}
|
|
|
|
function songList(it, parent) {
|
|
let song = document.createElement('button');
|
|
song.id = (it.artist + " - " + it.title).replace(/\s/g, '-').toLowerCase() + '-link';
|
|
let innerHTML = "<div style='display: inline-flex; align-items: center'><div style='text-align: left'>" + it.artist + " - " + it.title;
|
|
innerHTML += `<div style='font-size: small; text-align: left'>Last played: ${new Date(it.lastPlayed).toDateString()} | Times played: ${it.timesPlayed}</div></div>`;
|
|
it.attributeIds.forEach(function (id) {
|
|
if (attributes.has(id)) {
|
|
innerHTML += " <div style='background: #ffffff88; margin: 0 10px; border-radius: 5px; padding: 5px; height: 25px'>" + attributes.get(id) + "</div>";
|
|
}
|
|
})
|
|
song.innerHTML = innerHTML;
|
|
song.style.cursor = 'pointer';
|
|
song.style.fontSize = "1.2em";
|
|
song.style.fontWeight = "bold";
|
|
song.style.textDecoration = "none";
|
|
song.style.margin = "10px";
|
|
song.style.height = "69px";
|
|
song.onclick = function () {
|
|
showSong(it)
|
|
};
|
|
parent.appendChild(song);
|
|
parent.appendChild(document.createElement('br'));
|
|
}
|
|
|
|
function search(query) {
|
|
const results = [];
|
|
for (const [name, song] of songs) {
|
|
if (query.trim() !== '' && name.toLowerCase().includes(query.toLowerCase())) {
|
|
results.push(song);
|
|
}
|
|
}
|
|
document.getElementById('songs-title').innerText = 'Songs (' + results.length + '/' + songs.size + ')';
|
|
const parent = document.getElementById('songs');
|
|
parent.innerHTML = '';
|
|
if (results.length === 0) {
|
|
const noResults = document.createElement('div');
|
|
noResults.innerText = 'No results';
|
|
parent.appendChild(noResults);
|
|
return;
|
|
}
|
|
results.forEach(it => {
|
|
songList(it, document.getElementById('songs'));
|
|
})
|
|
}
|
|
|
|
function appendVersions(links, song) {
|
|
let parent = document.getElementById('selected-song');
|
|
for (let i = 0; i < links.length; i++) {
|
|
let button = document.createElement("button");
|
|
button.innerText = "Version " + (i + 1);
|
|
button.style.fontWeight = "bold";
|
|
button.style.textDecoration = "none";
|
|
button.onclick = function () {
|
|
showSong(song, i)
|
|
}
|
|
parent.appendChild(button);
|
|
}
|
|
}
|
|
|
|
function showSong(song, index = 0) {
|
|
const url = streamerSongListApi + twitchName + "/songs/" + song.id;
|
|
let songsRequest = new XMLHttpRequest();
|
|
songsRequest.onreadystatechange = function () {
|
|
if (this.readyState === 4 && this.status === 200) {
|
|
const data = JSON.parse(this.responseText);
|
|
if (!data.capo || data.capo.length === 0) {
|
|
showNotInSongList(song.artist + " - " + song.title);
|
|
} else {
|
|
if (data.capo.startsWith("https://")) {
|
|
let links = data.capo.split(" ");
|
|
showYoutube(song.artist + " - " + song.title, links[index]);
|
|
appendVersions(links, song);
|
|
} else {
|
|
showLocal(song.artist + " - " + song.title, data.capo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
songsRequest.open("GET", url, true);
|
|
songsRequest.send();
|
|
}
|
|
|
|
function showNotInSongList(name) {
|
|
let parent = document.getElementById('selected-song');
|
|
parent.innerHTML = '';
|
|
let song = document.createElement('div');
|
|
song.id = name.replace(/\s/g, '-').toLowerCase()
|
|
let title = document.createElement('h2');
|
|
title.innerText = name;
|
|
song.appendChild(title);
|
|
let video = document.createElement('div');
|
|
video.style.color = 'white';
|
|
video.style.backgroundColor = 'black';
|
|
video.style.textAlign = 'center';
|
|
video.style.alignContent = 'center';
|
|
video.style.fontSize = '1.2em';
|
|
video.style.fontWeight = 'bold';
|
|
video.style.width = '1024px';
|
|
video.style.height = '576px';
|
|
video.innerText = 'There is no Video connected to this Song. Please add it to the "Capo" field.';
|
|
video.appendChild(document.createElement('br'));
|
|
let search = document.createElement('a');
|
|
search.innerText = 'Search for this Song on YouTube in a new Tab';
|
|
search.target = '_blank';
|
|
search.href = "https://www.youtube.com/results?search_query=" + encodeURIComponent(name + " karaoke");
|
|
video.appendChild(search);
|
|
song.appendChild(video);
|
|
parent.appendChild(song);
|
|
}
|
|
|
|
function showYoutube(name, link) {
|
|
let parent = document.getElementById('selected-song');
|
|
parent.innerHTML = '';
|
|
let song = document.createElement('div');
|
|
song.id = name.replace(/\s/g, '-').toLowerCase()
|
|
let title = document.createElement('h2');
|
|
title.innerText = name;
|
|
song.appendChild(title);
|
|
let iframe = document.getElementById('youtube').cloneNode(false);
|
|
iframe.hidden = false;
|
|
iframe.src = invidiousUrl + '/embed/' + link.split("?v=")[1].split("&")[0] + '?iv_load_policy=3&related_videos=false&thin_mode=true&player_style=youtube&t=0';
|
|
iframe.referrerpolicy = "no-referrer-when-downgrade"
|
|
iframe.frameborder = "0"
|
|
song.appendChild(iframe);
|
|
parent.appendChild(song);
|
|
}
|
|
|
|
function showLocal(name, link) {
|
|
let parent = document.getElementById('selected-song');
|
|
parent.innerHTML = '';
|
|
let song = document.createElement('div');
|
|
song.id = name.replace(/\s/g, '-').toLowerCase()
|
|
let title = document.createElement('h2');
|
|
title.innerText = name;
|
|
song.appendChild(title);
|
|
let video = document.createElement('video');
|
|
video.controls = true;
|
|
video.width = 1024;
|
|
video.height = 572;
|
|
let source = document.createElement('source');
|
|
source.src = link;
|
|
video.appendChild(source);
|
|
song.appendChild(video);
|
|
parent.appendChild(song);
|
|
}
|
|
|
|
function initSongData(page = 0) {
|
|
const url = streamerSongListApi + twitchName + "/songs?size=100";
|
|
let songsRequest = new XMLHttpRequest();
|
|
songsRequest.onreadystatechange = function () {
|
|
if (this.readyState === 4 && this.status === 200) {
|
|
const data = JSON.parse(this.responseText);
|
|
if (data.total > (page + 1) * 100) {
|
|
initSongData(page + 1);
|
|
}
|
|
data.items.forEach(function (item) {
|
|
songs.set(item.artist + " - " + item.title, item)
|
|
})
|
|
}
|
|
}
|
|
songsRequest.open("GET", url + "¤t=" + page, true);
|
|
songsRequest.send();
|
|
}
|
|
|
|
function initAttributeData() {
|
|
const url = streamerSongListApi + twitchName;
|
|
let songsRequest = new XMLHttpRequest();
|
|
songsRequest.onreadystatechange = function () {
|
|
if (this.readyState === 4 && this.status === 200) {
|
|
const data = JSON.parse(this.responseText);
|
|
data.attributes.forEach(function (item) {
|
|
if (item.show) {
|
|
attributes.set(item.id, item.name);
|
|
}
|
|
})
|
|
}
|
|
}
|
|
songsRequest.open("GET", url, true);
|
|
songsRequest.send();
|
|
}
|
|
|
|
function initData() {
|
|
initAttributeData();
|
|
initSongData();
|
|
|
|
document.getElementById('songs-title').innerText = 'Songs (0/' + songs.size + ')';
|
|
}
|
|
|
|
initData();
|
|
updateQueue();
|
|
setInterval(function () {
|
|
updateQueue();
|
|
}, 10000);
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|