Last active
September 9, 2024 06:05
-
-
Save aequabit/cc2c57e9018ac4f959a7f9007083634d to your computer and use it in GitHub Desktop.
SyncTube - Play History
This file contains 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 SyncTube - Play History | |
// @match https://sync-tube.de/room/* | |
// @grant none | |
// @version 0.4 | |
// @author aequabit | |
// @description Remembers which videos were played | |
// @downloadURL https://gist.github.com/aequabit/cc2c57e9018ac4f959a7f9007083634d/raw/synctube-play-history.user.js | |
// ==/UserScript== | |
// TODO: | |
// - Also save initial video | |
// - Get existing history from other users? | |
// https://stackoverflow.com/a/35385518 | |
const fromHTML = (html, trim = true) => { | |
// Process the HTML string. | |
html = trim ? html.trim() : html; | |
if (!html) return null; | |
// Then set up a new template element. | |
const template = document.createElement('template'); | |
template.innerHTML = html; | |
const result = template.content.children; | |
// Then return either an HTMLElement or HTMLCollection, | |
// based on whether the input HTML had one or more roots. | |
if (result.length === 1) return result[0]; | |
return result; | |
}; | |
async function waitFor(conditional, interval = 20) { | |
return new Promise((resolve) => { | |
const _waitForInterval = setInterval(() => { | |
if (conditional() === true) { | |
clearInterval(_waitForInterval); | |
resolve(); | |
} | |
}, interval); | |
}); | |
} | |
let _ws = null; | |
async function WebSocket_onmessage(message) { | |
try { | |
const args = JSON.parse(message.data); | |
const eventId = args[0]; | |
if (eventId !== 45 /*player.load*/) return; | |
const entry = args[1]; | |
const historyObject = fromHTML(` | |
<div data-v-4af9d820="" class="card" id="hist-${entry.id}" style="cursor: pointer" data-src="${entry.src}"> | |
<div data-v-4af9d820="" class="thumbnail-wrapper"><img data-v-4af9d820="" class="thumbnail" src="${entry.thumbnail}" /><!----></div> | |
<div data-v-4af9d820="" class="info"> | |
<div data-v-4af9d820="" class="title">${entry.title}</div> | |
<div data-v-4af9d820="" class="author">${entry.author}</div> | |
<div data-v-4af9d820="" class="provider"><!----></div> | |
</div> | |
<!----> | |
</div> | |
`); | |
historyObject.onclick = function (e) { | |
if (!this.dataset.src) return console.error('No data-src attribute on history element'); | |
// [30,{"src":"https://www.youtube.com/watch?v=Nm4vhWs8HTc"},1725424261460] | |
const playlistAddEvent = [30 /*playlist.add*/, { src: this.dataset.src }, Date.now()]; | |
console.log(playlistAddEvent); | |
_ws.send(JSON.stringify(playlistAddEvent)); | |
}; | |
await waitFor(() => document.querySelector('#watchhistory') !== null); | |
const historyList = document.querySelector('#watchhistory'); | |
historyList.insertBefore(historyObject, historyList.firstChild); | |
console.log('PLAY: ', args[1]); | |
} catch (err) { | |
console.error('Could not parse incoming message: ', err); | |
} | |
} | |
const WebSocket_prototype_send = WebSocket.prototype.send; | |
WebSocket.prototype.send = function () { | |
if (_ws === null) { | |
this.addEventListener('message', WebSocket_onmessage); | |
_ws = this; | |
} | |
return WebSocket_prototype_send.apply(this, arguments); | |
}; | |
setTimeout(async () => { | |
await waitFor(() => document.querySelector('aside>.bottom') !== null); | |
const historyContainer = fromHTML(` | |
<div class="playlist" data-v-4af9d820="" data-v-a3645234=""> | |
<div data-v-4af9d820="" class="hover-scroll" id="watchhistory"></div> | |
</div> | |
`); | |
document.querySelector('aside>.bottom').appendChild(historyContainer); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment