This script allows you to watch and download videos on Rai.tv.
// ==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/[email protected]/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 / #### / ####); if (size > ####00) { 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 r###lt = await checkQuality(url, ''); return [r###lt].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(r###lts => { return r###lts[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>' + ' — <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 r###lt = {}; var parts = hash.split("&"); for(var i = 0; i<parts.length; i++) { var pair = parts[i].split("="); r###lt[pair[0]] = pair[1]; } return r###lt; } 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'>— Andrea</p>", $(this).parent()); } }); }); // end document.ready