Video.mediaset.it native video player and direct links

This script allows you to watch and download videos on Mediaset Play.

  1. // ==UserScript==
  2. // @name Video.mediaset.it native video player and direct links
  3. // @name:it Mediaset Play - Link diretti e download video
  4. // @namespace http://andrealazzarotto.com
  5. // @description This script allows you to watch and download videos on Mediaset Play.
  6. // @description:it Ti permette di guardare e scaricare i video da Mediaset Play
  7. // @match https://mediasetinfinity.mediaset.it/*
  8. // @match https://*.mediasetinfinity.mediaset.it/*
  9. // @match https://*.mediasetplay.mediaset.it/*
  10. // @match https://www.mediaset.it/*
  11. // @version 7.1.3
  12. // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js
  13. // @require https://unpkg.com/@ungap/from-entries@0.1.2/min.js
  14. // @grant GM_xmlhttpRequest
  15. // @grant GM.xmlHttpRequest
  16. // @connect mediaset.it
  17. // @connect video.mediaset.it
  18. // @connect cdnselector.xuniplay.fdnames.com
  19. // @connect execute-api.eu-west-1.amazonaws.com
  20. // @connect theplatform.eu
  21. // @connect akamaized.net
  22. // @connect mediaset.net
  23. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  24. // ==/UserScript==
  25.  
  26. /* Greasemonkey 4 wrapper */
  27. if (typeof GM !== "undefined" && !!GM.xmlHttpRequest) {
  28. GM_xmlhttpRequest = GM.xmlHttpRequest;
  29. }
  30.  
  31. function fetch(params) {
  32. return new Promise(function(resolve, reject) {
  33. params.onload = resolve;
  34. params.onerror = reject;
  35. GM_xmlhttpRequest(params);
  36. });
  37. }
  38.  
  39. var base_selector = "http://link.theplatform.eu/s/PR1GhC/media/guid/2702976343/[[GUID]]?mbr=true&formats=[[FORMATS]]&format=SMIL&assetTypes=HD,HBBTV,widevine,geoIT%7CgeoNo:HD,HBBTV,geoIT%7CgeoNo:HD,geoIT%7CgeoNo:SD,HBBTV,widevine,geoIT%7CgeoNo:SD,HBBTV,geoIT%7CgeoNo:SD,geoIT%7CgeoNo";
  40. var loc = unsafeWindow.location;
  41. var isIframe = loc.href.indexOf("player/") > 0;
  42. var isPlay = loc.href.indexOf("mediasetplay.mediaset.it/video/") > 0;
  43.  
  44. function boxStyle(selector, color, textcolor) {
  45. $(selector).css({
  46. 'padding': '.5em',
  47. 'margin': '1em 4em',
  48. 'text-align': 'center',
  49. 'background-color': color,
  50. 'color': textcolor
  51. });
  52. $(selector + ' a').css('color', textcolor);
  53. $(selector + ' pre').css({
  54. 'white-space': 'pre-wrap',
  55. 'word-break': 'break-all',
  56. });
  57. $(selector + ' *').css('font-size', '15px');
  58. $(selector + ' small').css('font-size', '13px');
  59. }
  60.  
  61. function writeLive(stream) {
  62. $('#stream-url').remove();
  63. $('<div id="stream-url">').insertAfter($('#playerContainer').parent());
  64. $('#stream-url').append('<p>Flusso della diretta <strong>da aprire con VLC o <code>streamlink</code>:</strong></p>')
  65. .append('<pre><code><a href="' + stream + '">' + stream + '</a></code></pre>');
  66. boxStyle('#stream-url', 'rgba(255,255,255,0.5)', 'black');
  67.  
  68. // kill login timeout
  69. unsafeWindow.userNotLogged = function() { return; };
  70. setTimeout(function() {
  71. $('.countdown').remove();
  72. }, 1000);
  73. }
  74.  
  75. function handleLive(pageURI) {
  76. var baseURL = "https://api-ott-prod-fe.mediaset.net/PROD/play/alive/nownext/v1.0?channelId=";
  77. if (pageURI.indexOf('/diretta/') < 0) {
  78. return;
  79. }
  80. fetch({
  81. method: 'GET',
  82. url: baseURL + pageURI.split('/diretta/')[1].split('_c')[1],
  83. headers: {
  84. 'Accept': 'application/json'
  85. }
  86. }).then(function(responseDetails) {
  87. var r = responseDetails.responseText;
  88. var data = $.parseJSON(r);
  89. var instruction = data.response.tuningInstruction;
  90. for (var i = 0; i < 5; i++) {
  91. var row = instruction['urn:theplatform:tv:location:any'][i];
  92. var public = row.publicUrls[0];
  93. var streaming = row.streamingUrl;
  94. if (row.format.indexOf('x-mpegURL') > 0) {
  95. return fetch({
  96. method: 'GET',
  97. url: public,
  98. headers: {
  99. 'Accept': 'application/atom+xml,application/xml,text/xml'
  100. }
  101. });
  102. }
  103. }
  104. }).then(function(responseDetails) {
  105. var src = responseDetails.finalUrl;
  106. writeLive(src);
  107. });
  108. }
  109.  
  110. function getInformation(vlink) {
  111. vlink.url = vlink.url.replace(/vod08\.msf\.cdn/, 'vod05.msf.cdn');
  112. return fetch({
  113. method: 'HEAD',
  114. url: vlink.url
  115. }).then((response) => {
  116. if (response.status == 404) {
  117. let result = Object.assign({}, vlink);
  118. result.error = true;
  119. result.size = 0;
  120. return result;
  121. }
  122. let headers = fromEntries(response.responseHeaders.split("\n").map(element => element.trim().toLowerCase().split(":")));
  123. let size = +headers['content-length'];
  124. size = Math.round(size / 1024 / 1024);
  125. let result = Object.assign({}, vlink);
  126. result.error = false;
  127. result.size = size;
  128. return result;
  129. });
  130. }
  131.  
  132. var range = (start, stop, step) => {
  133. var a = [start], b = start;
  134. while (b < stop) {
  135. a.push(b += step || 1);
  136. }
  137. return (b > stop) ? a.slice(0,-1) : a;
  138. };
  139.  
  140. var leftPad = (str, padding, length) => {
  141. str = `${str}`;
  142. while (str.length < length) {
  143. str = padding + str;
  144. }
  145. return str;
  146. };
  147.  
  148. var scrapeQualities = async (url) => {
  149. var start = url.match(/[^_]*_[^\/]*/);
  150. if (!start) {
  151. return [];
  152. }
  153. start = start[0];
  154.  
  155. var suffix = start.match(/_.*/)[0].slice(1);
  156.  
  157. var m3u8 = await Promise.all(['ss', 'sd', 'hd'].map(index => {
  158. var quality_url = `${start}/hlsrc/${index}_no_mpl.m3u8`;
  159. return getInformation({ na: `M3U8 (${index})`, url: quality_url, h: false, quality: index });
  160. }));
  161.  
  162. var results = await Promise.all(['ss', 'sd', 'hd'].map(index => {
  163. var quality_url = `${start}/mp4/${index}_no_mpl.mp4`;
  164. return getInformation({ na: '.MP4', url: quality_url, h: true, quality: index });
  165. }));
  166.  
  167. return m3u8.concat(results).filter(value => !value.error);
  168. };
  169.  
  170. function getLinks(responseDetails) {
  171. var r = responseDetails.responseText;
  172. r = r.replace(/msf.ticdn.it/g, 'msf.cdn.mediaset.net');
  173. r = r.replace(/netfarmunica/g, 'net/farmunica');
  174. r = r.replace(/<video/g, '<element');
  175. var doc = $.parseHTML(r);
  176. var $xml = $( doc );
  177. var videos = $xml.find("element");
  178. var vlinks = [];
  179. console.log($xml[0].innerHTML);
  180.  
  181. var appended = {};
  182. // parse video URLs
  183. videos.each(function (i) {
  184. var url = $( videos.get(i) ).attr("src");
  185. var type = url.slice(-3);
  186. var suffix = (url.match(/\/(ss|sd|hd)_/) || [null, null])[1];
  187. var name = "";
  188. var highlight = false;
  189. switch(type) {
  190. case "est": name = "ISM"; break;
  191. case "3u8":
  192. case "pl)": name = suffix ? `M3U8 (${suffix})` : "M3U8"; break;
  193. case "mpd": return;
  194. case "flv": name = ".FLV"; break;
  195. case "f4v": name = ".F4V"; break;
  196. case "mp4": name = ".MP4"; highlight = true; break;
  197. case "wmv": name = ".WMV"; break;
  198. }
  199. var ending = url.slice(-20);
  200. if (!appended[ending]) {
  201. vlinks.push( { na: name, url: url, h: highlight, quality: suffix } );
  202. appended[ending] = true;
  203. }
  204. });
  205.  
  206. return vlinks;
  207. }
  208.  
  209. function displayURLs(vlinks) {
  210. var container = $('#playerContainer');
  211. if (!isIframe && !container.length) {
  212. return setTimeout(function() {
  213. displayURLs(vlinks);
  214. }, 1000);
  215. }
  216.  
  217. // Clean up traces
  218. $('#video-links, #video-links-actions').remove();
  219.  
  220. if (isIframe) {
  221. $('<div id="video-links">').appendTo('body');
  222. } else {
  223. container.parent().after('<div id="video-links">');
  224. }
  225.  
  226. // display video URLs
  227. Promise.all(vlinks.map(getInformation)).then(results => {
  228. var DRM = false;
  229. var scrapeURL = '';
  230. var hasHD = false;
  231. results.forEach((o, i) => {
  232. scrapeURL = o.url;
  233. if (o.quality == 'hd') {
  234. hasHD = true;
  235. }
  236. var s = '<a style="font-weight: ' + (o.h ? 'bold' : 'normal') + '" href="' + o.url + '">' + o.na + (o.size ? ' (' + o.quality + ' ' + o.size + ' MB)' : '') + '</a>';
  237. $(s).appendTo('#video-links');
  238. if (i != results.length-1) {
  239. $('<span>&nbsp;|&nbsp;</span>').appendTo('#video-links');
  240. }
  241. if (o.url.indexOf('sampleaes') > 0 && o.url.indexOf('.m3u8') > 0) {
  242. DRM = true;
  243. }
  244. });
  245. $('<br><small>I flussi M3U8 devono essere registrati da un programma esterno (es. JDownloader o streamlink)</small>').appendTo('#video-links');
  246.  
  247. if (DRM || (!hasHD)) {
  248. var deepScan = () => {
  249. $('#video-links strong').html('Scansione profonda in corso...');
  250.  
  251. scrapeQualities(scrapeURL).then(results => {
  252. $('#video-links').empty();
  253. if (results.length) {
  254. results.forEach((o, i) => {
  255. var s = '<a style="font-weight: ' + (o.h ? 'bold' : 'normal') + '" href="' + o.url + '">' + o.na + (o.size ? ' (' + o.quality + ' ' + o.size + ' MB)' : '') + '</a>';
  256. $(s).appendTo('#video-links');
  257. if (i != results.length-1) {
  258. $('<span>&nbsp;|&nbsp;</span>').appendTo('#video-links');
  259. }
  260. });
  261. $('<br><small>I flussi M3U8 devono essere registrati da un programma esterno (es. JDownloader o streamlink)</small>').appendTo('#video-links');
  262. } else {
  263. $('#video-links').html('<span>Questo video è protetto da DRM perciò <strong>non può essere scaricato.</strong></span>');
  264. }
  265. boxStyle('#video-links', 'rgba(0,0,0,0.5)', 'white');
  266. });
  267. };
  268.  
  269. deepScan();
  270. }
  271.  
  272. boxStyle('#video-links', 'rgba(0,0,0,0.5)', 'white');
  273. if (!isIframe) {
  274. $("#video-links").after("<div id='video-links-actions'></div>");
  275. $("#video-links-actions")
  276. .append('<center style="font-size: 75%">' +
  277. '<a href="https://lazza.me/DonazioneScript">Clicca qui per fare una donazione</a>' +
  278. '</center>')
  279. .find('a').css('text-decoration', 'underline');
  280. boxStyle('#video-links-actions', 'rgba(0,0,0,0.35)', 'white');
  281. }
  282.  
  283. if(isIframe) {
  284. $('#video-links').css({
  285. 'position': 'absolute',
  286. 'bottom': '6em',
  287. 'left': '5%',
  288. 'right': '5%',
  289. 'font-size': '.9em',
  290. 'z-index': '99999999'
  291. })
  292. .append("<span id='close'>&times;</span>");
  293. $("#close").css({
  294. 'font-weight': 'bold',
  295. 'position': 'absolute',
  296. 'top': '1em',
  297. 'right': '1em',
  298. 'cursor': 'pointer'
  299. }).click(function() {
  300. $("#video-links").fadeOut();
  301. });
  302. $('#video-links a').attr('target', '_blank');
  303. boxStyle('#video-links', 'white', 'black');
  304. }
  305. });
  306. }
  307.  
  308. function get_urls() {
  309. var params = new URLSearchParams(location.search.slice(1));
  310. var guid = (loc.href.match(/(FAFU000000([0-9]{4,6})|F[0-9A-F]{14,})/) || [null])[0] || params.get('programGuid') || params.get('id');
  311. if (!!guid && guid.indexOf('FAFU') == 0) {
  312. guid = guid.slice(-6);
  313. }
  314. $("#video-links, #video-links-actions").remove();
  315. if (!!guid) {
  316. console.log("GUID: " + guid);
  317. var promises = [];
  318. ['MPEG4', 'M3U'].forEach((format) => {
  319. promises.push(
  320. fetch({
  321. method: 'GET',
  322. url: base_selector.replace('[[GUID]]', guid).replace('[[FORMATS]]', format),
  323. headers: {
  324. 'Accept': 'application/atom+xml,application/xml,text/xml'
  325. }
  326. }).then(getLinks)
  327. );
  328. });
  329.  
  330. Promise.all(promises).then((sets) => {
  331. var flat = sets.reduce((acc, val) => acc.concat(val), []);
  332. displayURLs(flat);
  333. });
  334. }
  335. }
  336.  
  337. var old_href = "";
  338. var new_href = "";
  339.  
  340. function handle_everything() {
  341. handleLive(loc.href);
  342.  
  343. get_urls();
  344. }
  345.  
  346. $(document).ready(function(){
  347. setInterval(function() {
  348. old_href = new_href;
  349. new_href = loc.href;
  350. if (new_href != old_href) {
  351. handle_everything();
  352. }
  353. }, 1000);
  354. });