Last active
June 21, 2024 13:40
-
-
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!
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 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>`); | |
}); | |
} | |
})(); |
Hello, I installed ViolentMonkey on Chrome, but it seems not working for me. I saw there is another package requirement bottleneck@2.13.0. I'm not familiar with these .js scripts. would you mind sharing how to set up the whole flow to crawl materials in the Schoology? Thanks a lot!
@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
Ok, I guess I abandoned this last year, oh well.
I have installed GreaseMonkey, but nothing seems to happen after visiting schoology