Skip to content

Instantly share code, notes, and snippets.

@AlexTMjugador
Last active June 8, 2021 09:21
Show Gist options
  • Save AlexTMjugador/37815b997ffd48868a96067207aa5fea to your computer and use it in GitHub Desktop.
Save AlexTMjugador/37815b997ffd48868a96067207aa5fea to your computer and use it in GitHub Desktop.
Bad Apple in Moovi (Moodle)
/**
* The Bad Apple video URL. It will be used in the "src" attribute of the <video> element.
*/
var videoSrc = "http://127.0.0.1:8000";
/**
* Whether to show debug data or not.
*/
var debug = false;
/**
* If true, orange colors will be used for whites.
*/
var invertColors = false;
/**
* The selector for the parent element that contains the calendars.
*/
var calendarWidgetSelector = "aside#block-region-side-pre";
/**
* The video element that plays Bad Apple.
* @type {HTMLVideoElement}
*/
var videoElement = undefined;
/**
* The canvas element that will be used to read pixels from a video frame.
* @type {HTMLCanvasElement}
*/
var canvasElement = undefined;
/**
* The 2D rendering context of canvasElement.
* @type {CanvasRenderingContext2D}
*/
var canvasContext = undefined;
/**
* The latest video playback timestamp, in seconds.
* @type {number}
*/
var lastVideoTime = undefined;
/**
* The list of day elements that will be used to display the frame.
* @type {NodeList}
*/
var dayList = undefined;
var playing = false;
function updateFrame() {
// Bail out if the video has finished
if (videoElement.ended) {
playing = false;
return;
}
let time = videoElement.currentTime;
if (time !== lastVideoTime) {
// Drawing video of another origin to this canvas can fail due to security
// reasons. To avoid this, the CORS setting is set on the video element
canvasContext.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
// Raw frame pixels in RGBA format
let channels = canvasContext.getImageData(0, 0, canvasElement.width, canvasElement.height).data;
let j = 0;
for (let i = 0; i < channels.length; i += 4, ++j) {
let value = channels[i] + channels[i + 1] + channels[i + 2];
let isWhite = value > 381;
if (invertColors) {
isWhite = !isWhite;
}
if (isWhite) {
dayList[j].classList.remove("calendar_event_course");
} else {
dayList[j].classList.add("calendar_event_course");
}
}
lastVideoTime = time;
}
requestAnimationFrame(updateFrame);
}
async function badApple() {
// Already playing, bail out
if (playing) {
return;
}
videoElement = document.createElement("video");
videoElement.crossOrigin = "anonymous"; // Enable CORS
// The server should answer with video data with the appropriate MIME type and
// Access-Control-Allow-Origin header for everything to work
videoElement.src = videoSrc;
canvasElement = document.createElement("canvas");
canvasElement.width = 7; // Days in a week
canvasElement.height = document.querySelectorAll(calendarWidgetSelector + " table.calendartable tr[data-region='month-view-week']").length; // Weeks
canvasContext = canvasElement.getContext("2d");
dayList = document.querySelectorAll(calendarWidgetSelector + " table.calendartable td:is(.day, .dayblank)");
if (debug) {
// Show canvas content in document
canvasElement.style.width = "25%";
document.querySelector(calendarWidgetSelector).appendChild(canvasElement);
}
try {
await videoElement.play();
console.info("First frame update");
playing = true;
updateFrame();
} catch (err) {
alert(`Playback error: ${err}`);
}
}
// Start Bad Apple when a key is pressed.
// The whole script file can be executed directly in the JavaScript console, in the web developer tools.
document.addEventListener("keydown", badApple);
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
server {
listen 8000;
server_name localhost;
add_header Access-Control-Allow-Origin *;
location = / {
root html;
index '[Alstroemeria Records]Bad Apple!! feat.nomico(Shadow Animation Version)[ACVS.008_Tr.00].mp4';
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment