diff --git a/README.md b/README.md index 9a9b2b51278768422ff2c089e03823064c3e495e..58ff8937ce59267b5c8c90172ae5bf8f50b15c75 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Nevertheless, in the code it is called _Open Excellence Media_ or for short **O ## what is it good for? -Most videos (eg. lecture recordings) embedded in RWTH Aachen University's moodle are hosted on an OpenCast. +Most videos (eg. lecture recordings) embedded in RWTH Aachen University's moodle are hosted on an OpenCast instance. These videos are usually embedded using an iframe, which makes it (intentionally) hard to download them for offline use. This extension want's to help by liberating lecture recordings and other videos provided in this rather restrictive manner. @@ -55,15 +55,18 @@ Nevertheless it is possible to get extensions signed by Mozilla and self-distrib ### downloads, with caveats _\*sigh\*_ +tl;dr: Build it from source yourself, or use [the download/install site](https://dev.sudo42.de/public/ff-addons/oem.html).\ +Read below for slightly more in-depth information. + #### external site -You should (hopefully) be able to directly download the latest version from:\ +~~You should (hopefully) be able to directly download the latest version from:\ <https://dev.sudo42.de/public/ff-addons/openexcellencemedia-latest.xpi>\ **Caveat:** Simply clicking the link is unlikely to work. Firefox seems to block installations of extensions, if a link does not point to the same domain.\ -~~I'm sorry … please just copy it and open the page yourself.~~ +I'm sorry … please just copy it and open the page yourself.~~ -**Update (and potential solution for the caveat described above):** +**Update (and potential solution for the caveat described above):**\ Here is a simple page with the sole purpose of being on the same domain as the extension file it links to:\ <https://dev.sudo42.de/public/ff-addons/oem.html>\ (This should kinda work around the problem mentioned above. @@ -84,7 +87,7 @@ This project is licensed unser EUPL, so feel free to use, read, adapt, or share ## 2do - [X] going to bed - [ ] styling (I love CSS, not joking here! **Update:** A little bit of css is present. Should work for now.) - - [ ] adding links in video-only tabs, too + - [X] adding links in video-only tabs, too - [X] packaging - [X] good name + icon (kinda done … _"good"_ is highly subjective, but I will give it a pass) - [ ] further investigation into the OpenCast API (looks like one might be able to have some fun with it) diff --git a/free.css b/free.css index 684339a0966855ec6019472f8e62885ad4af7a9c..cf4521d91d3261320436563a4d5b805216e1231e 100644 --- a/free.css +++ b/free.css @@ -1,17 +1,47 @@ -._ext-openExcellenceMedia_container .videoLinkList { +._ext-openExcellenceMedia_container, +.openExcellenceMediaPlugin { --c-rwth-violett: rgb(97,33,88); --c-link-bg: var(--c-rwth-violett); --c-link-fg: #fff; } -._ext-openExcellenceMedia_container .videoLinkList { +.openExcellenceMediaPlugin.oemPopOut { + position: fixed; + top: 3.4em; + left: 2.1em; + right: 2.1em; + bottom: unset; + z-index: 42021; + text-align: center; + font-size: 1.1em; +} +._ext-openExcellenceMedia_container .videoLinkList, +.openExcellenceMediaPlugin .videoLinkList { list-style-type: none; padding: 0; display: flex; flex-direction: row; } +.openExcellenceMediaPlugin .videoLinkList { +/* flex-direction: column; + width: max-content; + align-items: stretch; + margin: .5em .25em; */ + margin: .5em auto; + width: max-content; + max-width: 100%; + padding: .5em .25em; + background: rgba(242, 242, 242, .7); + opacity: .7; + box-shadow: 0 0 1em 0 rgba(0,0,0,.7); +} +.openExcellenceMediaPlugin .videoLinkList:hover, +.openExcellenceMediaPlugin .videoLinkList:focus-within { + opacity: 1; +} -._ext-openExcellenceMedia_container .videoLinkList .videoLink { +._ext-openExcellenceMedia_container .videoLinkList .videoLink, +.openExcellenceMediaPlugin .videoLinkList .videoLink { display: inline-block; padding: .5em 1em; background: var(--c-link-bg); @@ -19,3 +49,7 @@ margin: .25em .5em; border-radius: .2em; } +/* .openExcellenceMediaPlugin .videoLinkList .videoLink { + display: block; + text-align: center; +} */ diff --git a/free.js b/free.js index 928a6a9e488b40e9ef797994298e4a69b31f351e..f9bcbff31bccfe66376d877e464cd0a41cc2355e 100644 --- a/free.js +++ b/free.js @@ -19,8 +19,8 @@ if (window.location.host === 'engage.streaming.rwth-aachen.de') { videoTracks.forEach(t=>console.debug(t.id, t.mimetype, t.video.resolution, t.url)) if (inFrame) { window.parent.postMessage({videoTracks, meta}, '*') - } else { - // TODO: add download links to video + } /*/ else /* */ { + addLinksToVideoPlayer(videoTracks, meta) } }) } else if (window.location.host === 'moodle.rwth-aachen.de') { @@ -76,15 +76,8 @@ const FMT = (()=>{ } })() -function genLinkContainer(tracks, meta) { +function genLinkList(tracks, meta) { let videoTitle = meta['search-results'].result.dcTitle || '?' - let details = document.createElement('details') - details.id = genVideoContainerId(meta) - details.classList.add('_ext-openExcellenceMedia_container') - let summary = document.createElement('summary') - summary.innerText = `Download video: ${videoTitle}` - details.appendChild(summary) - let videoLinkList = document.createElement('ul') videoLinkList.classList.add('videoLinkList') tracks.forEach(track => { @@ -102,7 +95,112 @@ Audio: ${mAud.channels}ch@${FMT.hz(mAud.samplingrate)} (${FMT.bps(mAud.bitrate)} li.appendChild(a) videoLinkList.appendChild(li) }) - details.appendChild(videoLinkList) + + return videoLinkList +} + +function genLinkContainer(tracks, meta) { + let videoTitle = meta['search-results'].result.dcTitle || '?' + let details = document.createElement('details') + details.id = genVideoContainerId(meta) + details.classList.add('_ext-openExcellenceMedia_container') + let summary = document.createElement('summary') + summary.innerText = `Download video: ${videoTitle}` + details.appendChild(summary) + details.appendChild(genLinkList(tracks, meta)) return details } + +const _PLAYER_CONTROL_BUTTON_ID = '_ext_openExcellence_btn-ctrl-bar' +function genPlayerControlsButton() { + let container = document.createElement('div'); + ['buttonPlugin', 'right', 'openExcellenceMediaPlugin'].forEach(c=>container.classList.add(c)) + container.id = _PLAYER_CONTROL_BUTTON_ID + container.setAttribute('title', 'Show downloadable video links'); + + let icon = document.createElement('i'); + ['button-icon', 'icon-download'].forEach(c=>icon.classList.add(c)) + container.appendChild(icon) + + return container +} + +const _PLAYER_CONTROL_POPOUT_ID = '_ext_openExcellence_ctr-bar-pop-out' +function genPlayerControlsPopOut(tracks, meta) { + let container = document.createElement('div'); + ['openExcellenceMediaPlugin', 'oemPopOut'].forEach(c=>container.classList.add(c)) + container.id = _PLAYER_CONTROL_POPOUT_ID + container.appendChild(genLinkList(tracks, meta)) + + return container +} + +function addLinksToVideoPlayer(videoTracks, meta) { + // removing old elements, if they are still here ... + document.querySelectorAll(`#${_PLAYER_CONTROL_BUTTON_ID}, #${_PLAYER_CONTROL_POPOUT_ID}`) + .forEach(n => n.remove()) + + console.debug('adding links to page ...', document.readyState) + + let toolbarBtn = genPlayerControlsButton() + let toolbarPopOut = genPlayerControlsPopOut(videoTracks, meta) + document.body.appendChild(toolbarPopOut) + console.debug(toolbarPopOut) + + let timeoutFn = ()=>{ + // waiting for things like the toolbar to be ready + if (!document.querySelector('.playbackControls')) { + setTimeout(timeoutFn, 250) + return + } + console.debug('page seems to be ready ... adding things ...') + +/* + // web extensions can not trivially access properties in the window object of the page context + // just using paella's API and adding a regular plugin for this would have been nice + console.debug(window, window.paella) + window.paella.addPlugin(()=>{ + return class OpenExcellenceMedia extends paella.ButtonPlugin { + getIndex() { return 42021; } + getAlignment() { return 'right' } + getSubclass() { return "openExcellenceMedia-ctrl-btn" } + getIconClass() { return 'icon-download' } + getName() { return "_.OpenExcellenceMedia.ff-ext" } + getButtonType() { return paella.ButtonPlugin.type.popUpButton } + buildContent(domElement) { + let linkList = genLinkList(videoTracks, meta) + domElement.appendChild(linkList) + domElement.classList.add('OpenExcellenceMediaPlugin') + } + + } + }) +*/ + + let open = false + let buttonBarContainer = document.querySelector('.playbackBarPlugins') + let popOutContainer = document.querySelector('.popUpPluginContainer') + buttonBarContainer.appendChild(toolbarBtn) + popOutContainer.appendChild(toolbarPopOut) + let _setOpen = (newState = null) => { + if (newState === undefined || newState === null) newState = !open + open = newState + let style = toolbarPopOut.style + if (open) { + style.display = null + toolbarBtn.classList.add('selected') + } else { + style.display = 'none' + toolbarBtn.classList.remove('selected') + } + } + _setOpen(false) + toolbarBtn.addEventListener('click', e=>{ + _setOpen() + }) + } + console.debug('wating for player interface to get ready ...') + timeoutFn() + +} diff --git a/manifest.json b/manifest.json index 2985996124b22578f3e72460559ca9bd26c01a7b..767a54ee2092d59f5d132f36ec3e949bcbf9bbee 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "OpenExcellenceMedia", - "version": "0.0.2", + "version": "0.0.3", "description": "Adds download links to videos on RWTH moodle which are served using an OpenCast iframe.", "homepage_url": "https://git.fsmpi.rwth-aachen.de/moodleOpenCastDownloads/ff-ext", "icons": {