From 2658a3234eea2d1a62963d40d961c9cbadaeb36b Mon Sep 17 00:00:00 2001 From: Benedikt Mautner Date: Fri, 20 Mar 2026 13:56:19 +0100 Subject: [PATCH 1/3] initial tuwel downloader commit --- tuwel-video-download.user.js | 102 +++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 tuwel-video-download.user.js diff --git a/tuwel-video-download.user.js b/tuwel-video-download.user.js new file mode 100644 index 0000000..2a59998 --- /dev/null +++ b/tuwel-video-download.user.js @@ -0,0 +1,102 @@ +// ==UserScript== +// @name Tuwel Video Download +// @namespace Violentmonkey Scripts +// @match https://tuwel.tuwien.ac.at/mod/opencast/view.php* +// @grant none +// @version 1.0 +// @author FSINF +// @description 3/14/2026, 5:18:42 PM +// ==/UserScript== + +function parseEpisodeStreams() { + const episode = window.episode; + const streams = + episode && Array.isArray(episode.streams) ? episode.streams : []; + const parsed = []; + + streams.forEach((stream, streamIndex) => { + const sources = stream && stream.sources ? stream.sources : {}; + const streamName = + stream && stream.content ? stream.content : `stream ${streamIndex + 1}`; + + const mp4 = Array.isArray(sources.mp4) ? sources.mp4 : []; + mp4.forEach((entry, entryIndex) => { + if (entry && entry.src) { + const w = entry.res && entry.res.w ? entry.res.w : "?"; + const h = entry.res && entry.res.h ? entry.res.h : "?"; + parsed.push({ + label: `${streamName} (${w}x${h})`, + src: entry.src, + }); + } + }); + }); + + return parsed.filter(function (item, index) { + return ( + parsed.findIndex(function (other) { + return other.src === item.src; + }) === index + ); + }); +} + +function startDownload(url) { + const a = document.createElement("a"); + a.href = url; + a.download = ""; + a.style.display = "none"; + document.body.appendChild(a); + a.click(); + a.remove(); +} + +function injectDownloadButton() { + let playerWrapper = document.querySelector(".player-wrapper"); + if (!playerWrapper) { + playerWrapper = document.querySelector(".page-context-header"); + if (!playerWrapper) { + return; + } + } + + const streams = parseEpisodeStreams(); + if (!streams.length) { + return; + } + + const container = document.createElement("div"); + container.style.marginBottom = "12px"; + + const select = document.createElement("select"); + select.style.marginRight = "8px"; + + streams.forEach((stream) => { + const option = document.createElement("option"); + option.value = stream.src; + option.textContent = stream.label; + select.appendChild(option); + }); + + const button = document.createElement("button"); + button.textContent = "Download selected stream"; + button.type = "button"; + button.addEventListener("click", function () { + const selectedOption = select.options[select.selectedIndex]; + if (!selectedOption) { + return; + } + startDownload(selectedOption.value); + }); + + container.appendChild(select); + container.appendChild(button); + + playerWrapper.parentNode.insertBefore(container, playerWrapper); +} + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", injectDownloadButton); +} else { + injectDownloadButton(); +} From e0cdeb6036be4c2a8b22fd9042b589268f4e1e2d Mon Sep 17 00:00:00 2001 From: Benedikt Mautner Date: Fri, 20 Mar 2026 14:00:33 +0100 Subject: [PATCH 2/3] . --- tuwel-video-download.user.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tuwel-video-download.user.js b/tuwel-video-download.user.js index 2a59998..f1112ea 100644 --- a/tuwel-video-download.user.js +++ b/tuwel-video-download.user.js @@ -14,6 +14,7 @@ function parseEpisodeStreams() { episode && Array.isArray(episode.streams) ? episode.streams : []; const parsed = []; + // extract mp4 sources from streams streams.forEach((stream, streamIndex) => { const sources = stream && stream.sources ? stream.sources : {}; const streamName = @@ -32,6 +33,7 @@ function parseEpisodeStreams() { }); }); + // remove duplicates return parsed.filter(function (item, index) { return ( parsed.findIndex(function (other) { @@ -54,6 +56,7 @@ function startDownload(url) { function injectDownloadButton() { let playerWrapper = document.querySelector(".player-wrapper"); if (!playerWrapper) { + // fallback for insertion of button playerWrapper = document.querySelector(".page-context-header"); if (!playerWrapper) { return; From c2a5595e1ccbab3aadbb34ba7c5f5c9161bec2c0 Mon Sep 17 00:00:00 2001 From: Benedikt Mautner Date: Fri, 20 Mar 2026 14:02:41 +0100 Subject: [PATCH 3/3] add download and update url --- tuwel-video-download.user.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tuwel-video-download.user.js b/tuwel-video-download.user.js index f1112ea..81207a1 100644 --- a/tuwel-video-download.user.js +++ b/tuwel-video-download.user.js @@ -1,11 +1,13 @@ // ==UserScript== // @name Tuwel Video Download -// @namespace Violentmonkey Scripts +// @namespace https://fsinf.at // @match https://tuwel.tuwien.ac.at/mod/opencast/view.php* // @grant none // @version 1.0 // @author FSINF // @description 3/14/2026, 5:18:42 PM +// @downloadURL https://fsinf.at/userscripts/tuwel-video-download.user.js +// @updateURL https://fsinf.at/userscripts/tuwel-video-download.user.js // ==/UserScript== function parseEpisodeStreams() {