Skip to content

Instantly share code, notes, and snippets.

@aequabit
Last active September 9, 2024 06:05
Show Gist options
  • Save aequabit/cc2c57e9018ac4f959a7f9007083634d to your computer and use it in GitHub Desktop.
Save aequabit/cc2c57e9018ac4f959a7f9007083634d to your computer and use it in GitHub Desktop.
SyncTube - Play History
// ==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