Ö1 – Simple <audio> Stream and Download

Sendungen auf Ö1 schnell und einfach anhören und herunterladen

目前為 2015-10-03 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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.0.0
// @grant       unsafeWindow
// @run-at      document-start
// ==/UserScript==

var $A = function(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
	inject_content();
	
	var konsole_orig = unsafeWindow.konsole;
	
	// Define replacement function for stream window launcher
	var konsole = exportFunction(function(url) {
		// Determine track ID from URL
		var URLQuery = url.split("#")[0].split("?")[1];
		var track_id;
		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=") {
					track_id = parseInt(URLOption.substr(9));
					break;
				}
			};
		}
		
		// Guess which container contained the clicked stream
		var DOMStreamContainer = document.querySelector(".has-7tage > a[href='" + url.replace("'", "\\'") + "']");
		
		// Delegate to original handler if the target stream container could not be determined
		if(!track_id || !DOMStreamContainer) {
			console.log("UserScript: Failed to determine stream container or track ID for URL: " + url);
			
			return konsole_orig(url);
		}
		
		var DOMStreamContainer = DOMStreamContainer.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();
		}
		
		// 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 DOMAudio = document.createElement("audio");
			DOMAudio.controls = true;
			DOMAudio.preload  = "auto";
			DOMAudio.src      = playlist[0];
			DOMStreamController.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";
			
			// Start playback
			DOMAudio.play();
		});
		XHRPlaylist.open("GET", "http://oe1.orf.at/programm/" + track_id + "/playlist", true);
		XHRPlaylist.send();
	}, unsafeWindow);
	
	// Replace page read-only stream window launcher function
	Object.defineProperty(unsafeWindow, "konsole", {
		value: konsole
	});
});



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;
		}
		
		html.download-proxy > body > * {
			display: none !important;
		}
		
		html.download-proxy > body > .userscript-audio-download {
			display: block !important;
		}
		
		
		
		.userscript-audio-controller {
			display: block;
			margin-top:  0.5em;
			margin-left: 196px;
		}
		
		.userscript-audio-download {
			text-indent: -9999px;
			display: block;
			width: 64px;
			height: 32px;
			background: transparent url("//oe1.orf.at/static/img/ico-tile.png") repeat scroll -100px -1372px;
		}
		
		.overlay-download-liste {
			display: none !important;
		}
		
		
		
		#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);
}