Skip to content

Instantly share code, notes, and snippets.

@nicholastay
Last active June 21, 2024 13:40
Show Gist options
  • Save nicholastay/e6c108cc49bc297f50a1c417624e6601 to your computer and use it in GitHub Desktop.
Save nicholastay/e6c108cc49bc297f50a1c417624e6601 to your computer and use it in GitHub Desktop.
Schoology userscript - Quickly download files off folder browsers (press raw to install) - Be sure to allow popups!
// ==UserScript==
// @name Schoology quick-download files helper
// @namespace http://nicholastay.github.io/
// @version 0.3.0
// @author Nicholas Tay <nexerq@gmail.com>
// @license MIT
// @icon https://i.imgur.com/QmmYqzZ.png
// @match *://app.schoology.com/*
// @match *://schoology.cgs.vic.edu.au/*
// @grant none
// @homepage https://gist.github.com/nicholastay/e6c108cc49bc297f50a1c417624e6601
// @downloadURL https://gist.github.com/nicholastay/e6c108cc49bc297f50a1c417624e6601/raw/schoology-quickdl.user.js
// @noframes
// @require https://unpkg.com/bottleneck@2.13.0/es5.js
// ==/UserScript==
;(function() {
// lets try not to kill schoology: 10 per 5s
var limiter = new Bottleneck({
reservoir: 10,
reservoirRefreshAmount: 100,
reservoirRefreshInterval: 5 * 1000
});
injectCSS();
if (injectLabels())
injectDownloadAll();
injectLabelEventHandler();
injectSubfolderHandler();
injectGroupResources();
console.log("[nexerq/quickDL] Initialized.");
function noop() {}
function injectCSS() {
$("head").append("<style type=\"text/css\">.nexerq-quickdl-style:hover { text-decoration: underline; cursor: pointer; }</style>");
console.log("[nexerq/quickDL] CSS injected");
}
function injectLabels(subfolder) {
var $contentFiles = $("#folder-contents-table");
if (!$contentFiles || ($contentFiles.length < 1))
return console.log("[nexerq/quickDL] Not a folder listing page, not injecting that.");
console.log("[nexerq/quickDL] Found folder listings.");
var $documents = $contentFiles.find(".type-document:not(.nexerq-quickdl-processed)");
if (!$documents || ($documents.length < 1)) {
console.log("[nexerq/quickDL] No download documents found.");
return false;
}
console.log("[nexerq/quickDL] Found " + $documents.length + " documents to process");
$documents.each(function() {
var $this = $(this);
var $attachment = $this.find(".attachments-file-name");
var $link = $attachment.find("a");
// mark as processed
$this.addClass("nexerq-quickdl-processed");
// create dl btn
var $newBtn = $attachment.find(".attachments-file-size").clone().appendTo($attachment);
$newBtn.addClass("nexerq-quickdl-style");
$newBtn.addClass("nexerq-quickdl-link");
$newBtn.text("Download");
$newBtn.attr("orig-link", $link.attr("href"));
// weird subfolder bullshit
var $subContent = $link.find(".infotip-content");
$newBtn.attr("orig-name", ($subContent.length > 0 ? $subContent.text() : $link.text()));
if (subfolder) // detection for quickdl all - do not include these!
$newBtn.addClass("nexerq-quickdl-subfolder");
});
console.log("[nexerq/quickDL] Labels injected");
return true;
}
function injectLabelEventHandler() {
$(document).on("click", ".nexerq-quickdl-link", function() {
var $this = $(this);
var origName = $this.attr("orig-name");
var origLink = $this.attr("orig-link");
// kinda hacky: ajax http get the other page
var req = function() {
console.log("[nexerq/quickDL] Attempting to ajax get the target page (" + origName + ").");
limiter.submit($.ajax, origLink, {
success: function(data) {
var $dl = $(data).find(".attachments-file a:first");
if (!$dl || ($dl.length < 1)) {
console.log("[nexerq/quickDL] File could not be found in success: " + origName);
return alert("[nexerq/quickDL] Sorry, this file (" + origName + ") cannot be quick-downloaded at this time. You may have to just open the page and download manually.");
}
// dl
invokeDownload(origName, $dl.attr("href"));
},
error: function(jqXHR, textStatus, err) {
if (jqXHR.status === 429) {// 429 rate limited
console.log("[nexerq/quickDL] Rate limited 429 on " + origName + ". Retry in 10s");
setTimeout(req, 30000);
}
}
}, noop); // noop cb
};
req();
});
console.log("[nexerq/quickDL] Event handler registered");
}
function invokeDownload(fileName, url) {
console.log("[nexerq/quickDL] Downloading " + fileName + ".");
// ugh - https://stackoverflow.com/questions/18451856/how-can-i-let-a-user-download-multiple-files-when-a-button-is-clicked
//var tmpLink = document.createElement("a");
//tmpLink.style.display = "none";
//document.body.appendChild(tmpLink);
//tmpLink.setAttribute("href", url);
//tmpLink.setAttribute("download", fileName);
//tmpLink.click();
//document.body.removeChild(tmpLink);
window.open(url, fileName + " - nexerq/quickDL");
}
function injectDownloadAll() {
$(".materials-top").append(`<span class="attachments-file-size gray nexerq-quickdl-all nexerq-quickdl-style">Quick-download all (non-recursive)</span>`);
$(".nexerq-quickdl-all").click(function() {
console.log("[nexerq/quickDL] Downloading all available files...");
// oh dear... recursion
var $links = $(".nexerq-quickdl-link:not(.nexerq-quickdl-subfolder)");
$links.each(function() {
$(this).click();
});
});
console.log("[nexerq/quickDL] Download all injected");
}
function injectSubfolderHandler() {
// attach to document
$(document).on("click", ".folder-contents-cell .folder-expander", function() {
var $this = $(this);
console.log("[nexerq/quickDL] Subfolder open detected");
// wait for ajax to complete...
var waitCount = 0;
var $parentTd = $this.closest("td");
var wait = setInterval(function() {
var $subtree = $parentTd.children(".folder-subtree");
if ($subtree && $subtree.length > 0) {
clearInterval(wait);
if (injectLabels(true)) {
// inject dl all
$parentTd.find(".folder-title").first().append(`<span class="attachments-file-size gray nexerq-quickdl-all-subfolder nexerq-quickdl-style">Quick-download all</span>`);
$parentTd.find(".nexerq-quickdl-all-subfolder").click(function() {
$parentTd.find(".nexerq-quickdl-link").each(function() {
$(this).click();
});
});
console.log("[nexerq/quickDL] Injected subfolder event and dl all");
}
}
if (++waitCount > 5) {
console.log("[nexerq/quickDL] Subfolder open - something went wrong while waiting.");
clearInterval(wait);
}
}, 750);
});
console.log("[nexerq/quickDL] Event handler for subfolder handling injected");
}
// probably the worst implemented one
function injectGroupResources() {
if (!window.location.pathname.includes("/group") || !window.location.pathname.includes("/materials"))
return console.log("[nexerq/quickDL] Not a group resource page, not injecting that.");
// wait. AJAX PLS
var i = 0;
console.log("[nexerq/quickDL] Waiting for group page...");
var wait = setInterval(function() {
var $groupResources = $("#collection-view");
if (!$groupResources || ($groupResources.length < 1)) {
if (++i > 5) {
console.log("[nexerq/quickDL] Failed to find resources.");
clearInterval(wait);
}
return;
}
clearInterval(wait);
_injectGroupResources();
}, 750);
}
function _injectGroupResources() {
var $groupResources = $("#collection-view");
// event handler
$(document).on("click", ".nexerq-quickdl-group-link", function() {
var $resource = $(this).closest(".template-s-content-generic-post-docviewer");
var resId = $resource.attr("id").replace("t-", "");
var origName = $resource.find(".item-title").text();
// ajax get template, then the ID docviewer and parse its config
// ... ughhhhhhhhhh
console.log("[nexerq/quickDL] Trying group resource: " + origName);
limiter.submit($.get, window.location.origin + "/template/" + resId, function(templateData) {
console.log("[nexerq/quickDL] Got template data: " + origName);
// time to docviewer
limiter.submit($.get, window.location.origin + $(templateData).find(".docviewer-iframe").attr("src"), function(docData) {
var $configFunc = $(docData).find("script:contains('pdfPath')");
if (!$configFunc || ($configFunc.length < 1))
return console.log("[nexerq/quickDL] Could not find pdfPath config: " + origName);
var _docConfig = $configFunc.text().match(/PdfTronWebViewer, ({.*"}"}})/);
if (!_docConfig)
return console.log("[nexerq/quickDL] Could not parse pdfConfig: " + origName);
var docConfig = JSON.parse(_docConfig[1]);
var customConfig = JSON.parse(docConfig.options.custom);
invokeDownload(origName, customConfig.downloadLink);
});
});
});
var $resources = $groupResources.find(".template-s-content-generic-post-docviewer:not(.nexerq-quickdl-processed)");
if (!$resources || ($resources.length < 1))
return console.log("[nexerq/quickDL] No download-docviewers found.");
console.log("[nexerq/quickDL] Found " + $resources.length + " group resources to process");
$resources.each(function() {
var $resource = $(this);
$resource.addClass("nexerq-quickdl-processed");
$resource.find(".resource-details").append(`<span class="nexerq-quickdl-style nexerq-quickdl-group-link"> · Download</span>`);
});
}
})();
@soarn
Copy link

soarn commented Mar 1, 2021

@k2973363 if you still need to use the script, add this line in the ==UserScript== section:

// @match        *://*.schoology.com/*

This script was not originally made to run on all schoology.com domains, but that line will fix it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment