Ö1 – Simple <audio> Stream and Download

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

当前为 2015-10-03 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 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);
}