您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Sendungen auf Ö1 schnell und einfach anhören und herunterladen
当前为
// ==UserScript== // @name Ö1 – Simple <audio> Stream and Download // @description Sendungen auf Ö1 schnell und einfach anhören und herunterladen // @namespace https://xmine127.tk/gm/ // @include *://oe1.orf.at/* // @include *://loopstream01.apa.at/*#DOWNLOAD=* // @version 1.1.0 // @grant unsafeWindow // @run-at document-start // ==/UserScript== function $A(collection) { return Array.prototype.slice.call(collection, 0); }; function create_download_link(attributes) { var DOMDownloadLink = document.createElement("a"); for(var attribute in attributes) { if(attributes.hasOwnProperty(attribute)) { DOMDownloadLink[attribute] = attributes[attribute]; } } DOMDownloadLink.className = "userscript-audio-download"; DOMDownloadLink.appendChild(document.createTextNode("Download")); return DOMDownloadLink; } if(window.location.hash.startsWith("#DOWNLOAD=")) { // Download link iframe page var downloadAttributes = JSON.parse(decodeURIComponent(window.location.hash.substr(10))); document.addEventListener("DOMContentLoaded", function() { // Inject style information inject_content(); // Mark this page's content as not-important document.documentElement.className = "download-proxy"; // Add the (important) download link document.body.appendChild(create_download_link(downloadAttributes)); }, true); return; } document.addEventListener("DOMContentLoaded", function() { // Inject extra page nodes and styles inject_content(); // Detect if page URL inherently points to some stream var pageTrackID = window.location.pathname.match(/^\/programm\/(\d+).*$/); if(pageTrackID instanceof Array) { pageTrackID = parseInt(pageTrackID[1]); } // Define replacement function for stream window launcher var konsole_orig = unsafeWindow.konsole; var konsole = exportFunction(function(url, trackRestore) { var DOMPlayButton; var trackID; if(pageTrackID) { // Find play area on program pages DOMPlayButton = document.querySelector(".galleryitem > .overlay-7tage"); // Assume that the page's inherent track should be played // (although parsing the requested URL would work too) trackID = pageTrackID; } else { // Guess the play button used to play the stream based on the received URL DOMPlayButton = document.querySelector(".has-7tage > a[href='" + url.replace("'", "\\'") + "']"); // Determine track ID from URL requested by the caller var URLQuery = url.split("#")[0].split("?")[1]; if(URLQuery) { var URLOptions = URLQuery.split("&"); for(var i = 0; i < URLOptions.length; i++) { var URLOption = URLOptions[i]; if(URLOption.substr(0, 9) == "track_id=") { trackID = parseInt(URLOption.substr(9)); break; } }; } } // Delegate to original handler if the target stream container could not be determined if(!trackID || !DOMPlayButton) { console.log("UserScript: Failed to determine stream container or track ID for URL: " + url); return konsole_orig(url); } var DOMStreamContainer = DOMPlayButton.parentNode.parentNode; // Generate title string from stream information var DOMStreamTitle = DOMStreamContainer.querySelector(".textbox > h3"); var DOMStreamDate = DOMStreamContainer.querySelector(".textbox > .datum"); var title = ""; if(DOMStreamTitle) { title += DOMStreamTitle.textContent.trim(); if(title.substr(title.length - 1) == "*") { title = title.substr(0, title.length - 1).trim(); } } if(DOMStreamDate) { title += (title != "") ? " vom " : ""; title += DOMStreamDate.textContent.split("|")[1].trim(); } // Hide "Play episode" button(s) $A(DOMPlayButton.parentNode.querySelectorAll(".overlay-playbutton")).forEach(function(DOMPlayButton) { DOMPlayButton.style.display = "none"; }); // Find or add main container below container with information text var DOMStreamController = DOMStreamContainer.getElementsByClassName("userscript-audio-controller")[0]; if(!DOMStreamController) { DOMStreamController = document.createElement("div"); DOMStreamController.className = "userscript-audio-controller"; DOMStreamContainer.appendChild(DOMStreamController); } // Show progress spinner in controller area var DOMLoadingSpinner = document.getElementById("fountainG"); DOMLoadingSpinner.style.display = "block"; DOMStreamController.appendChild(DOMLoadingSpinner); // Download playlist file for stream var XHRPlaylist = new XMLHttpRequest(); XHRPlaylist.addEventListener("load", function() { if(XHRPlaylist.status != 200 || !XHRPlaylist.responseXML) { var status = XHRPlaylist.status + " " + XHRPlaylist.statusText; console.log("UserScript: Could not retrieve playlist file: " + status); return konsole_orig(url); } // Create list of audio tracks in stream playlist var playlist = []; $A(XHRPlaylist.responseXML.querySelectorAll("playlist > track")).forEach(function(XMLTrack) { var url = XMLTrack.getAttribute("url"); if(url) { playlist.push(url); } }); // Create <audio> tag (with UI) for episode var DOMAudioContainer = document.createElement("div"); DOMAudioContainer.className = "userscript-audio-container"; DOMStreamController.appendChild(DOMAudioContainer); var DOMAudio = document.createElement("audio"); DOMAudio.controls = true; DOMAudio.preload = "auto"; DOMAudio.src = playlist[0]; DOMAudioContainer.appendChild(DOMAudio); // Create download link var downloadAttributes = { href: playlist[0], download: title, target: "_new", type: "audio/mpeg" }; if(navigator.product == "Gecko") { // Create <iframe> that contains download link on <audio> domain (thanks Mozilla...) var DOMDownloadFrame = document.createElement("iframe"); DOMDownloadFrame.className = "userscript-audio-download"; DOMDownloadFrame.src = "//loopstream01.apa.at/welcome.aspx#DOWNLOAD=" + JSON.stringify(downloadAttributes); DOMStreamController.appendChild(DOMDownloadFrame); } else { // Other browser either support the "download" attribute – or they don't… // The one's that don't, won't support streaming the content and will therefor offer it for download // regardless... :-) DOMStreamController.appendChild(create_download_link(downloadAttributes)); } // Hide progress spinner DOMLoadingSpinner.style.display = "none"; // Track current playback position // (so that playback can be continued after page navigation) function setPlaybackHandler() { // Do not update state while stream hasn't initialized yet if(DOMAudio.readyState < 2) { return; } // Store state information for this stream try { var storage = window.localStorage; var keyName = "userscript" + "." + trackID; if(!DOMAudio.ended) { storage.setItem(keyName, JSON.stringify({ playing: !DOMAudio.paused, currentTime: DOMAudio.currentTime })); } else { storage.removeItem(keyName); } } catch(e) { console.log(e); /* Handle full storage gracefully */ } } DOMAudio.addEventListener("play", setPlaybackHandler); DOMAudio.addEventListener("playing", setPlaybackHandler); DOMAudio.addEventListener("seeked", setPlaybackHandler); DOMAudio.addEventListener("timeupdate", setPlaybackHandler); DOMAudio.addEventListener("pause", setPlaybackHandler); DOMAudio.addEventListener("ended", setPlaybackHandler); // Seek to requested position (once the stream has initialized) DOMAudio.addEventListener("loadedmetadata", function() { // Read shared playback information var state = {}; try { var storage = window.localStorage; var keyName = "userscript" + "." + trackID; state = JSON.parse(storage.getItem(keyName)); } catch(e) { console.log(e); /* Handle failed JSON.parse() gracefully */ } if(typeof(state.currentTime) === "number") { DOMAudio.currentTime = state.currentTime; } if(state.playing !== true && trackRestore) { DOMAudio.pause(); } }); // Start playback DOMAudio.play(); }); XHRPlaylist.open("GET", "http://oe1.orf.at/programm/" + trackID + "/playlist", true); XHRPlaylist.send(); }, unsafeWindow); // Replace page read-only stream window launcher function Object.defineProperty(unsafeWindow, "konsole", { value: konsole }); // Auto-start playback, if the user was previously playing the stream of the current page if(pageTrackID) { konsole(null, true); } }); function inject_content() { // Inject extra HTML tags for loading indicator var DOMLoadingSpinner = document.createElement("div"); DOMLoadingSpinner.id = "fountainG"; DOMLoadingSpinner.style.display = "none"; for(var i = 1; i <= 8; i++) { var DOMLoadingItem = document.createElement("div"); DOMLoadingItem.id = "fountainG_" + i; DOMLoadingItem.className = "fountainG"; DOMLoadingSpinner.appendChild(DOMLoadingItem); } document.body.appendChild(DOMLoadingSpinner); // Inject extra page styles for loading indicator var DOMStylesheet = document.createElement("style"); DOMStylesheet.type = "text/css"; DOMStylesheet.innerHTML = (function () {/* html.download-proxy, html.download-proxy > body { margin: 0 !important; padding: 0 !important; height: 100%; } html.download-proxy > body > * { display: none !important; } html.download-proxy > body > .userscript-audio-download { display: block !important; } .userscript-audio-controller { display: inline-block; margin-top: 0.5em; margin-left: 196px; height: 32px; } .userscript-audio-download { text-indent: -9999px; width: 64px; background: #999494 url("//oe1.orf.at/static/img/ico-tile.png") repeat scroll -100px -1372px; } .userscript-audio-container { background-color: #5C5959; padding-right: 0.5em; } .userscript-audio-container, .userscript-audio-download { display: inline-block; vertical-align: top; height: 100%; } .userscript-audio-controller audio { background-color: white; } .overlay-download-liste, .galleryitem > .hover-infobar, .galleryitem > .overlay-download { display: none !important; } .gallery > .userscript-audio-container { margin-top: 0; } #fountainG{ position:relative; width:84px; height:10px; margin:auto; } .fountainG{ position:absolute; top:0; background-color:rgb(0,0,0); width:10px; height:10px; animation-name:bounce_fountainG; -o-animation-name:bounce_fountainG; -ms-animation-name:bounce_fountainG; -webkit-animation-name:bounce_fountainG; -moz-animation-name:bounce_fountainG; animation-duration:1.5s; -o-animation-duration:1.5s; -ms-animation-duration:1.5s; -webkit-animation-duration:1.5s; -moz-animation-duration:1.5s; animation-iteration-count:infinite; -o-animation-iteration-count:infinite; -ms-animation-iteration-count:infinite; -webkit-animation-iteration-count:infinite; -moz-animation-iteration-count:infinite; animation-direction:normal; -o-animation-direction:normal; -ms-animation-direction:normal; -webkit-animation-direction:normal; -moz-animation-direction:normal; transform:scale(.3); -o-transform:scale(.3); -ms-transform:scale(.3); -webkit-transform:scale(.3); -moz-transform:scale(.3); border-radius:7px; -o-border-radius:7px; -ms-border-radius:7px; -webkit-border-radius:7px; -moz-border-radius:7px; } #fountainG_1{ left:0; animation-delay:0.6s; -o-animation-delay:0.6s; -ms-animation-delay:0.6s; -webkit-animation-delay:0.6s; -moz-animation-delay:0.6s; } #fountainG_2{ left:10px; animation-delay:0.75s; -o-animation-delay:0.75s; -ms-animation-delay:0.75s; -webkit-animation-delay:0.75s; -moz-animation-delay:0.75s; } #fountainG_3{ left:21px; animation-delay:0.9s; -o-animation-delay:0.9s; -ms-animation-delay:0.9s; -webkit-animation-delay:0.9s; -moz-animation-delay:0.9s; } #fountainG_4{ left:31px; animation-delay:1.05s; -o-animation-delay:1.05s; -ms-animation-delay:1.05s; -webkit-animation-delay:1.05s; -moz-animation-delay:1.05s; } #fountainG_5{ left:42px; animation-delay:1.2s; -o-animation-delay:1.2s; -ms-animation-delay:1.2s; -webkit-animation-delay:1.2s; -moz-animation-delay:1.2s; } #fountainG_6{ left:52px; animation-delay:1.35s; -o-animation-delay:1.35s; -ms-animation-delay:1.35s; -webkit-animation-delay:1.35s; -moz-animation-delay:1.35s; } #fountainG_7{ left:63px; animation-delay:1.5s; -o-animation-delay:1.5s; -ms-animation-delay:1.5s; -webkit-animation-delay:1.5s; -moz-animation-delay:1.5s; } #fountainG_8{ left:73px; animation-delay:1.64s; -o-animation-delay:1.64s; -ms-animation-delay:1.64s; -webkit-animation-delay:1.64s; -moz-animation-delay:1.64s; } @keyframes bounce_fountainG{ 0%{ transform:scale(1); background-color:rgb(0,0,0); } 100%{ transform:scale(.3); background-color:rgb(255,255,255); } } @-o-keyframes bounce_fountainG{ 0%{ -o-transform:scale(1); background-color:rgb(0,0,0); } 100%{ -o-transform:scale(.3); background-color:rgb(255,255,255); } } @-ms-keyframes bounce_fountainG{ 0%{ -ms-transform:scale(1); background-color:rgb(0,0,0); } 100%{ -ms-transform:scale(.3); background-color:rgb(255,255,255); } } @-webkit-keyframes bounce_fountainG{ 0%{ -webkit-transform:scale(1); background-color:rgb(0,0,0); } 100%{ -webkit-transform:scale(.3); background-color:rgb(255,255,255); } } @-moz-keyframes bounce_fountainG{ 0%{ -moz-transform:scale(1); background-color:rgb(0,0,0); } 100%{ -moz-transform:scale(.3); background-color:rgb(255,255,255); } } */}).toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1]; document.head.appendChild(DOMStylesheet); }