Last active
April 14, 2021 09:01
-
-
Save jangxx/b4e6c15b7b5907e2f11fd12d0f464448 to your computer and use it in GitHub Desktop.
Reddit integration for watch2gether.com. Allows the automatic creation of playlists based on videos posted in a subreddit.
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 Watch2Gether Reddit Integration | |
// @namespace https://literalchaos.de | |
// @version 1.2.1 | |
// @description Add videos from reddit to a watch2gether playlist | |
// @author jangxx | |
// @match https://www.watch2gether.com/rooms/* | |
// @match https://w2g.tv/rooms/* | |
// @grant GM_xmlhttpRequest | |
// @grant GM_setValue | |
// @grant GM_getValue | |
// @connect reddit.com | |
// @require https://userscripts-mirror.org/scripts/source/107941.user.js | |
// @downloadURL https://gist.github.com/jangxx/b4e6c15b7b5907e2f11fd12d0f464448/raw/watch2gether_reddit_integration.user.js | |
// @updateURL https://gist.github.com/jangxx/b4e6c15b7b5907e2f11fd12d0f464448/raw/watch2gether_reddit_integration.user.js | |
// ==/UserScript== | |
const LOADING_SPINNER_CSS = ` | |
.loader-container { | |
display: inline-flex; | |
align-items: center; | |
} | |
.loader-o3R47I { | |
font-size: 10px; | |
text-indent: -9999em; | |
width: 20px; | |
height: 20px; | |
border-radius: 50%; | |
background: linear-gradient(to right, #555 10%, rgba(0,0,0, 0) 42%); | |
position: relative; | |
-webkit-animation: load3 1s infinite linear; | |
animation: load3 1s infinite linear; | |
-webkit-transform: translateZ(0); | |
-ms-transform: translateZ(0); | |
transform: translateZ(0); | |
} | |
.loader-o3R47I:before { | |
width: 50%; | |
height: 50%; | |
background: #555; | |
border-radius: 100% 0 0 0; | |
position: absolute; | |
top: 0; | |
left: 0; | |
content: ''; | |
} | |
.loader-o3R47I:after { | |
background: #fff; | |
width: 75%; | |
height: 75%; | |
border-radius: 50%; | |
content: ''; | |
margin: auto; | |
position: absolute; | |
top: 0; | |
left: 0; | |
bottom: 0; | |
right: 0; | |
} | |
@-webkit-keyframes load3 { | |
0% { | |
-webkit-transform: rotate(0deg); | |
transform: rotate(0deg); | |
} | |
100% { | |
-webkit-transform: rotate(360deg); | |
transform: rotate(360deg); | |
} | |
} | |
@keyframes load3 { | |
0% { | |
-webkit-transform: rotate(0deg); | |
transform: rotate(0deg); | |
} | |
100% { | |
-webkit-transform: rotate(360deg); | |
transform: rotate(360deg); | |
} | |
} | |
`; | |
(function() { | |
'use strict'; | |
// DOM stuff | |
const loadingSpinnerStyle = document.createElement("style"); | |
loadingSpinnerStyle.appendChild(document.createTextNode(LOADING_SPINNER_CSS)); | |
document.head.appendChild(loadingSpinnerStyle); | |
const redditTabElem = document.createElement("div"); | |
redditTabElem.innerHTML = "Reddit"; | |
document.querySelector("#w2g-sidebar-menu").appendChild(redditTabElem); | |
const redditTabContent = document.createElement("div"); | |
redditTabContent.className = "w2g-menu-tab"; | |
redditTabContent.id = "w2g-us-reddit"; | |
redditTabContent.innerHTML = | |
` | |
<form class="ui form" id="w2g-us-redditform"> | |
<div class="field"> | |
<label>Subreddit name</label> | |
<input placeholder="Enter subreddit name" id="w2g-us-subreddit"> | |
</div> | |
<div class="field"> | |
<label>Subreddit section</label> | |
<div class="ui input"> | |
<select class="ui fluid selection dropdown" id="w2g-us-subredditsection"> | |
<option name="hot">hot</option> | |
<option name="new">new</option> | |
<option name="rising">rising</option> | |
<option name="controversial">controversial</option> | |
<option name="top">top</option> | |
<option name="gilded">gilded</option> | |
</select> | |
</div> | |
</div> | |
<div class="field"> | |
<label>Number of posts</label> | |
<input placeholder="Number of posts to look through" autocomplete="off" id="w2g-us-postcount" value="100" type="number"> | |
</div> | |
<div class="field"> | |
<label>Upvotes</label> | |
<input placeholder="Minimum number of upvotes" autocomplete="off" id="w2g-us-minupvotes" value="100" type="number"> | |
</div> | |
<div class="loader-container"> | |
<button class="ui button" id="w2g-us-addtoplaylist-button" type="submit">Create new playlist</button> | |
<div id="w2g-us-redditplaylist-loader" class="loader-o3R47I" style="margin-left: 10px; display: none;"></div> | |
<div id="w2g-us-redditplaylist-status" style="margin-left: 10px; display: none"></div> | |
</div> | |
</form> | |
`; | |
document.querySelector(".w2g-content-right").appendChild(redditTabContent); | |
// load saved values if available | |
if (GM_SuperValue.get("last-subreddit", null) != null) { | |
redditTabContent.querySelector("#w2g-us-subreddit").value = GM_SuperValue.get("last-subreddit", null); | |
} | |
if (GM_SuperValue.get("last-section", null) != null) { | |
redditTabContent.querySelector("#w2g-us-subredditsection").value = GM_SuperValue.get("last-section", null); | |
} | |
if (GM_SuperValue.get("last-postcount", null) != null) { | |
redditTabContent.querySelector("#w2g-us-postcount").value = GM_SuperValue.get("last-postcount", null); | |
} | |
if (GM_SuperValue.get("last-minupvotes", null) != null) { | |
redditTabContent.querySelector("#w2g-us-minupvotes").value = GM_SuperValue.get("last-minupvotes", null); | |
} | |
redditTabElem.addEventListener("click", evt => { | |
document.querySelector(".w2g-active").classList.remove("w2g-active"); | |
redditTabContent.classList.add("w2g-active"); | |
redditTabElem.classList.add("w2g-active"); | |
}); | |
redditTabContent.querySelector("#w2g-us-redditform").addEventListener("submit", evt => { | |
evt.preventDefault(); | |
redditTabContent.querySelector("#w2g-us-subreddit").parentNode.classList.remove("error"); | |
redditTabContent.querySelector("#w2g-us-postcount").parentNode.classList.remove("error"); | |
redditTabContent.querySelector("#w2g-us-minupvotes").parentNode.classList.remove("error"); | |
let subreddit = redditTabContent.querySelector("#w2g-us-subreddit").value; | |
let section = redditTabContent.querySelector("#w2g-us-subredditsection").value; | |
let postcount = redditTabContent.querySelector("#w2g-us-postcount").value; | |
let minupvotes = redditTabContent.querySelector("#w2g-us-minupvotes").value; | |
if (subreddit == "") { | |
redditTabContent.querySelector("#w2g-us-subreddit").parentNode.classList.add("error"); | |
} | |
if (postcount == "") { | |
redditTabContent.querySelector("#w2g-us-postcount").parentNode.classList.add("error"); | |
} | |
if (minupvotes == "") { | |
redditTabContent.querySelector("#w2g-us-minupvotes").parentNode.classList.add("error"); | |
} | |
let date = new Date(); | |
let dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`; | |
// store preferences | |
GM_SuperValue.set("last-subreddit", subreddit); | |
GM_SuperValue.set("last-section", section); | |
GM_SuperValue.set("last-postcount", Number(postcount)); | |
GM_SuperValue.set("last-minupvotes", Number(minupvotes)); | |
let playlistName = `/r/${subreddit}/${section} ${dateStr}`; | |
createPlaylist(playlistName).then(pid => { | |
redditTabContent.querySelector("#w2g-us-redditplaylist-loader").style.display = "inline-block"; | |
redditTabContent.querySelector("#w2g-us-redditplaylist-status").style.display = "block"; | |
redditTabContent.querySelector("#w2g-us-redditplaylist-status").innerHTML = "Loading videos..."; | |
postMessage({ | |
type: "loadSubredditVideos", | |
subreddit: subreddit, | |
post_count: Number(postcount), | |
min_upvotes: Number(minupvotes), | |
section: section, | |
playlist: pid, | |
}); | |
}); | |
return false; | |
}); | |
// Userscript functionality | |
window.addEventListener("message", function(evt) { | |
var message; | |
try { | |
message = JSON.parse(evt.data); | |
} catch(e) {} | |
if(!message) return; | |
if(message.type === undefined) return; | |
switch (message.type) { | |
case "loadSubredditVideos": | |
loadSubredditVideos(message); | |
break; | |
case "addVideos": | |
redditTabContent.querySelector("#w2g-us-redditplaylist-status").innerHTML = "Adding videos to playlist..."; | |
addVideos(message); | |
break; | |
} | |
}, false); | |
function createPlaylist(full_name) { | |
let name = full_name.substr(0, 40); | |
// step 1: create new playlist | |
return $w2g.postJSON("/rooms/" + w2g.stream + "/playlists/sync_update", { new_list: name } ).then((r) => { | |
// step 2: retrieve list of all playlists to find out the id of our new playlist | |
return $w2g.getJSON("/rooms/" + w2g.stream + "/sync_state", "GET"); | |
}).then(resp => { | |
let state = resp.state; | |
let playlists = state.find(elem => elem[0] == "playlists"); | |
let items; | |
try { | |
items = playlists[1].payload.lists; | |
} catch(e) { | |
console.error(e); | |
alert("Received invalid response"); | |
return; | |
} | |
let item = items.find(i => i.title == name); | |
if (item == undefined) { | |
alert("Received invalid response"); | |
return; | |
} | |
return item.key; | |
}); | |
} | |
function addVideos(message) { | |
let entries = message.data; | |
let count = message.post_count; | |
let playlist = message.playlist; | |
let videolist = []; | |
for(let i = 0; i < count; i++) { | |
let entry = entries[i].data; | |
let media, media_type; | |
try { | |
media_type = entry.secure_media.type; | |
media = entry.secure_media.oembed; | |
} catch(e) { | |
// skip video without media info | |
continue; | |
} | |
if(media_type == "youtube.com" && entry.score >= message.min_upvotes) { | |
console.log("Add video:", entry.url, "(" + entry.score + ")"); | |
try { | |
videolist.push({ | |
title: decodeHtml(media.title) + ` (${entry.score})`, | |
url: entry.url, | |
thumb: media.thumbnail_url, | |
}); | |
} catch(e) {} | |
} | |
} | |
addVideolist(playlist, videolist).finally(() => { | |
document.querySelector("#w2g-us-redditplaylist-loader").style.display = "none"; | |
document.querySelector("#w2g-us-redditplaylist-status").style.display = "none"; | |
}); | |
} | |
async function addVideolist(playlist_id, videolist) { | |
// add videos to the playlist in chunks of 25 | |
for(let i = 0; i < videolist.length/25; i++) { | |
// call function provided by the website to add filtered videos | |
await $w2g.postJSON("/rooms/" + w2g.stream + "/playlists/" + playlist_id + "/playlist_items/sync_update", { add_items: JSON.stringify( videolist.slice(25*i, 25*i+25) ) } ); | |
} | |
} | |
// https://stackoverflow.com/a/42182294/1342618 | |
function decodeHtml(html) { | |
let txt = document.createElement("textarea"); | |
txt.innerHTML = html; | |
return txt.value; | |
} | |
function postMessage(messageObj) { | |
try { | |
let message = JSON.stringify(messageObj); | |
window.postMessage(message, "*"); | |
} catch(e) { | |
alert("Error while sending message"); | |
} | |
} | |
function loadSubredditVideos(message) { | |
let subreddit = message.subreddit; | |
let section = message.section; | |
let post_count = message.post_count; | |
let min_upvotes = message.min_upvotes; | |
let playlist = message.playlist; | |
requestMoreVideos(subreddit + "/" + section, post_count, min_upvotes, playlist); | |
} | |
function requestMoreVideos(subreddit, count, min_upvotes, playlist, videos, after) { | |
if(videos === undefined) videos = []; | |
after = (after === undefined) ? "" : "?after=" + after; | |
GM_xmlhttpRequest({ | |
url: "https://www.reddit.com/r/" + subreddit + ".json" + after, | |
method: "GET", | |
headers: { | |
"User-Agent": "watch2gether userscript Loader" | |
}, | |
ignoreCache: true, | |
responseType: "json", | |
onload: function(resp) { | |
if(resp.status != 200) return; | |
videos = videos.concat(resp.response.data.children); | |
redditTabContent.querySelector("#w2g-us-redditplaylist-status").innerHTML = `Loading videos (${videos.length})`; | |
if(videos.length >= count) { | |
postMessage({type: "addVideos", data: videos, min_upvotes: min_upvotes, post_count: count, playlist: playlist}); | |
} else { | |
requestMoreVideos(subreddit, count, min_upvotes, playlist, videos, resp.response.data.after); | |
} | |
}, | |
onerror: function() { | |
document.querySelector("#w2g-us-redditplaylist-loader").style.display = "none"; | |
document.querySelector("#w2g-us-redditplaylist-status").style.display = "none"; | |
} | |
}); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment