您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
This script gives you the direct links while watching a video on rsi.ch.
// ==UserScript== // @name RSI direct link // @namespace http://andrealazzarotto.com/ // @include http://rsi.ch/* // @include http://*.rsi.ch/* // @include https://rsi.ch/* // @include https://*.rsi.ch/* // @version 4.2.3 // @description This script gives you the direct links while watching a video on rsi.ch. // @copyright 2013+, Andrea Lazzarotto & GreenDragon - GPLv3 License // @author Andrea Lazzarotto, GreenDragon // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js // @grant GM_xmlhttpRequest // @grant GM.xmlHttpRequest // @connect il.srgssr.ch // @connect codww-vh.akamaihd.net // @connect cdn.rsi.ch // @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; } $('head').append("<style>.direct-links, .direct-links > a { color: white !important; font-weight: bold !important; } .direct-links > a:hover { text-decoration: underline !important; } .direct-links > .sep { color: yellow !important; } .direct-links-iframe, .direct-links-iframe > a { color: #af001d !important; font-weight: bold !important; } .direct-links-iframe > a:hover { text-decoration: underline !important; } .direct-links-iframe > .sep { color: white !important; }</style>"); var layerURL = "http://il.srgssr.ch/integrationlayer/1.0/ue/rsi/video/play/"; var render_piece = function (html) { var tree = $(html); if (!tree.length) return ''; var output = []; var lastColor = null; tree.find('span').each(function () { var element = $(this); var thisColor = element.attr('tts:color'); if (lastColor && lastColor != thisColor) output.push('\n-'); lastColor = thisColor; output.push(element.text().trim()); }); var joined = output.join(' '); joined = joined.replace(/\ +\n/, '\n').replace(/(^\n|\n$)/, ''); joined = joined.replace(/\n+/, '\n').replace(/\ +/, ' '); return joined; }; var render_part = function (html, id) { var tree = $(html); var begin = tree.attr('begin').replace('.', ','); var end = tree.attr('end').replace('.', ','); return id + '\n' + begin + ' --> ' + end + '\n' + render_piece(html); }; var handle_subtitles = function (subURL, element_id, title) { if (!subURL) return; if (subURL.toLowerCase().endsWith('srt')) { console.log("[handle_subtitles] Found SRT subtitles"); $('#' + element_id).append('<span class="sep"> | </span><a href="' + subURL + '" target="_blank">SRT</a>'); return; } GM_xmlhttpRequest({ method: 'GET', url: subURL, onload: function (responseDetails) { var r = responseDetails.responseText; var srt_content = ''; var sub_type = ''; if (subURL.toLowerCase().endsWith('vtt')) { console.log("[handle_subtitles] Found VTT subtitles"); sub_type = 'VTT'; srt_content = r .replace(/(WEBVTT.*)[\r\n]*/g, '') .replace(/(\d{2}:\d{2}:\d{2})\.(\d{3}\s+)\-\-\>(\s+\d{2}:\d{2}:\d{2})\.(\d{3}\s*)/g, '$1,$2-->$3,$4') .replace(/\<.+\>(.+)/g, '$1') .replace(/\<.+\>(.+)\<.+\/\>/g, '$1'); } else if (subURL.toLowerCase().endsWith('xml')) { console.log("[handle_subtitles] Found TTML subtitles"); sub_type = 'TTML'; var srt_list = []; var doc = $.parseXML(r); var $xml = $(doc); $xml.find('p').each(function (index, value) { srt_list.push(render_part(value.outerHTML, index + 1)); }); srt_content = srt_list.join('\n\n') } if (srt_content.length > 0) { $('#' + element_id) .append('<span class="sep"> | </span><a class="srt-link">SRT</a>') .append('<span class="sep"> | </span><a href="' + subURL + '" target="_blank">' + sub_type + '</a>') .find('.srt-link').attr('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(srt_content)).attr('download', 'sottotitoli.srt'); } } }); }; var setup_links = function (element, id, qualities, subtitles, box_class) { element.parent().find('.' + box_class).remove(); element.after('<div class="' + box_class + '" id="' + id + '"></div>'); for (var key = 0; key < qualities.length; key++) { var chunks = qualities[key].split(','); var last = chunks[chunks.length - 1]; var pos = parseInt(last.split('index_')[1]); var url = 'http://mediaww.rsi.ch/' + chunks[0].split('net/i/')[1] + chunks[pos + 1] + '.mp4'; console.log('[setup_links] Found direct URL -> ' + url); $('#' + id) .append('<span class="sep"> | </span>') .append('<a href="' + url + '">Link ' + (key + 1) + '</a>'); } handle_subtitles(subtitles, id); $('#' + id + ' span:first-child').remove(); $('#' + id + ' a').css('font-weight', 'bold'); $('#' + id).css({ 'margin': '.75em 0', 'box-sizing': 'border-box', 'width': 'auto', 'text-align': 'center' }); }; var setup_error = function (element, id, box_class) { element.parent().find('.' + box_class).remove(); element.after('<div class="' + box_class + '" id="' + id + '">Link non disponibili</div>'); console.log('[setup_error] Video not available'); $('#' + id + ' span:first-child').remove(); $('#' + id + ' a').css('font-weight', 'bold'); $('#' + id).css({ 'margin': '.75em 0', 'box-sizing': 'border-box', 'width': 'auto', 'text-align': 'center' }); }; var setup_video = function (id, target_element, box_class) { GM_xmlhttpRequest({ method: 'GET', url: layerURL + id.toString() + '.xml', onerror: function (responseDetails) { setup_error(target_element, selector, box_class); }, onload: function (responseDetails) { var r = responseDetails.responseText; var doc = $.parseXML(r); var $xml = $(doc); console.log("[setup_video] Loaded XML file for " + id); var manifestURL = $($xml.find('Playlist[protocol="HTTP-HLS"] > url').get(0)).text(); if (!!!manifestURL) { setup_error(target_element, selector, box_class); return; } console.log("[setup_video] Manifest URL: " + manifestURL); var subtitles = $($xml.find('TTMLUrl').get(0)).text(); if (!!subtitles) console.log("[setup_video] Subtitles URL: " + subtitles); else console.log("[setup_video] Subtitles not present"); var qualities = []; var chunks = manifestURL.split(','); for (var pos = 0; pos < chunks.length - 2; pos++) qualities.push(manifestURL.replace('master.m3u8', 'index_' + pos + '_av.m3u8')); var selector = 'link-' + id; setup_links(target_element, selector, qualities, subtitles, box_class); } }); }; var setup_frames = function () { // Handle iframe videos var dividers = [':', '%3A']; for (var i = 0; i < dividers.length; i++) { var divider = 'video' + dividers[i]; $("iframe[src*='" + divider + "']").each(function () { var frame = $(this); var id = parseInt(frame.attr('src').split(divider)[1].split('&')[0]); if (!!id && (frame.data('done') !== id)) { // Mark element immediately to avoid multiple requests frame.data('done', id); console.log("[setup_frames] Marking " + id + " as done"); var target_element = frame.parent(); if (!!$('.srg-showcase-video').length) { // Handle video iframe in showcase template console.log("[setup_frames] showcase template detected"); target_element = $('.content').first(); } else if (!!$('.mfp-desc').length) { // Handle video iframe in MagnificPopup $('.mfp-desc').first().prepend('<div id="empty-placeholder" style="display:none"></div>'); target_element = $('#empty-placeholder').first(); } setup_video(id, target_element, 'direct-links-iframe'); } }); // end each } }; var setup_html5 = function () { // Handle HTML5 video var video_obj = $("[aria-label='Player Video']").first(); var id; if (location.href.indexOf('id=') > 0) { id = parseInt(location.href.split("id=")[1].split('&')[0]); } else if (location.href.indexOf('urn=') > 0 && location.href.indexOf(':video:') > 0) { id = parseInt(location.href.split("urn=")[1].split('&')[0].split(':video:')[1]); } if (!!id && (video_obj.attr('data') !== id)) { // Mark element immediately to avoid multiple requests video_obj.attr('data', id); console.log("[setup_html5] Marking " + id + " as done"); setup_video(id, video_obj.parent(), 'direct-links'); } else { console.log("[setup_html5] Unsupported HTML5 video URL"); } }; $(document).arrive("iframe[src*='video:'], iframe[src*='video%3A']", {existing: true}, function () { console.log('[rsi_observer] IFrame video detected'); setup_frames(); }); $(document).arrive("[aria-label='Player Video']", {existing: true}, function () { if (location.href.indexOf('/video/') > 0) { console.log('[rsi_observer] HTML5 video detected'); setup_html5(); } else { console.log('[rsi_observer] Unsupported HTML5 video detected'); } });