Rai.tv native video player and direct links - LEGACY

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

安装此脚本?
作者推荐脚本

您可能也喜欢Rai Play video download

安装此脚本
  1. // ==UserScript==
  2. // @name Rai.tv native video player and direct links - LEGACY
  3. // @namespace http://andrealazzarotto.com
  4. // @description This script allows you to watch and download videos on Rai.tv.
  5. // @include http://www*.rai.*/dl/RaiTV/programmi/media/*
  6. // @include https://www*.rai.*/dl/RaiTV/programmi/media/*
  7. // @include http://www*.rai.*/dl/RaiTV/tematiche/*
  8. // @include https://www*.rai.*/dl/RaiTV/tematiche/*
  9. // @include http://www*.rai.*/dl/*PublishingBlock-*
  10. // @include https://www*.rai.*/dl/*PublishingBlock-*
  11. // @include http://www*.rai.*/dl/replaytv/replaytv.*
  12. // @include https://www*.rai.*/dl/replaytv/replaytv.*
  13. // @include http://rai.it/*
  14. // @include https://rai.it/*
  15. // @include http://*.rai.it/*
  16. // @include https://*.rai.it/*
  17. // @include http://www.rainews.it/dl/rainews/*
  18. // @include https://www.rainews.it/dl/rainews/*
  19. // @include http://www.rainews.it/tgr/*
  20. // @include https://www.rainews.it/tgr/*
  21. // @version 9.2.0
  22. // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js
  23. // @require https://unpkg.com/@ungap/from-entries@0.1.2/min.js
  24. // @grant GM_xmlhttpRequest
  25. // @grant GM.xmlHttpRequest
  26. // @connect rai.it
  27. // @connect rai.tv
  28. // @connect raiplay.it
  29. // @connect akamaized.net
  30. // @connect akamaihd.net
  31. // @connect msvdn.net
  32. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  33. // ==/UserScript==
  34.  
  35. /* Greasemonkey 4 wrapper */
  36. if (typeof GM !== "undefined" && !!GM.xmlHttpRequest)
  37. GM_xmlhttpRequest = GM.xmlHttpRequest;
  38.  
  39. function fetch(params) {
  40. return new Promise(function(resolve, reject) {
  41. params.onload = resolve;
  42. params.onerror = reject;
  43. GM_xmlhttpRequest(params);
  44. });
  45. }
  46.  
  47. var checkQuality = (url, rate) => {
  48. return fetch({
  49. method: 'GET',
  50. url: url,
  51. headers: {
  52. 'User-Agent': 'raiweb',
  53. 'Range': 'bytes=0-255',
  54. },
  55. }).then(
  56. (response) => {
  57. let headers = fromEntries(response.responseHeaders.split("\n").map(element => element.trim().toLowerCase().split(":")));
  58. let range = headers['content-range'] || '/0';
  59. let size = +(range.split('/').slice(1,)[0] || 0);
  60. let megabytes = Math.round(size / 1024 / 1024);
  61. if (size > 102400) {
  62. return { quality: rate, url: url, megabytes: megabytes };
  63. } else {
  64. return null;
  65. }
  66. },
  67. () => null
  68. );
  69. }
  70.  
  71. var qualities = async (url) => {
  72. let bases = [];
  73. let rates = [5000, 3200, 2401, 2400, 1800, 1500, 1200, 800, 600, 400];
  74. if (url.indexOf('.m3u8') > 0) {
  75. let parts = url.replace(/\?.*/, '').split(',');
  76. // Verify all the rates
  77. const new_rates = parts.slice(1).map(value => value.replace(/\/.*/, '')).reverse().filter(value => !isNaN(value));
  78. // Handle single rate case
  79. if (new_rates.length) {
  80. rates = new_rates;
  81. } else {
  82. let rate = url.split('.mp4')[0].split('_').slice(-1)[0];
  83. rates = [rate];
  84. }
  85. const path = parts[0];
  86. let servers = [
  87. 'creativemedia1.rai.it',
  88. 'creativemedia2.rai.it',
  89. 'creativemedia3.rai.it',
  90. 'creativemedia4.rai.it',
  91. 'creativemedia6-rai-it.akamaized.net',
  92. 'creativemedia7-rai-it.akamaized.net',
  93. 'download2.rai.it',
  94. 'download2-geo.rai.it',
  95. 'creativemediax1.rai.it',
  96. 'creativemediax2.rai.it',
  97. ];
  98. let file_path;
  99. if (path.indexOf('akamaized.net') > 0 || path.indexOf('akamaihd.net') > 0) {
  100. const path_parts = path.split('.net/');
  101. file_path = '/' + path_parts[1];
  102. } else {
  103. const path_parts = path.split('msvdn.net/');
  104. const first = path_parts[1].replace(/^[^0-9]+/, '');
  105. file_path = first.slice(1);
  106. }
  107. // Fix the "/i/" prefix
  108. if (file_path.slice(0, 3) === '/i/') {
  109. file_path = file_path.replace('/i/', '/');
  110. }
  111. file_path = file_path.replace(/_[1-9]+0?0[01]\.mp4.*/, '_');
  112. if (file_path.indexOf('.mp4') > 0) {
  113. file_path = file_path.replace(/\.mp4.*/, '');
  114. rates = [''];
  115. }
  116. bases = servers.map(server => {
  117. return `http://${server}${file_path}${rates[0]}.mp4`;
  118. });
  119. console.log(bases);
  120. } else {
  121. bases.push(url);
  122.  
  123. var ending = url.match(/_[1-9]+0?0[01]\.mp4/);
  124. if (!ending || !ending.length) {
  125. let result = await checkQuality(url, '');
  126. return [result].filter(value => (value !== null));
  127. }
  128. }
  129.  
  130. let promises = [];
  131. bases.forEach(url => {
  132. var promise = Promise.all(rates.map(rate => {
  133. var quality_url = url.replace(/_[1-9]+0?0[01]\.mp4/, `_${rate}.mp4`);
  134. return checkQuality(quality_url, rate);
  135. }));
  136. promises.push(promise);
  137. });
  138. const groups = await Promise.all(promises);
  139. for (let i = 0; i < groups.length; i++) {
  140. const filtered = groups[i].filter(value => (value !== null));
  141. if (filtered.length) {
  142. return filtered;
  143. }
  144. }
  145.  
  146. return [];
  147. };
  148.  
  149. var solveRelinker = (url) => {
  150. var secure = url.replace('http://', 'https://');
  151. return fetch({
  152. method: 'HEAD',
  153. url: secure,
  154. headers: {
  155. 'User-Agent': 'raiweb',
  156. },
  157. }).then(
  158. (response) => {
  159. let final = response.finalUrl;
  160. let valid = (final.indexOf('mp4') > 0 || final.indexOf('.m3u8') > 0) && final.indexOf('DRM_') < 0;
  161. if (valid) {
  162. return qualities(response.finalUrl).then(results => {
  163. return results[0].url;
  164. });
  165. }
  166. }
  167. );
  168. };
  169.  
  170. function playerElement() {
  171. var selectors = [
  172. "#Player.Player",
  173. "div.player-container",
  174. "div.Player:has(video)",
  175. "div.Player:has(embed)",
  176. "div.Player",
  177. "div#Player",
  178. "div#idPlayer",
  179. "div.videoContainer:has(iframe)",
  180. "div.mediaRaiTV:has(iframe)",
  181. "div.entry-content > div:has(iframe)",
  182. "div.video-iframe",
  183. "div.player-video",
  184. ];
  185.  
  186. for (var k in selectors) {
  187. var PL = $(selectors[k]).get(0);
  188. if(PL)
  189. return $( PL );
  190. }
  191.  
  192. return null;
  193. }
  194.  
  195. function appendMsg(text, PL) {
  196. if(!PL)
  197. PL = playerElement();
  198. $("#subcontent").remove();
  199. PL.append("<div id='subcontent'>" + text + "</div>");
  200. $("#subcontent").css({
  201. "padding": "5px",
  202. "color": "white",
  203. 'width': '60%',
  204. 'min-width': '18rem',
  205. 'max-width': '100%',
  206. "background": "rgba(0,0,0,0.5)",
  207. 'margin': '15px auto'
  208. });
  209. $("#subcontent p").css({
  210. 'margin': '.2em auto',
  211. 'padding': 0
  212. });
  213. $("#subcontent a").css({
  214. 'color': 'white',
  215. 'font-weight': 'bold',
  216. 'text-decoration': 'underline'
  217. });
  218. }
  219.  
  220. function suggestAlternative(relinker) {
  221. 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>");
  222. }
  223.  
  224. function placeHolder(url, kind, PL, remove) {
  225. remove = typeof remove !== 'undefined' ? remove : true;
  226.  
  227. if(!PL)
  228. PL = playerElement();
  229. if(remove)
  230. $("#direct-link").remove();
  231.  
  232. var wrapper = PL.closest('.video-player-wrapper');
  233. if (!wrapper.length) {
  234. wrapper = $('<div class="video-player-wrapper" />');
  235. PL.wrap(wrapper);
  236. };
  237. var direct = $('<div id="direct-link" />');
  238. $('.video-player-wrapper').append(direct);
  239. direct.css({
  240. 'padding': '5px',
  241. 'margin': '10px auto 15px',
  242. 'width': '60%',
  243. 'min-width': '18rem',
  244. 'max-width': '100%',
  245. 'border': '1px solid #888',
  246. 'text-align': 'center',
  247. 'box-shadow': '0px 5px 15px 0px rgba(0, 0, 0, .7)',
  248. 'background-color': '#cfc',
  249. })
  250. .append('<a href="'+url+'">' + kind + " Direct Link</a>");
  251. direct.find('a').css({
  252. 'font-size': '13px',
  253. 'font-weight': 'normal',
  254. 'color': 'black'
  255. });
  256.  
  257. wrapper.parent().css('overflow', 'visible');
  258.  
  259. appendMsg('<center>' +
  260. '<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>' +
  261. '&nbsp;&nbsp;—&nbsp;&nbsp;<a href="https://lazza.me/DonazioneScript">Fai una donazione</a>' +
  262. '</center>', $('.video-player-wrapper'));
  263. }
  264.  
  265. function setUP(url, kind) {
  266. if(kind.toLowerCase().indexOf("smooth") != -1 ||
  267. kind.toLowerCase().indexOf("csm") != -1)
  268. return;
  269.  
  270. // fix spaces
  271. url = url.split(' ').join('%20');
  272.  
  273. placeHolder(url, kind);
  274. }
  275.  
  276. function decide(videoURL, videoURL_MP4, estensioneVideo) {
  277. if (videoURL_MP4) {
  278. // handle the relinker client-side
  279. solveRelinker(videoURL_MP4).then(r => {
  280. setUP(r, "MP4");
  281. }).catch(() => {
  282. suggestAlternative(videoURL_MP4);
  283. });
  284. }
  285. else if (videoURL) {
  286. // handle the relinker client-side
  287. solveRelinker(videoURL).then(r => {
  288. if (r.substr(r.length - 4).substr(0,1) == '.') {
  289. estensioneVideo = r.substr(r.length - 3).toUpperCase();
  290. }
  291. setUP(r, estensioneVideo);
  292. }).catch(() => {
  293. suggestAlternative(videoURL);
  294. });
  295. } // end if (videoURL)
  296. }
  297.  
  298. function parseQuery(hash) {
  299. var result = {};
  300. var parts = hash.split("&");
  301. for(var i = 0; i<parts.length; i++) {
  302. var pair = parts[i].split("=");
  303. result[pair[0]] = pair[1];
  304. }
  305. return result;
  306. }
  307.  
  308. function setUpFromURL(url) {
  309. // get the original page content
  310. GM_xmlhttpRequest({
  311. method: 'GET',
  312. url: url,
  313. onload: function(responseDetails) {
  314. var r = responseDetails.responseText;
  315. // kill script tags to avoid execution (and errors!)
  316. r = r.replace(new RegExp('script', 'g'), 'dummy');
  317. var data = $('<div></div>').append(r).html();
  318.  
  319. var videoURL = null;
  320. var videoURL_MP4 = null;
  321. var estensioneVideo = null;
  322.  
  323. // set the correct variables
  324. try {
  325. videoURL = data.match(/videoURL = ["'](.*?)["']/)[1];
  326. }
  327. catch(e) {}
  328. try {
  329. videoURL = data.match(/data-video-url="([^"]*)"/)[1];
  330. }
  331. catch(e) {}
  332. try {
  333. videoURL_MP4 = data.match(/videoURL_MP4 = ["'](.*?)["']/)[1];
  334. }
  335. catch(e) {}
  336. try {
  337. estensioneVideo = data.match(/estensioneVideo = ["'](.*?)["']/)[1];
  338. }
  339. catch(e) {}
  340.  
  341. decide(videoURL, videoURL_MP4, estensioneVideo);
  342. }
  343. });
  344. }
  345.  
  346. $(document).ready(function(){
  347.  
  348. unsafeWindow.createPlayer = function() { return false; };
  349. $("body").append(`
  350. <style>
  351. iframe~.tagManagerError {
  352. display: none;
  353. }
  354.  
  355. @media screen and (max-width: 639px) {
  356. .videoOverlay {
  357. position: unset;
  358. height: auto;
  359. z-index: unset;
  360. padding-top: 0;
  361. padding-bottom: 0 !important;
  362. transform: scale(0.65);
  363. margin-left: -10vw;
  364. margin-right: -10vw;
  365. width: 120vw;
  366. transform-origin: top center;
  367. background: none;
  368. font-size: 6vw;
  369. }
  370. .videoOverlay *,
  371. .videoOverlay .button {
  372. font-size: inherit !important;
  373. }
  374. }
  375. </style>
  376. `);
  377.  
  378. var isRaiPlay = !!$(".Player[data-video-url]").length;
  379. var isReplay = !!$("script[src*='/replaytv.js'], script[src*='/replaytv.dev.js']").length;
  380. var isTematiche = window.location.href.indexOf("tematiche") > 0;
  381. var isPublishingBlock = window.location.href.indexOf("PublishingBlock") > 0;
  382. var isRubriche = window.location.href.indexOf("rubriche") > 0;
  383. var isTGR = window.location.href.indexOf("rainews.it/tgr") > 0;
  384. var isMultiple = (isTematiche || isPublishingBlock || isRubriche);
  385.  
  386. console.log("isRaiPlay: " + isRaiPlay);
  387. console.log("isReplay: " + isReplay);
  388. console.log("isTematiche: " + isTematiche);
  389. console.log("isPublishingBlock: " + isPublishingBlock);
  390. console.log("isRubriche: " + isRubriche);
  391. console.log("isMultiple: " + isMultiple);
  392. console.log("isTGR: " + isTGR);
  393.  
  394. var frames = $("iframe[src*='/dl/objects/embed.html'], iframe[src*='/dl/ray/'], " +
  395. "iframe[src*='/dl/Rai/'], iframe[src*='/dl/siti/'], iframe[src*='ContentItem'], iframe[src*='iframe/video/']");
  396.  
  397. if (isRaiPlay) {
  398. var PL = $(".Player[data-video-url]");
  399. var relinker = PL.data('video-url');
  400. console.log(relinker);
  401. solveRelinker(relinker).then(r => {
  402. var estensioneVideo = "";
  403. if (r.substr(r.length - 4).substr(0,1) == '.')
  404. estensioneVideo = r.substr(r.length - 3).toUpperCase();
  405. if(r.length > 0) {
  406. setUP(r, estensioneVideo, PL);
  407. }
  408. }).catch(() => {
  409. suggestAlternative(relinker);
  410. });
  411. }
  412.  
  413. if(!isMultiple && !isReplay &&
  414. (unsafeWindow.videoURL || unsafeWindow.videoURL_MP4)) {
  415. console.log("[Has video URL]");
  416.  
  417. var videoURL = $("meta[name=videourl]").attr("content");
  418. if(!videoURL)
  419. videoURL = unsafeWindow.videoURL;
  420. var videoURL_MP4 = $("meta[name=videourl_h264]").attr("content");
  421. if(!videoURL_MP4)
  422. videoURL_MP4 = unsafeWindow.videoURL_MP4;
  423. if(!videoURL_MP4)
  424. videoURL_MP4 = $("meta[name=videourl_mp4]").attr("content");
  425. var estensioneVideo = unsafeWindow.estensioneVideo;
  426. if(estensioneVideo)
  427. estensioneVideo = estensioneVideo.toUpperCase();
  428. else
  429. estensioneVideo = "Unknown";
  430. if(unsafeWindow.MediaItem.type == 'WMV')
  431. // avoid bug when estensioneVideo = CSM and MediaItem.type = WMV
  432. estensioneVideo = "WMV";
  433.  
  434. decide(videoURL, videoURL_MP4, estensioneVideo);
  435.  
  436. }
  437. // end Rai.tv "standard"
  438.  
  439. else if(frames.length && !isReplay && !isMultiple) {
  440. var url = frames.attr("src");
  441. if(url.indexOf("embed.html") > 0)
  442. url = "http://www.rai.tv" + url.replace(/.*embed.html\?/, "");
  443. if(url.indexOf("/iframe/video") > 0)
  444. url = url.replace("/iframe/video", "/video");
  445. setUpFromURL(url);
  446. }
  447.  
  448. // end iframes
  449.  
  450. else if(isTGR) {
  451. $("div.Player").each(function() {
  452. var relinker = $(this).attr('data-mediaurl');
  453. if (relinker) {
  454. setTimeout(function() {
  455. decide(relinker);
  456. $("div.Player").parent().css('padding', 0);
  457. }, 1000);
  458. }
  459. });
  460. }
  461.  
  462. else if(isMultiple && !isReplay) {
  463. if(unsafeWindow.videoURL) {
  464. document.videoURL = '';
  465. setInterval(function() {
  466. if(!playerElement())
  467. return;
  468. document.prevURL = document.videoURL;
  469. document.videoURL = unsafeWindow.videoURL;
  470. if(document.videoURL && (document.prevURL != document.videoURL)) {
  471. decide(document.videoURL);
  472. }
  473. }, 400);
  474. }
  475. else
  476. setInterval(function() {
  477. if(!playerElement())
  478. return;
  479. document.HprevId = document.Hid;
  480. document.Hid = $("div.Player").attr("data-id");
  481. console.log(document.Hid);
  482. if(!document.Hid)
  483. try {
  484. document.Hid = $("div.player-video iframe").attr("src").split("media/")[1].split(".html")[0];
  485. }
  486. catch(e) {}
  487.  
  488. // remove video list click events to allow opening of "real" pages
  489. // if not on "tematiche"
  490. if(!isTematiche) {
  491. $(".listaVideo a").unbind("click");
  492. }
  493. if(document.Hid && (document.Hid != document.HprevId)) {
  494. var completeURL = "http://www.rai.tv/dl/RaiTV/" +
  495. "programmi/media/" + document.Hid + ".html";
  496. setUpFromURL(completeURL);
  497. }
  498. }, 400);
  499. }
  500.  
  501. // end Tematiche
  502.  
  503. else if($("script:contains('draw')").length > 0 ||
  504. $("div.infoVideo").length > 0) {
  505. var videoURL = $("script:contains('draw')").text().split("'")[1];
  506. if(videoURL !== null && videoURL.indexOf("relinker") > 0) {
  507. GM_xmlhttpRequest({
  508. method: 'GET',
  509. url: videoURL,
  510. headers: {
  511. 'Accept': 'application/atom+xml,application/xml,text/xml'
  512. },
  513. onload: function(responseDetails) {
  514. var r = responseDetails.responseText;
  515. var doc = $.parseXML(r);
  516. var $xml = $( doc );
  517.  
  518. var url = $xml.find("REF").attr("HREF");
  519. url = url.replace("http://", "mms://");
  520.  
  521. setUP(url, "MMS Stream");
  522. }
  523. });
  524. }
  525. else if(videoURL !== null && videoURL.indexOf(".html") > 0) {
  526. setUpFromURL(videoURL);
  527. }
  528. else { // last try
  529. var PL = playerElement();
  530. var initParams = PL.find("param[name=initParams]").attr("value");
  531. if (initParams.indexOf("mediaUri") != -1) {
  532. var url = initParams.split("mediaUri=")[1].split(",")[0];
  533. decide(url, null, null); // decide will find the type
  534. }
  535. }
  536. }
  537.  
  538. // end pages like report.rai.it
  539.  
  540. // handle RTMP based flash objects on Rai.it
  541. $("object").not("object object").each(function() {
  542. var o = $(this);
  543. var flashvars = o.find("param[name=flashvars]").attr("value");
  544. if(!flashvars)
  545. flashvars = o.find("embed").attr("flashvars");
  546. if(!flashvars)
  547. flashvars = "";
  548. var path = flashvars.replace(/.*percorso[^=]*=/gi, "")
  549. .replace(/&.*/gi, "").replace(/\?.*/gi, "");
  550. if(path.toLowerCase().indexOf("rtmp")!=-1) {
  551. var url = path.replace('mp4:','').replace('rtmp','http')
  552. .replace('.mp4','') + '.mp4';
  553. placeHolder(url, "MP4", o, false);
  554. }
  555. });
  556. // end code for flash videos
  557.  
  558. // handle new pages with projekktor
  559. var pj;
  560. try {
  561. pj = unsafeWindow.projekktor();
  562. }
  563. catch (e) {
  564. pj = false;
  565. }
  566. if(pj) {
  567. var files = pj.media;
  568. var src = files[files.length - 1].file[0].src;
  569. var el = $('div.projekktor').parent();
  570. placeHolder(src, 'MP4', el);
  571. el.css('background', 'transparent');
  572. }
  573. // end projekktor
  574.  
  575. // handle WP-Video
  576. $('div[class^="wp-video"]').each(function() {
  577. var url = $(this).find('video').attr('src');
  578. placeHolder(url, "MP4", $(this), false);
  579. });
  580.  
  581. // handle jwplayer
  582. $('script:contains("jwplayer(")').each(function() {
  583. var content;
  584. try {
  585. content = $(this).text().split('sources:')[1].split('[{')[1].split('}]')[0];
  586. }
  587. catch (e) { return; }
  588. parts = content.split('file:').filter(function(x){return x.indexOf('m3u8') > 0;});
  589. if(parts.length) {
  590. var m3u8_url;
  591. try {
  592. m3u8_url = parts[0].split('"')[1];
  593. }
  594. catch (e) { return; }
  595. placeHolder(m3u8_url, 'M3U8 Stream', $(this).parent(), false);
  596. appendMsg(
  597. "<p>Ricordo che per registrare i video in formato M3U8 va utilizzato <code>ffmpeg</code>. " +
  598. "Maggiori informazioni <a href='http://lazza.me/1PLyi12'>cliccando qui</a>.</p>" +
  599. "<p style='text-align:right'>&mdash; Andrea</p>", $(this).parent());
  600. }
  601.  
  602. });
  603.  
  604. }); // end document.ready

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址