Rai.tv native video player and direct links - LEGACY

This script allows you to watch and download videos on Rai.tv.

Instalar este script¿?
Script recomendado por el autor

Puede que también te guste Rai Play video download.

Instalar este script
// ==UserScript==
// @name        Rai.tv native video player and direct links - LEGACY
// @namespace   http://andrealazzarotto.com
// @description This script allows you to watch and download videos on Rai.tv.
// @include     http://www*.rai.*/dl/RaiTV/programmi/media/*
// @include     https://www*.rai.*/dl/RaiTV/programmi/media/*
// @include     http://www*.rai.*/dl/RaiTV/tematiche/*
// @include     https://www*.rai.*/dl/RaiTV/tematiche/*
// @include     http://www*.rai.*/dl/*PublishingBlock-*
// @include     https://www*.rai.*/dl/*PublishingBlock-*
// @include     http://www*.rai.*/dl/replaytv/replaytv.*
// @include     https://www*.rai.*/dl/replaytv/replaytv.*
// @include     http://rai.it/*
// @include     https://rai.it/*
// @include     http://*.rai.it/*
// @include     https://*.rai.it/*
// @include     http://www.rainews.it/dl/rainews/*
// @include     https://www.rainews.it/dl/rainews/*
// @include     http://www.rainews.it/tgr/*
// @include     https://www.rainews.it/tgr/*
// @version     9.2.0
// @require     https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js
// @require     https://unpkg.com/@ungap/from-entries@0.1.2/min.js
// @grant       GM_xmlhttpRequest
// @grant       GM.xmlHttpRequest
// @connect     rai.it
// @connect     rai.tv
// @connect     raiplay.it
// @connect     akamaized.net
// @connect     akamaihd.net
// @connect     msvdn.net
// @license     GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
// ==/UserScript==

/* Greasemonkey 4 wrapper */
if (typeof GM !== "undefined" && !!GM.xmlHttpRequest)
  GM_xmlhttpRequest = GM.xmlHttpRequest;

function fetch(params) {
    return new Promise(function(resolve, reject) {
        params.onload = resolve;
        params.onerror = reject;
        GM_xmlhttpRequest(params);
    });
}

var checkQuality = (url, rate) => {
    return fetch({
        method: 'GET',
        url: url,
        headers: {
            'User-Agent': 'raiweb',
            'Range': 'bytes=0-255',
        },
    }).then(
        (response) => {
            let headers = fromEntries(response.responseHeaders.split("\n").map(element => element.trim().toLowerCase().split(":")));
            let range = headers['content-range'] || '/0';
            let size = +(range.split('/').slice(1,)[0] || 0);
            let megabytes = Math.round(size / 1024 / 1024);
            if (size > 102400) {
                return { quality: rate, url: url, megabytes: megabytes };
            } else {
                return null;
            }
        },
        () => null
    );
}

var qualities = async (url) => {
    let bases = [];
    let rates = [5000, 3200, 2401, 2400, 1800, 1500, 1200, 800, 600, 400];
    if (url.indexOf('.m3u8') > 0) {
        let parts = url.replace(/\?.*/, '').split(',');
        // Verify all the rates
        const new_rates = parts.slice(1).map(value => value.replace(/\/.*/, '')).reverse().filter(value => !isNaN(value));
        // Handle single rate case
        if (new_rates.length) {
            rates = new_rates;
        } else {
            let rate = url.split('.mp4')[0].split('_').slice(-1)[0];
            rates = [rate];
        }
        const path = parts[0];
        let servers = [
            'creativemedia1.rai.it',
            'creativemedia2.rai.it',
            'creativemedia3.rai.it',
            'creativemedia4.rai.it',
            'creativemedia6-rai-it.akamaized.net',
            'creativemedia7-rai-it.akamaized.net',
            'download2.rai.it',
            'download2-geo.rai.it',
            'creativemediax1.rai.it',
            'creativemediax2.rai.it',
        ];
        let file_path;
        if (path.indexOf('akamaized.net') > 0 || path.indexOf('akamaihd.net') > 0) {
            const path_parts = path.split('.net/');
            file_path = '/' + path_parts[1];
        } else {
            const path_parts = path.split('msvdn.net/');
            const first = path_parts[1].replace(/^[^0-9]+/, '');
            file_path = first.slice(1);
        }
        // Fix the "/i/" prefix
        if (file_path.slice(0, 3) === '/i/') {
            file_path = file_path.replace('/i/', '/');
        }
        file_path = file_path.replace(/_[1-9]+0?0[01]\.mp4.*/, '_');
        if (file_path.indexOf('.mp4') > 0) {
            file_path = file_path.replace(/\.mp4.*/, '');
            rates = [''];
        }
        bases = servers.map(server => {
            return `http://${server}${file_path}${rates[0]}.mp4`;
        });
        console.log(bases);
    } else {
        bases.push(url);

        var ending = url.match(/_[1-9]+0?0[01]\.mp4/);
        if (!ending || !ending.length) {
            let result = await checkQuality(url, '');
            return [result].filter(value => (value !== null));
        }
    }

    let promises = [];
    bases.forEach(url => {
        var promise = Promise.all(rates.map(rate => {
            var quality_url = url.replace(/_[1-9]+0?0[01]\.mp4/, `_${rate}.mp4`);
            return checkQuality(quality_url, rate);
        }));
        promises.push(promise);
    });
    const groups = await Promise.all(promises);
    for (let i = 0; i < groups.length; i++) {
        const filtered = groups[i].filter(value => (value !== null));
        if (filtered.length) {
            return filtered;
        }
    }

    return [];
};

var solveRelinker = (url) => {
    var secure = url.replace('http://', 'https://');
    return fetch({
        method: 'HEAD',
        url: secure,
        headers: {
            'User-Agent': 'raiweb',
        },
    }).then(
        (response) => {
            let final = response.finalUrl;
            let valid = (final.indexOf('mp4') > 0 || final.indexOf('.m3u8') > 0) && final.indexOf('DRM_') < 0;
            if (valid) {
                return qualities(response.finalUrl).then(results => {
                    return results[0].url;
                });
            }
        }
    );
};

function playerElement() {
	var selectors = [
        "#Player.Player",
        "div.player-container",
		"div.Player:has(video)",
		"div.Player:has(embed)",
		"div.Player",
		"div#Player",
		"div#idPlayer",
		"div.videoContainer:has(iframe)",
		"div.mediaRaiTV:has(iframe)",
		"div.entry-content > div:has(iframe)",
        "div.video-iframe",
		"div.player-video",
];

	for (var k in selectors) {
		var PL = $(selectors[k]).get(0);
		if(PL)
			return $( PL );
	}

	return null;
}

function appendMsg(text, PL) {
	if(!PL)
		PL = playerElement();
    $("#subcontent").remove();
	PL.append("<div id='subcontent'>" + text + "</div>");
	$("#subcontent").css({
		"padding": "5px",
		"color": "white",
		'width': '60%',
        'min-width': '18rem',
        'max-width': '100%',
		"background": "rgba(0,0,0,0.5)",
		'margin': '15px auto'
	});
	$("#subcontent p").css({
		'margin': '.2em auto',
		'padding': 0
	});
	$("#subcontent a").css({
		'color': 'white',
		'font-weight': 'bold',
        'text-decoration': 'underline'
	});
}

function suggestAlternative(relinker) {
	appendMsg("<p>Link diretto non disponibile. Prova a scaricarlo con <code>youtube-dl</code>:</p><pre><code>youtube-dl \"" + relinker + "\"</code></pre><p>Attenzione! Alcuni video si possono salvare solo dall'Italia.</p>");
}

function placeHolder(url, kind, PL, remove) {
	remove = typeof remove !== 'undefined' ? remove : true;

	if(!PL)
		PL = playerElement();
	if(remove)
		$("#direct-link").remove();

    var wrapper = PL.closest('.video-player-wrapper');
    if (!wrapper.length) {
        wrapper = $('<div class="video-player-wrapper" />');
        PL.wrap(wrapper);
    };
    var direct = $('<div id="direct-link" />');
    $('.video-player-wrapper').append(direct);
    direct.css({
			'padding': '5px',
			'margin': '10px auto 15px',
			'width': '60%',
        	'min-width': '18rem',
        	'max-width': '100%',
			'border': '1px solid #888',
			'text-align': 'center',
			'box-shadow': '0px 5px 15px 0px rgba(0, 0, 0, .7)',
			'background-color': '#cfc',
		})
        .append('<a href="'+url+'">' + kind + " Direct Link</a>");
	direct.find('a').css({
			'font-size': '13px',
			'font-weight': 'normal',
			'color': 'black'
		});

	wrapper.parent().css('overflow', 'visible');

    appendMsg('<center>' +
              '<iframe allowtransparency="true" style="width: 102px; height: 20px; position: relative; vertical-align: middle; display: inline-block;" src="https://www.facebook.com/v2.12/plugins/like.php?href=https%3A%2F%2Ffacebook.com%2FAndreaLazzarottoSoftware&layout=button_count&sdk=joey&share=false&show_faces=false" frameborder="0"></iframe>' +
              '&nbsp;&nbsp;—&nbsp;&nbsp;<a href="https://lazza.me/DonazioneScript">Fai una donazione</a>' +
              '</center>', $('.video-player-wrapper'));
}

function setUP(url, kind) {
	if(kind.toLowerCase().indexOf("smooth") != -1 ||
			kind.toLowerCase().indexOf("csm") != -1)
		return;

	// fix spaces
	url = url.split(' ').join('%20');

	placeHolder(url, kind);
}

function decide(videoURL, videoURL_MP4, estensioneVideo) {
	if (videoURL_MP4) {
		// handle the relinker client-side
        solveRelinker(videoURL_MP4).then(r => {
            setUP(r, "MP4");
        }).catch(() => {
            suggestAlternative(videoURL_MP4);
        });
	}
	else if (videoURL) {
		// handle the relinker client-side
        solveRelinker(videoURL).then(r => {
            if (r.substr(r.length - 4).substr(0,1) == '.') {
                estensioneVideo = r.substr(r.length - 3).toUpperCase();
            }
            setUP(r, estensioneVideo);
        }).catch(() => {
            suggestAlternative(videoURL);
        });
	} // end if (videoURL)
}

function parseQuery(hash) {
	var result = {};
	var parts = hash.split("&");
	for(var i = 0; i<parts.length; i++) {
		var pair = parts[i].split("=");
		result[pair[0]] = pair[1];
	}
	return result;
}

function setUpFromURL(url) {
	// get the original page content
	GM_xmlhttpRequest({
		method: 'GET',
		url: url,
		onload: function(responseDetails) {
			var r = responseDetails.responseText;
			// kill script tags to avoid execution (and errors!)
			r = r.replace(new RegExp('script', 'g'), 'dummy');
			var data = $('<div></div>').append(r).html();

			var videoURL = null;
			var videoURL_MP4 = null;
			var estensioneVideo = null;

			// set the correct variables
			try {
				videoURL = data.match(/videoURL = ["'](.*?)["']/)[1];
			}
			catch(e) {}
            try {
                videoURL = data.match(/data-video-url="([^"]*)"/)[1];
            }
            catch(e) {}
			try {
				videoURL_MP4 = data.match(/videoURL_MP4 = ["'](.*?)["']/)[1];
			}
			catch(e) {}
			try {
				estensioneVideo = data.match(/estensioneVideo = ["'](.*?)["']/)[1];
			}
			catch(e) {}

			decide(videoURL, videoURL_MP4, estensioneVideo);
		}
	});
}

$(document).ready(function(){

	unsafeWindow.createPlayer = function() { return false; };
    $("body").append(`
    <style>
        iframe~.tagManagerError {
            display: none;
        }

        @media screen and (max-width: 639px) {
            .videoOverlay {
                position: unset;
                height: auto;
                z-index: unset;
                padding-top: 0;
                padding-bottom: 0 !important;
                transform: scale(0.65);
                margin-left: -10vw;
                margin-right: -10vw;
                width: 120vw;
                transform-origin: top center;
                background: none;
                font-size: 6vw;
            }
            .videoOverlay *,
            .videoOverlay .button {
                font-size: inherit !important;
            }
        }
    </style>
    `);

    var isRaiPlay = !!$(".Player[data-video-url]").length;
	var isReplay = !!$("script[src*='/replaytv.js'], script[src*='/replaytv.dev.js']").length;
	var isTematiche = window.location.href.indexOf("tematiche") > 0;
	var isPublishingBlock = window.location.href.indexOf("PublishingBlock") > 0;
	var isRubriche = window.location.href.indexOf("rubriche") > 0;
    var isTGR = window.location.href.indexOf("rainews.it/tgr") > 0;
	var isMultiple = (isTematiche || isPublishingBlock || isRubriche);

    console.log("isRaiPlay: " + isRaiPlay);
	console.log("isReplay: " + isReplay);
	console.log("isTematiche: " + isTematiche);
	console.log("isPublishingBlock: " + isPublishingBlock);
	console.log("isRubriche: " + isRubriche);
	console.log("isMultiple: " + isMultiple);
    console.log("isTGR: " + isTGR);

	var frames = $("iframe[src*='/dl/objects/embed.html'], iframe[src*='/dl/ray/'], " +
			"iframe[src*='/dl/Rai/'], iframe[src*='/dl/siti/'], iframe[src*='ContentItem'], iframe[src*='iframe/video/']");

	if (isRaiPlay) {
		var PL = $(".Player[data-video-url]");
		var relinker = PL.data('video-url');
		console.log(relinker);
        solveRelinker(relinker).then(r => {
            var estensioneVideo = "";
            if (r.substr(r.length - 4).substr(0,1) == '.')
                estensioneVideo = r.substr(r.length - 3).toUpperCase();
            if(r.length > 0) {
                setUP(r, estensioneVideo, PL);
            }
        }).catch(() => {
            suggestAlternative(relinker);
        });
	}

	if(!isMultiple && !isReplay &&
		(unsafeWindow.videoURL || unsafeWindow.videoURL_MP4)) {
        console.log("[Has video URL]");

		var videoURL = $("meta[name=videourl]").attr("content");
		if(!videoURL)
			videoURL = unsafeWindow.videoURL;
		var videoURL_MP4 = $("meta[name=videourl_h264]").attr("content");
		if(!videoURL_MP4)
			videoURL_MP4 = unsafeWindow.videoURL_MP4;
		if(!videoURL_MP4)
			videoURL_MP4 = $("meta[name=videourl_mp4]").attr("content");
		var estensioneVideo = unsafeWindow.estensioneVideo;
		if(estensioneVideo)
			estensioneVideo = estensioneVideo.toUpperCase();
		else
			estensioneVideo = "Unknown";
		if(unsafeWindow.MediaItem.type == 'WMV')
			// avoid bug when estensioneVideo = CSM and MediaItem.type = WMV
			estensioneVideo = "WMV";

		decide(videoURL, videoURL_MP4, estensioneVideo);

	}
	// end Rai.tv "standard"

	else if(frames.length && !isReplay && !isMultiple) {
		var url = frames.attr("src");
		if(url.indexOf("embed.html") > 0)
			url = "http://www.rai.tv" + url.replace(/.*embed.html\?/, "");
        if(url.indexOf("/iframe/video") > 0)
            url = url.replace("/iframe/video", "/video");
		setUpFromURL(url);
	}

	// end iframes

    else if(isTGR) {
        $("div.Player").each(function() {
            var relinker = $(this).attr('data-mediaurl');
            if (relinker) {
                setTimeout(function() {
                    decide(relinker);
                    $("div.Player").parent().css('padding', 0);
                }, 1000);
            }
        });
    }

	else if(isMultiple && !isReplay) {
		if(unsafeWindow.videoURL) {
			document.videoURL = '';
			setInterval(function() {
				if(!playerElement())
					return;
				document.prevURL = document.videoURL;
				document.videoURL = unsafeWindow.videoURL;
				if(document.videoURL && (document.prevURL != document.videoURL)) {
					decide(document.videoURL);
				}
			}, 400);
		}
		else
			setInterval(function() {
				if(!playerElement())
					return;
				document.HprevId = document.Hid;
				document.Hid = $("div.Player").attr("data-id");
                console.log(document.Hid);
				if(!document.Hid)
					try {
						document.Hid = $("div.player-video iframe").attr("src").split("media/")[1].split(".html")[0];
					}
					catch(e) {}

				// remove video list click events to allow opening of "real" pages
				// if not on "tematiche"
				if(!isTematiche) {
					$(".listaVideo a").unbind("click");
				}
				if(document.Hid && (document.Hid != document.HprevId)) {
					var completeURL = "http://www.rai.tv/dl/RaiTV/" +
						"programmi/media/" + document.Hid + ".html";
					setUpFromURL(completeURL);
				}
			}, 400);
	}

	// end Tematiche

	else if($("script:contains('draw')").length > 0 ||
			$("div.infoVideo").length > 0) {
		var videoURL = $("script:contains('draw')").text().split("'")[1];
		if(videoURL !== null && videoURL.indexOf("relinker") > 0) {
			GM_xmlhttpRequest({
				method: 'GET',
				url: videoURL,
				headers: {
					'Accept': 'application/atom+xml,application/xml,text/xml'
				},
				onload: function(responseDetails) {
					var r = responseDetails.responseText;
					var doc = $.parseXML(r);
					var $xml = $( doc );

					var url = $xml.find("REF").attr("HREF");
					url = url.replace("http://", "mms://");

					setUP(url, "MMS Stream");
				}
			});
		}
		else if(videoURL !== null && videoURL.indexOf(".html") > 0) {
			setUpFromURL(videoURL);
		}
		else { // last try
			var PL = playerElement();
			var initParams = PL.find("param[name=initParams]").attr("value");
			if (initParams.indexOf("mediaUri") != -1) {
				var url = initParams.split("mediaUri=")[1].split(",")[0];
				decide(url, null, null); // decide will find the type
			}
		}
	}

	// end pages like report.rai.it

	// handle RTMP based flash objects on Rai.it
	$("object").not("object object").each(function() {
		var o = $(this);
		var flashvars = o.find("param[name=flashvars]").attr("value");
		if(!flashvars)
			flashvars = o.find("embed").attr("flashvars");
		if(!flashvars)
			flashvars = "";
		var path = flashvars.replace(/.*percorso[^=]*=/gi, "")
				.replace(/&.*/gi, "").replace(/\?.*/gi, "");
		if(path.toLowerCase().indexOf("rtmp")!=-1) {
			var url = path.replace('mp4:','').replace('rtmp','http')
						.replace('.mp4','') + '.mp4';
			placeHolder(url, "MP4", o, false);
		}
	});
	// end code for flash videos

	// handle new pages with projekktor
	var pj;
	try {
		pj = unsafeWindow.projekktor();
	}
	catch (e) {
		pj = false;
	}
	if(pj) {
		var files = pj.media;
		var src = files[files.length - 1].file[0].src;
		var el = $('div.projekktor').parent();
		placeHolder(src, 'MP4', el);
		el.css('background', 'transparent');
	}
	// end projekktor

	// handle WP-Video
	$('div[class^="wp-video"]').each(function() {
		var url = $(this).find('video').attr('src');
		placeHolder(url, "MP4", $(this), false);
	});

	// handle jwplayer
	$('script:contains("jwplayer(")').each(function() {
		var content;
		try {
			content = $(this).text().split('sources:')[1].split('[{')[1].split('}]')[0];
		}
		catch (e) { return; }
		parts = content.split('file:').filter(function(x){return x.indexOf('m3u8') > 0;});
		if(parts.length) {
			var m3u8_url;
			try {
				m3u8_url = parts[0].split('"')[1];
			}
			catch (e) { return; }
			placeHolder(m3u8_url, 'M3U8 Stream', $(this).parent(), false);
			appendMsg(
				"<p>Ricordo che per registrare i video in formato M3U8 va utilizzato <code>ffmpeg</code>. " +
				"Maggiori informazioni <a href='http://lazza.me/1PLyi12'>cliccando qui</a>.</p>" +
				"<p style='text-align:right'>&mdash; Andrea</p>", $(this).parent());
		}

	});

}); // end document.ready