您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Skrypt służący do pobierania materiałów ze znanych serwisów VOD.
// ==UserScript== // @name Skrypt umożliwiający pobieranie materiałów ze znanych serwisów VOD. // @version 7.2.4 // @description Skrypt służący do pobierania materiałów ze znanych serwisów VOD. // Działa poprawnie tylko z rozszerzeniem Tampermonkey. // Cześć kodu pochodzi z: // miniskrypt.blogspot.com, // miniskrypt.hubaiitv.pl // @author Przmus, zacny // @namespace http://www.ipla.tv/ // @source https://github.com/zacny/voddownloader // @include /^https://(vod|cyfrowa)\.tvp\.pl/video/.*$/ // @include /^https?://.*\.tvp.(pl|info)/sess/TVPlayer2/embed.*$/ // @include /^https?://((?!wiadomosci).)*\.tvp\.pl/\d{6,}/.*$/ // @include https://www.tvpparlament.pl/sess/* // @include https://www.ipla.tv/* // @include https://player.pl/* // @include https://*.cda.pl/* // @include https://vod.pl/* // @include https://redir.atmcdn.pl/* // @include https://*.redcdn.pl/file/o2/redefine/partner/* // @include https://partner.ipla.tv/embed/* // @include https://wideo.wp.pl/* // @include https://ninateka.pl/* // @include https://www.arte.tv/*/videos/* // @include https://pulsembed.eu/* // @include https://tv-trwam.pl/local-vods/* // @exclude http://www.tvp.pl/sess/* // @exclude /^https?://(bialystok|gorzow|krakow|olsztyn|rzeszow|wroclaw|bydgoszcz|katowice|lublin|opole|szczecin|gdansk|kielce|lodz|poznan|warszawa)\.tvp.\pl/.*$/ // @exclude /^https?://.*\.vod\.tvp\.pl/\d{6,}/.*$/ // @exclude https://www.cda.pl/iframe/* // @grant GM_getResourceText // @grant GM_xmlhttpRequest // @grant GM_download // @grant GM_setClipboard // @grant GM_info // @connect tvp.pl // @connect getmedia.redefine.pl // @connect distro.redefine.pl // @connect player-api.dreamlab.pl // @connect api.arte.tv // @connect b2c.redefine.pl // @connect player.pl // @connect api-trwam.app.insysgo.pl // @run-at document-end // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/platform/1.3.5/platform.min.js // @require https://gitcdn.xyz/cdn/zacny/voddownloader/4b17a120f521eaddf476d6e8fe3be152d506f244/lib/js/mdb-with-waves-patch.js // @require https://gitcdn.xyz/cdn/kapetan/jquery-observe/ca67b735bb3ae8d678d1843384ebbe7c02466c61/jquery-observe.js // @resource buttons_css https://raw.githubusercontent.com/zacny/voddownloader/master/lib/css/voddownloader-buttons.css // @resource content_css https://raw.githubusercontent.com/zacny/voddownloader/master/lib/css/voddownloader-content.css // ==/UserScript== (function vodDownloader($, platform, Waves) { 'use strict'; var Exception = (function(error, templateParams) { this.error = error; this.templateParams = Array.isArray(templateParams) ? templateParams : [templateParams]; }); var Tool = (function(Tool) { Tool.deleteParametersFromUrl = function(url){ return decodeURIComponent(url.replace(/\?.*/,'')); }; Tool.getUrlParameter = function(paramName, url){ var results = new RegExp('[\?&]' + paramName + '=([^&#]*)').exec(url); if (results==null) { return null; } return decodeURIComponent(results[1]) || 0; }; Tool.formatConsoleMessage = function(message, params){ console.log.apply(this, $.merge([message], params)); }; var removeNotAllowedFileNameChars = function(input){ var regexp = new RegExp('[\/:*?"<>|]+', 'g'); return input.replace(regexp, '').replace(/\s+/g, ' '); }; Tool.downloadFile = function(fileUrl, title){ var extension = Tool.deleteParametersFromUrl(fileUrl.split('.').pop()); var movieTitle = (title !== undefined && title !== '' ) ? title : 'nieznany'; var name = removeNotAllowedFileNameChars(movieTitle) + '.' + extension; GM_download(fileUrl, name); }; Tool.template = function(templates, ...keys){ return (function(...values) { var dict = values[values.length - 1] || {}; var result = [templates[0]]; keys.forEach(function(key, i) { var value = Number.isInteger(key) ? values[key] : dict[key]; result.push(value, templates[i + 1]); }); return result.join(''); }); }; Tool.getRealUrl = function(){ var topUrl = window.sessionStorage.getItem(config.storage.topWindowLocation); return topUrl !== null ? topUrl : window.location.href; }; Tool.isTopWindow = function(){ return window.top === window.self; }; Tool.pad = function(number, characters){ return(1e15+number+"").slice(-characters) }; Tool.mapDescription = function(data){ var defaults = $.extend({}, config.description.defaults); var sourceDescriptions = config.description.sources[data.source] || {}; var descriptionVariant = sourceDescriptions[data.key] || {}; return $.extend(true, defaults, data, descriptionVariant); }; return Tool; }(Tool || {})); const config = { attempts: 10, attemptTimeout: 1500, storage: { doNotWarn: 'voddownloader.doNotwarnIfIncorrectPluginSettingsDetected', topWindowLocation: 'voddownloader.topWindowLocation' }, urlParamPattern: '#', urlParamDefaultKey: 'videoId', urlPartPattern: '~', include: { fontawesome: { id: 'fontawesome', css: 'https://use.fontawesome.com/releases/v5.8.2/css/all.css' }, bootstrap: { id: 'bootstrap', css: 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css' }, mdb: { id: 'mdb', css: 'https://cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.8.2/css/mdb.min.css', } }, error: { id: { caption: 'Nie udało się odnaleźć idetyfikatora.', template: Tool.template`Algorytm rozpoznawania identyfikatora wideo na stronie: ${0} \ zakończył się niepowodzeniem. Może to oznaczać błąd skryptu lub zmiany w portalu.`, }, tvnId: { caption: 'Nie udało się odnaleźć idetyfikatora.', template: Tool.template`Algorytm rozpoznawania identyfikatora wideo na stronie: ${0} \ zakończył się niepowodzeniem.\nJeżeli jest to główna strona programu oznacza to, \ że nie udało się odnaleźć identyfikatora ostatniego odcinka. Wejdź na stronę odcinka \ i spróbuj ponownie.\nMoże to również oznaczać błąd skryptu lub zmiany w portalu.`, }, call: { caption: 'Błąd pobierania informacji o materiale.', template: Tool.template`Wystąpił błąd w wykonaniu skryptu w kroku: ${0} na stronie: ${1} \ Zgłoś problem autorom skryptu.`, }, noSource: { caption: 'Nie udało się odnaleźć metadanych tego materiału.', template: Tool.template`Materiał ze strony ${0} nie posiada zdefiniowanych metadanych potrzebnych do \ działania skryptu lub są one nieprawidłowe.\n Może to oznaczać, że nie jest to materiał publicznie dostępny, nie posiada zdefiniowanych źródeł lub nie \ mogą one zostać wyświetlone w przeglądarce bez dodatkowego oprogramowania albo jest to materiał \ umieszczony w płatnej strefie.`, type: 'info' }, timeout: { caption: 'Zbyt długi czas odpowiedzi.', template: Tool.template`Dla kroku: ${0} na stronie "${1}" nie dotarły \ informacje zwrotne.\nPrzypuszczalnie jest to problem sieciowy. Spróbuj ponownie za jakiś czas.` }, noParent: { caption: 'Brak zakładki ze stroną główną.', template: Tool.template`Została zamknięta zakładka ze stroną na której został uruchomiony skrypt. \ Ta zakładka nie może przez to działać poprawnie. Otwórz ponownie stronę główną: \n ${0} \n by przywrócić prawidłowe funkcjonowanie skryptu.` } }, description: { defaults: { index: 99, language: 'polski', audio: 'MPEG AAC' }, sources: { IPLA: { '1080p': {video: 'H264 MPEG-4 AVC, 4011 kb/s, 1920x1080, 25fps, 16:9', index: 1}, '720p': {video: 'H264 MPEG-4 AVC, 1672 kb/s, 1280x720, 25fps, 16:9', index: 2}, '576p': {video: 'H264 MPEG-4 AVC, 1175 kb/s, 1024x576, 25fps, 16:9', index: 3}, '384p': {video: 'H264 MPEG-4 AVC, 256 kb/s, 484x272, 25fps, 16:9', index: 4} }, WP: { HQ: {video: 'H264 MPEG-4 AVC, 1804 kb/s, 1280x720, 24fps, 16:9', index: 1}, LQ: {video: 'H264 MPEG-4 AVC, 616 kb/s, 640x360, 24fps, 16:9', index: 2} }, TVN: { 'HD': {video: 'H264 MPEG-4 AVC, 2776 kb/s, 1280x720, 25fps, 16:9', index: 1}, 'Bardzo wysoka': {video: 'H264 MPEG-4 AVC, 1786 kb/s, 1280x720, 25fps, 16:9', index: 2}, 'Wysoka': {video: 'H264 MPEG-4 AVC, 1191 kb/s, 720x576, 25fps, 5:4', index: 3}, 'Standard': {video: 'H264 MPEG-4 AVC, 794 kb/s, 720x576, 25fps, 5:4', index: 4}, 'Średnia': {video: 'H264 MPEG-4 AVC, 596 kb/s, 640x480, 25fps, 4:3', index: 5}, 'Niska': {video: 'H264 MPEG-4 AVC, 417 kb/s, 512x384, 25fps, 4:3', index: 6}, 'Bardzo niska': {video: 'H264 MPEG-4 AVC, 238 kb/s, 320x240, 25fps, 4:3', index: 7} }, VOD: { '1080':{video: 'H264 MPEG-4 AVC, 1920x1080, 25fps, 16:9', index: 1}, '720': {video: 'H264 MPEG-4 AVC, 1280x720, 25fps, 16:9', index: 2}, '576': {video: 'H264 MPEG-4 AVC, 1024x576, 25fps, 16:9', index: 3}, '480': {video: 'H264 MPEG-4 AVC, 854x480, 25fps, 16:9', index: 4}, '360': {video: 'H264 MPEG-4 AVC, 640x360, 25fps, 16:9', index: 5}, '240': {video: 'H264 MPEG-4 AVC, 426x240, 25fps, 16:9', index: 6} }, TVP: { '9100000': {video: 'H264 MPEG-4 AVC, 21030 kb/s, 1920x1080, 25fps, 16:9', index: 1}, '5420000': {video: 'H264 MPEG-4 AVC, 9875 kb/s, 1280x720, 25fps, 16:9', index: 2}, '2850000': {video: 'H264 MPEG-4 AVC, 4661 kb/s, 960x540, 25fps, 16:9', index: 3}, '1750000': {video: 'H264 MPEG-4 AVC, 1782 kb/s, 800x450, 25fps, 16:9', index: 4}, '1500000': {video: 'H264 MPEG-4 AVC, 1487 kb/s, 720x404, 25fps, 16:9', index: 5}, '1250000': {video: 'H264 MPEG-4 AVC, 1255 kb/s, 640x360, 25fps, 16:9', index: 6}, '820000': {video: 'H264 MPEG-4 AVC, 809 kb/s, 480x270, 25fps, 16:9', index: 7}, '590000': {video: 'H264 MPEG-4 AVC, 581 kb/s, 398x224, 25fps, 199:112', index: 8} }, ARTE: { '2200': {video: 'H264 MPEG-4 AVC, 2438 kb/s, 1280x720, 25fps, 16:9', index: 1}, '1500': {video: 'H264 MPEG-4 AVC, 1619 kb/s, 720x406, 25fps, 360:203', index: 2}, '800': {video: 'H264 MPEG-4 AVC, 805 kb/s, 640x360, 25fps, 16:9', index: 3}, '300': {video: 'H264 MPEG-4 AVC, 357 kb/s, 384x216, 25fps, 16:9', index: 4} }, NINATEKA: { 'video/mp4': {video: 'H264 MPEG-4 AVC, 900 kb/s, 640x360, 25fps, 16:9', index: 1}, 'application/x-mpegURL': {description: "H264 MPEG-4 video stream with multiple resolutions", format: "HLS", index: 2}, 'application/dash+xml': {description: "MPEG-DASH video stream with multiple resolutions", format: "MPD", index: 3}, }, CDA: { '1080p': {video: 'H264 MPEG-4 AVC, 1920x1080, 16:9', index: 1}, '720p': {video: 'H264 MPEG-4 AVC, 1280x720, 16:9', index: 2}, '480p': {video: 'H264 MPEG-4 AVC, 854x480, 427:240', index: 3}, '360p': {video: 'H264 MPEG-4 AVC, 640x360, 16:9', index: 4}, }, TRWAM: { '3': {video: 'H264 MPEG-4 AVC, 640x360, 16:9', index: 1}, '2': {description: "H264 MPEG-4 video stream with multiple resolutions", format: "HLS", index: 2}, '9': {description: "MPEG-DASH video stream with multiple resolutions", format: "MPD", index: 3}, } } } }; var Step = (function(properties){ var step = { urlTemplateParts: [], urlTemplate: '', before: function(input){return input}, after: function (output) {return output}, resultUrlParams: function (input, template) { var urlParams = {}; $.each(input, function (key, value) { template = template.replace(new RegExp(config.urlParamPattern + key,'g'), value); urlParams[key] = value; }); return { url: template, urlParams: urlParams }; }, resolveUrl: function (input, partIndex) { return this.resultUrlParams(input, this.resolveUrlParts(partIndex)); }, isRemote: function(){ return this.urlTemplate.length > 0; }, method: 'GET', headers: {}, responseType: 'json', retryErrorCodes: [], methodParam: function(){return {}}, resolveUrlParts: function(partIndex){ if(this.urlTemplateParts.length){ return this.urlTemplate.replace(config.urlPartPattern, this.urlTemplateParts[partIndex]); } return this.urlTemplate; } }; return $.extend(true, step, properties); }); var Notification = (function(Notification) { var create = function(title, bodyContent, special) { var specialContentClasses = special ? ' special-color white-text' : ''; var content = $('<div>').addClass('toast notification' + specialContentClasses).attr('role', 'alert') .attr('aria-live', 'assertive').attr('aria-atomic', 'true') .attr('name', special ? 'special' : 'normal').attr('data-delay', '5000'); var header = $('<div>').addClass('toast-header special-color-dark white-text'); var warnIcon = $('<i>').addClass('fas fa-exclamation-triangle pr-2'); var notificationTitle = $('<strong>').addClass('mr-auto').text(title); var time = $('<small>').text(new Date().toLocaleTimeString()); var close = $('<button>').attr('type', 'button').addClass('ml-2 mb-1 close white-text') .attr('data-dismiss', 'toast').attr('aria-label', 'Close') .append($('<span>').attr('aria-hidden', 'true').text('\u00D7')); if(special){ header.append(warnIcon); content.attr('data-autohide', 'false'); } header.append(notificationTitle).append(time).append(close); var body = $('<div>').addClass('toast-body notification-body').append(bodyContent); content.append(header).append(body); return content; }; Notification.show = function(options, w){ options = options || {}; var special = false; if (options.hasOwnProperty('special')) { special = options.special; } if(!options.hasOwnProperty('title') || !options.hasOwnProperty('content')){ return; } var rootElement = $(w.document.body); var notification = create(options.title, options.content, special); $('#notification-container', rootElement).append(notification); $('.toast', rootElement).toast('show'); $('.toast', rootElement).on('hidden.bs.toast', function (){ $.each($(this), function(index, value) { var element = $(value); element.remove(); }); }) }; return Notification; }(Notification || {})); var PluginSettingsDetector = (function(PluginSettingsDetector){ var prepareWarningNotification = function(w) { var bodyContent = $('<div>') .append('Twój dodatek ma nieprawidłowe ustawienia, przez co nie możesz korzystać z opcji ') .append('bezpośredniego pobierania plików. Możesz skorygować je w następujący sposób:'); var list = $('<ol>').addClass('m-0') .append($('<li>').text('Otwórz Panel sterowania Tampermonkey i kliknij ustawienia.')) .append($('<li>').text('Ogólne > Tryb konfiguracji > Expert')) .append($('<li>').text('Pobieranie BETA > Tryb pobierania > API przeglądarki')) .append($('<li>').text('Zapisz ustawienia, a jeżeli przeglądarka zapyta o możliwość zarządzania' + ' pobieranymi plikami, należy się zgodzić')); bodyContent.append(list).append(createButton(w)); var options = {title: 'Wykryto problem', content: bodyContent, special: true}; Notification.show(options, w); }; var createButton = function(w){ return $('<button>').attr('type', 'button').addClass('btn btn-dark btn-sm m-1 pl-3 pr-3') .append($('<i>').addClass('fas pr-1 fa-window-close')).append('Nie pokazuj więcej').click(function(){ var rootElement = $(w.document.body); w.localStorage.setItem(config.storage.doNotWarn, true); $('.toast.special-color', rootElement).toast('hide'); setTimeout(function(){ $('.toast.special-color', rootElement).remove(); }, 1000); }); }; var disableDownload = function(w){ var rootElement = $(w.document.body); $('.fa-save', rootElement).closest('button').attr('disabled', true); }; PluginSettingsDetector.detect = function(w){ var downloadMode = GM_info.downloadMode; if(downloadMode !== 'browser'){ disableDownload(w); var value = w.localStorage.getItem(config.storage.doNotWarn); if(value !== 'true'){ prepareWarningNotification(w); } } }; return PluginSettingsDetector; }(PluginSettingsDetector || {})); var DomTamper = (function(DomTamper){ DomTamper.injectStyle = function(w, name){ var head = $(w.document.head); if(!head.find('style[name="' + name + '"]').length){ var styleElement = $('<style>').attr('type', 'text/css') .attr('name', name).text((GM_getResourceText(name))); head.append(styleElement); } }; DomTamper.injectStylesheet = function (w, setting) { var head = $(w.document.head); if(!head.find('link[name="' + setting.id + '"]').length){ var stylesheet = $('<link>').attr('name', setting.id).attr('type', 'text/css').attr('rel', 'stylesheet') .attr('href', setting.css); head.append(stylesheet); } }; var prepareHead = function(w){ DomTamper.injectStylesheet(w, config.include.fontawesome); DomTamper.injectStylesheet(w, config.include.bootstrap); DomTamper.injectStylesheet(w, config.include.mdb); DomTamper.injectStyle(w, 'content_css'); }; var createLinks = function(w, additionalClass){ var links = [ { url: 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RWX4EUR77CMKU', icon: 'fa-hand-holding-usd', tooltip: 'dotacje' }, { url: 'https://greasyfork.org/pl/scripts/6049-skrypt-umo%C5%BCliwiaj%C4%85cy-pobieranie-' + 'materia%C5%82%C3%B3w-ze-znanych-serwis%C3%B3w-vod/feedback', icon: 'fa-comments', tooltip: 'problemy, komentarze' }, { url: 'https://github.com/zacny/voddownloader/issues', icon: 'fa-bug', tooltip: 'zgłoś błąd' } ]; var container = $('<div>').addClass('links-position'); links.forEach(function(link){ var button = $('<button>').attr('type', 'button').attr('title', link.tooltip) .addClass('btn btn-sm m-1 p-2').addClass(additionalClass) .append($('<i>').addClass('fas').addClass(link.icon).addClass('fa-2x')); button.click(function(){ w.open(link.url); }); container.append(button); }); return container; }; var prepareBody = function(w, pageContent, detection) { appendOrReplace(w, pageContent); attachWaveEffect(w, pageContent); if(detection) { PluginSettingsDetector.detect(w); } }; var appendOrReplace = function (w, pageContent) { var body = $(w.document.body); if(body.children().length > 0){ body.children(":first").replaceWith(pageContent); } else { body.append(pageContent); } }; var attachWaveEffect = function(w, pageContent){ var buttons = pageContent.find('.btn:not(.btn-flat), .btn-floating'); Waves.attach(buttons, ['waves-light']); Waves.init({}, w); }; DomTamper.handleError = function(exception, w){ if(w === undefined){ w = window.open(); } prepareHead(w); var errorData = getErrorData(exception); var pageContent = $('<div>').addClass('page-content'); pageContent.append(createErrorContent(errorData)); pageContent.append(createLinks(w, errorData.type === 'error' ? 'btn-danger' : 'special-color white-text')); prepareBody(w, pageContent); }; var getErrorData = function(exception){ var type = 'error'; var caption = 'Niespodziewany błąd'; var message = 'Natrafiono na niespodziewany błąd: ' + exception; if(exception.error){ message = exception.error.template.apply(this, exception.templateParams).replace(/\n/g, '<br/>'); caption = exception.error.caption; type = exception.error.type !== undefined ? exception.error.type : 'error'; } return { message: linkify(message), caption: caption, type: type } }; var linkify = function(text) { var linkDetectionRegex = /(((https?:\/\/)|(www\.))[^\s]+)/g; return text.replace(linkDetectionRegex, function(url) { return '<u><a class="text-white" href="' + url + '">' + url + '</a></u>'; }); }; var createErrorContent = function(errorData){ var typeClass = errorData.type === 'error' ? 'bg-danger' : 'bg-dark'; var card = $('<div>').addClass('card text-white mb-3').addClass(typeClass); var cardHeader = $('<div>').addClass('card-header') .text('Niestety natrafiono na problem, który uniemożliwił dalsze działanie'); var cardBody = $('<div>').addClass('card-body') .append($('<h5>').addClass('card-title').text(errorData.caption)) .append($('<div>').addClass('card-text text-white mb-3').append(errorData.message)) .append($('<div>').addClass('card-text text-white') .append('Informacje o systemie: ').append(platform.description)) .append($('<div>').addClass('card-text text-white') .append('Wersja pluginu: ').append(GM_info.version)) .append($('<div>').addClass('card-text text-white') .append('Wersja skryptu: ').append(GM_info.script.version)); card.append(cardHeader).append(cardBody); return card; }; DomTamper.removeButton = function(properties){ $(properties.injection.selector).find('#' + properties.injection.id).remove(); }; DomTamper.createButton = function(properties){ DomTamper.removeButton(properties); var element = properties.inject(); element.bind('click', properties.click); $(properties.injection.selector).append(element); }; DomTamper.createLoader = function(w){ prepareHead(w); var pageContent = $('<div>').addClass('page-content'); pageContent.append(createLoaderContent()); pageContent.append(createLinks(w, 'special-color white-text')); prepareBody(w, pageContent); Unloader.init(w); }; var createLoaderContent = function(){ var card = $('<div>').addClass('card text-white bg-dark'); var cardHeader = $('<div>').addClass('card-header').text('Poczekaj trwa wczytywanie danych...'); var cardBody = $('<div>').addClass('card-body'); var bodyContainer = $('<div>').addClass('d-flex justify-content-center m-3'); var spinner = $('<div>').addClass('spinner-border spinner-size').attr('role', 'status') .append($('<span>').addClass('sr-only').text('Loading...')); cardBody.append(bodyContainer.append(spinner)); card.append(cardHeader).append(cardBody); return card; }; var setWindowTitle = function(data, w){ var head = $(w.document.head); var title = head.find('title'); if(title.length) { title.text(data.title); } else { head.append($('<title>').text(data.title)); } }; DomTamper.createDocument = function(data, w){ prepareHead(w); setWindowTitle(data, w); var pageContent = $('<div>').addClass('page-content'); pageContent.append(Accordion.create(w, data)); pageContent.append(createLinks(w, 'special-color white-text')); pageContent.append(createNotificationContainer()); prepareBody(w, pageContent, true); Unloader.init(w); Accordion.bindActions(w, data); }; var createNotificationContainer = function(){ return $('<div>').attr('id', 'notification-container') .attr('aria-live', 'polite').attr('aria-atomic', 'true').addClass('notification-container'); }; return DomTamper; }(DomTamper || {})); var HistoryTamper = (function(HistoryTamper){ HistoryTamper.onLocationChange = function(locationChangeCallback){ history.pushState = ( f => function pushState(){ var ret = f.apply(this, arguments); window.dispatchEvent(new Event('pushstate')); window.dispatchEvent(new Event('locationchange')); return ret; })(history.pushState); history.replaceState = ( f => function replaceState(){ var ret = f.apply(this, arguments); window.dispatchEvent(new Event('replacestate')); window.dispatchEvent(new Event('locationchange')); return ret; })(history.replaceState); window.addEventListener('popstate',()=>{ window.dispatchEvent(new Event('locationchange')) }); window.addEventListener('locationchange', function(){ locationChangeCallback(); }); }; return HistoryTamper; }(HistoryTamper || {})); var Accordion = (function(Accordion) { Accordion.create = function(w, data){ var mainCardTitle = $('<div>').addClass('card-header').text(data.title); var accordion = $('<div>').addClass('accordion md-accordion').attr('id', 'accordion') .attr('role', 'tablist').attr('aria-multiselectable', 'true'); createCards(accordion, data); var mainCardBody = $('<div>').addClass('card-body p-0').append(accordion); return $('<div>').addClass('card').append(mainCardTitle).append(mainCardBody); }; var createCards = function(accordion, data) { for(var key in data.cards) { var card = createCard({ card: data.cards[key], key: key, title: data.title }); accordion.append(card); } }; var createCard = function(data){ var accordionCard = $('<div>').addClass('border border-top-0'); var content = $('<div>').addClass('card-body pt-0'); var badgeClass = 'badge-light'; var textMuted = 'text-muted'; if(data.card.items.length > 0){ badgeClass = 'badge-danger'; textMuted = 'text-dark'; content.append(createCardContent(data)); } var icon = $('<i>').addClass('fas').addClass(data.card.icon).addClass('pr-2'); var badge = $('<span>').addClass('badge mr-3 float-right').addClass(badgeClass) .text(data.card.items.length); var cardTitle = $('<h6>').addClass('mb-0').addClass(textMuted).append(icon).append(badge) .append($('<span>').text(data.card.label)); var link = $('<a>').append(cardTitle); var cardHeader = $('<div>').addClass('ml-3 p-2').attr('role', 'tab').attr('id', data.key).append(link); var cardBody = $('<div>').addClass('collapse').attr('role', 'tabpanel') .attr('aria-labelledby', data.key).append(content); if(data.card.collapse){ cardBody.addClass('show'); } accordionCard.append(cardHeader); accordionCard.append(cardBody); return accordionCard; }; var createCardContent = function(data){ var table = $('<table>').addClass('table table-bordered table-striped btn-table'); var tbody = $('<tbody>'); table.append(tbody); createRows(tbody, data); return table; }; var createRows = function(tableBody, data){ data.card.items.forEach(function(item) { tableBody.append(createRow({ item: item, info: data.card.info, title: data.title, actions: data.card.actions })); }); }; var createRow = function(data){ var actions = $('<td>').attr('scope', 'row').addClass('action-row-' + data.actions.length); data.actions.forEach(function(action){ actions.append(createButton(action, data)); }); var description = $('<td>').html(createDescriptionHtml(data)); return $('<tr>').append(actions).append(description); }; var createDescriptionHtml = function(data){ var descriptionHtml = $('<div>'); createDescription(data).forEach(function(item, idx, array){ descriptionHtml.append($('<b>').text(item.desc + ': ')) .append($('<span>').text(item.value)); if(idx !== array.length - 1) {//not last descriptionHtml.append($('<span>').text(', ')); } }); return descriptionHtml; }; var itemExist = function(data, info){ return data.item.hasOwnProperty(info.name) && data.item[info.name] != null }; var createDescription = function(data){ var description = []; data.info.forEach(function(info){ if (itemExist(data, info)) { description.push({ desc: info.desc, value: data.item[info.name] }); } }); return description; }; var createButton = function(action, data){ return $('<button>').attr('type', 'button').attr('data-url', data.item.url).attr('data-title', data.title) .addClass('btn btn-dark btn-sm m-1 pl-3 pr-3') .append($('<i>').addClass('fas pr-1').addClass(action.icon)).append(action.label); }; Accordion.bindActions = function(w, data){ cardActions(w, data); buttonActions(w); }; var cardActions = function(w, data){ for(var key in data.cards) { var cardHeader = $(w.document.body).find('#' + key); var disabled = cardHeader.find('h6.text-muted'); if(disabled.length){ disabled.addClass('cursor-normal'); } else { $(w.document.body).find('#' + key).click(function() { var id = $(this).attr('id'); $(w.document.body).find('div[aria-labelledby="' + id + '"]').toggle(); }); } } }; var buttonActions = function(w){ getButton(w, '.fa-clone').click(function(){ copyActionClick($(this), w) }); getButton(w, '.fa-film').click(function(){ openActionClick($(this), w) }); getButton(w, '.fa-download').click(function(){ downloadActionClick($(this), w) }); }; var getButton = function(w, iconClass){ return $(w.document.body).find(iconClass).parent(); }; var downloadActionClick = function (element, w) { var options = {title: 'Rozpoczęto pobieranie pliku', content: element.attr('data-title')}; Tool.downloadFile(element.attr('data-url'), element.attr('data-title')); Notification.show(options, w); }; var copyActionClick = function (element, w) { GM_setClipboard(element.attr('data-url')); var options = {title: 'Kopiowanie', content: 'Skopiowano do schowka'}; Notification.show(options, w); }; var openActionClick = function (element, w) { w.open(element.attr('data-url')); }; return Accordion; }(Accordion || {})); var Executor = (function(Executor){ var execute = function(service, options, w){ var setup = setupStep(service, options); logStepInfo(options, setup); if(setup.isRemote()){ executeAsync(service, setup, options, w); } else { callback(service, options, w); } }; var executeAsync = function(service, setup, options, w){ var chain = options.chainNames[options.chainIndex]; var chainStep = chain + '[' + options.stepIndex + ']'; var exceptionParams = [chainStep, Tool.getRealUrl()]; var requestParams = { method: setup.method, headers: setup.headers, url: setup.resolveUrl.url, data: JSON.stringify(setup.methodParam()), responseType: setup.responseType, onload: function(data) { var currentStep = getCurrentStep(service, options); if(retryPossible(currentStep, options, data.status)){ execute(service, options, w); } else { if(setup.responseType === 'jsonp'){ var match = data.responseText.match(/callback\(([\s\S]*?)\);/); if(match && match[1] && !match[1].startsWith('null')){ setStepResult(options, {async: JSON.parse(match[1])}); } } else { setStepResult(options, {async: data.response || {}}); } callback(service, options, w); } }, onerror: function(data){ DomTamper.handleError(new Exception(config.error.call, exceptionParams), w); }, ontimeout: function(){ DomTamper.handleError(new Exception(config.error.timeout, exceptionParams), w); } }; GM_xmlhttpRequest(requestParams); }; var retryPossible = function(step, options, status){ return step.retryErrorCodes.indexOf(status) >= 0 && step.urlTemplateParts[options.retries++]; }; var logStepInfo = function(options, setup){ var chain = options.chainNames[options.chainIndex]; var step = chain + '[' + options.stepIndex + ']'; var stepParams = $.isEmptyObject(setup.methodParam()) ? '' : JSON.stringify(setup.methodParam()); var params = [ 'color:green', options.retries+1, 'color:black', ':', 'color:blue', step, 'color:red', setup.isRemote() ? setup.method : '---', 'color:black;font-weight: bold', setup.isRemote() ? setup.resolveUrl.url : '---', 'color:magenta', stepParams ]; Tool.formatConsoleMessage('%c%s%c%s%c%s%c %s %c %s %c%s', params); }; var setupStep = function(service, options){ var currentStep = getCurrentStep(service, options); beforeStep(currentStep, options); var setup = $.extend(true, {}, currentStep); if(currentStep.isRemote()) { setup.resolveUrl = currentStep.resolveUrl(getStepResult(options).before, options.retries); } return setup; }; var getCurrentStep = function(service, options){ var chain = options.chainNames[options.chainIndex]; var steps = service.chains[chain]; return steps[options.stepIndex]; }; var beforeStep = function(currentStep, options){ var stepOutput = currentStep.before(getStepResult(options, true).after || {}, getStepResult(options, true)); if(currentStep.isRemote()){ if(typeof stepOutput === 'string' || typeof stepOutput == 'number'){ var result = stepOutput; stepOutput = {}; stepOutput[config.urlParamDefaultKey] = result; } } setStepResult(options, {before: stepOutput}); }; var getStepResult = function(options, previous){ var chain = options.chainNames[options.chainIndex]; if(!options.results){ options.results = {}; } if(!options.results[chain]){ options.results[chain] = []; } if(!options.results[chain][options.stepIndex]){ options.results[chain].push({}); } var stepIndex = previous && options.stepIndex > 0 ? options.stepIndex - 1 : options.stepIndex; return options.results[chain][stepIndex]; }; var setStepResult = function(options, object){ var chain = options.chainNames[options.chainIndex]; options.results[chain][options.stepIndex] = $.extend(true, getStepResult(options), object); }; var hasNextStep = function(service, options){ var chain = options.chainNames[options.chainIndex]; var steps = service.chains[chain]; return steps.length - 1 > options.stepIndex; }; var hasNextChain = function(service, options){ return options.chainNames.length - 1 > options.chainIndex; }; var pushChain = function(service, options){ if(hasNextChain(service, options)){ options.chainIndex += 1; options.stepIndex = 0; return true; } return false; }; var pushStep = function(service, options) { if(hasNextStep(service, options)){ options.stepIndex += 1; return true; } return false; }; var afterStep = function(service, options) { var currentStep = getCurrentStep(service, options); var previousResult = currentStep.isRemote() ? getStepResult(options).async : getStepResult(options).before; var output = currentStep.after(previousResult || {}, getStepResult(options)); options.retries = 0; setStepResult(options, {after: output}); }; var callback = function(service, options, w){ try { afterStep(service, options); if(pushStep(service, options) || pushChain(service, options)) { return Promise.resolve().then( Executor.chain(service, options, w) ); } else { return Promise.resolve().then( service.onDone(options.results, w) ); } } catch(e){ DomTamper.handleError(e, w); } }; Executor.chain = function(service, options, w){ try { if(w === undefined){ w = window.open(); DomTamper.createLoader(w, service); } execute(service, options, w); } catch(e){ DomTamper.handleError(e, w); } }; return Executor; }(Executor || {})); function Configurator(properties){ var service = { observer: { anchor: undefined, mode: 'added', selector: undefined }, injection: { selector: properties.observer.selector, id: 'direct-download', class: '', }, cardsData: { title: '', cards: { videos: { icon: 'fa-video', label: 'Video', collapse: true, items: [], info: [ {name: 'video', desc: 'video'}, {name: 'audio', desc: 'audio'}, {name: 'language', desc: 'wersja językowa'} ], actions: [ {label: 'Pobierz', icon: 'fa-download'}, {label: 'Kopiuj', icon: 'fa-clone'}, {label: 'Otwórz', icon: 'fa-film'} ] }, subtitles: { icon: 'fa-file-alt', label: 'Napisy', collapse: false, items: [], info: [ {name: 'description', desc: 'opis'}, {name: 'format', desc: 'format'} ], actions: [ {label: 'Pobierz', icon: 'fa-download'} ] }, streams: { icon: 'fa-stream', label: 'Strumienie', collapse: false, items: [], info: [ {name: 'description', desc: 'opis'}, {name: 'format', desc: 'format'} ], actions: [ {label: 'Kopiuj', icon: 'fa-clone'} ] } } }, chains: { videos: [] }, chainSelector: function(){ return ['videos']; }, formatter: function(data){ data.cards['videos'].items.sort(function (a, b) { return a.index - b.index; }); data.cards['subtitles'].items.sort(function (a, b) { return ('' + a.format).localeCompare(b.format); }); data.cards['streams'].items.sort(function (a, b) { return ('' + a.format).localeCompare(b.format); }); }, aggregate: function(data){ var aggregatedData = $.extend(true, {}, service.cardsData); var chains = service.chainSelector(); chains.forEach(function(chain){ var extend = data[chain][data[chain].length - 1].after; $.extend(true, aggregatedData, extend); }); return aggregatedData; }, onDone: function(data, w) { var aggregatedData = service.aggregate(data); service.formatter(aggregatedData); DomTamper.createDocument(aggregatedData, w); }, ready: function(){ return $(service.observer.selector).length > 0; }, click: function(){ var chainNames = service.chainSelector(); Executor.chain(service, { stepIndex: 0, chainIndex: 0, retries: 0, chainNames: chainNames }); }, inject: function(){ var icon1 = $('<i>').addClass('fa fa-circle fa-stack-2x video_button_circle'); var icon2 = $('<i>').addClass('fa fa-video fa-stack-1x fa-inverse'); var span = $('<span>').addClass('fa-stack fa-1x').append(icon1).append(icon2); var div = $('<div>') .attr('id', service.injection.id).attr('title', 'informacje o wideo') .append(span).addClass('video_button') .addClass(service.injection.class); $(service.observer.selector).hover(() => div.show(), () => div.hide()); return div; }, }; return $.extend(true, service, properties); } var Detector = (function(conf) { var configuration = conf; var logObservation = function(){ var observer = configuration.properties.observer; var color = configuration.properties.ready() ? 'color:green' : 'color:red'; var anchor = observer.anchor ? observer.anchor + '->' : ''; var params = [color, anchor + observer.selector, 'color:black']; Tool.formatConsoleMessage('[%c%s%c]', params); }; this.observe = function(){ var observer = configuration.properties.observer; if(configuration.properties.ready()){ logObservation(); configuration.successCallback(); } else { $(observer.anchor).observe(observer.mode, observer.selector, function(record) { logObservation(); configuration.successCallback(); }); } }; }); var ElementDetector = (function(ElementDetector){ ElementDetector.detect = function(properties, callback){ var detector = new Detector({ properties: properties, successCallback: callback }); detector.observe(); }; return ElementDetector; }(ElementDetector || {})); var Unloader = (function(Unloader) { var win; var url; Unloader.init = function(w){ win = w; url = Tool.getRealUrl(); $(window).bind('beforeunload', function(){ if(!win.closed) { DomTamper.handleError(new Exception(config.error.noParent, url), win); } }); }; return Unloader; }(Unloader || {})); var MessageReceiver = (function(MessageReceiver) { var win; var origin; var callbackFunction; var alreadyConfirmed = false; var alreadyPosted = false; var receiveMessage = function(event, callback){ if (event.origin !== origin) { return; } var data = parseJSON(event.data); if($.isEmptyObject(data)){ return; } if(data.confirmation){ alreadyConfirmed = true; } else { data.confirmation = true; if(!alreadyPosted) { window.removeEventListener('message', callbackFunction); alreadyPosted = true; postMessage(data); callback(data); } } }; var parseJSON = function(json) { if (typeof json == 'object') return {}; try { return JSON.parse(json); } catch(e) { return {}; } }; var postMessage = function(data){ data = JSON.stringify(data); win.postMessage(data, '*'); }; MessageReceiver.awaitMessage = function(object, callback){ initCommunication(object, callback); }; var initCommunication = function(object, callback){ callbackFunction = function(e){ receiveMessage(e, callback); }; window.addEventListener('message', callbackFunction); win = getProperty(object, 'windowReference'); origin = getProperty(object, 'origin'); }; var getProperty = function(object, prop){ if(object.hasOwnProperty(prop)){ return object[prop]; } }; MessageReceiver.postUntilConfirmed = function(object){ initCommunication(object); isMessageConfirmed(config.attempts, getProperty(object, 'message')) }; var isMessageConfirmed = function(attempt, message){ if (alreadyConfirmed || attempt <= 0) { return Promise.resolve().then(function(){ window.removeEventListener('message', callbackFunction); if(attempt <= 0){ console.warn("Nie udało się przekazać adresu z okna głównego."); } }); } else if(attempt > 0){ attempt = attempt-1; postMessage(message); return Promise.resolve().then( setTimeout(isMessageConfirmed, config.attemptTimeout, attempt, message) ); } }; return MessageReceiver; }(MessageReceiver || {})); var Common = (function(Common) { Common.grabIplaSubtitlesData = function(data){ var items = []; var subtitles = (((data.result || {}).mediaItem || {}).displayInfo || {}).subtitles || []; subtitles.forEach(function(subtitle) { items.push({ url: subtitle.src, description: subtitle.name, format: subtitle.format }) }); return { cards: {subtitles: {items: items}} }; }; Common.run = function(properties){ HistoryTamper.onLocationChange(function () { DomTamper.removeButton(properties); }); ElementDetector.detect(properties, function () { DomTamper.createButton(properties); }); }; Common.createProperties = function(anchor, selector, mode) { return { observer: { anchor: anchor, mode: mode ? mode : 'added', selector: selector, }, ready: function() { return $(this.observer.selector).length > 0; } }; }; return Common; }(Common || {})); var TVP = (function() { var dataAttributeParser = function() { var src = $(properties.observer.selector).attr('data-video-id'); if(src !== undefined){ return { videoId: src.split("/").pop() }; } return urlForwardParser(); }; var urlForwardParser = function() { var urlMatch = window.location.href.match(/^https?:\/\/.*\.tvp\..*\/(\d{6,})\/.*$/); if(urlMatch && urlMatch[1]){ return urlMatch[1]; } return urlParameterParser(); }; var urlParameterParser = function(){ var ids = [ Tool.getUrlParameter('ID', window.location.href), Tool.getUrlParameter('object_id', window.location.href) ]; var id = ids.find(nonNull); if(id){ return id; } throw new Exception(config.error.id, window.location.href); }; var nonNull = function (id) { return id !== null; }; var properties = new Configurator({ observer: { selector: '#JS-TVPlayer2-Wrapper, .player-video-container, #tvplayer, #Player' }, chains: { videos: [ new Step({ urlTemplate: 'https://tvp.pl/pub/stat/videofileinfo?video_id=#videoId', before: function (input) { return dataAttributeParser(); }, after: function (input, result) { return getRealVideoId(input, result.before.videoId); } }), new Step({ urlTemplate: 'https://vod.tvp.pl/sess/TVPlayer2/api.php?id=#videoId&@method=getTvpConfig' + '&@callback=callback', responseType: 'jsonp', after: function(input){ return grapVideoData(input); } }) ] } }); var getRealVideoId = function(json, videoId){ var videoId = (json || {}).copy_of_object_id !== undefined ? json.copy_of_object_id : videoId; return { videoId: videoId }; }; var grapVideoData = function(data){ var items = []; var subtitlesItems = []; var info = ((data || {}).content || {}).info || {}; var files = ((data || {}).content || {}).files || []; var subtitles = ((data || {}).content || {}).subtitles || []; var files = removeUnsupportedVideoFormats(files); if(files.length) { files.forEach(function (file) { var videoDesc = file.quality.bitrate; items.push(Tool.mapDescription({ source: 'TVP', key: videoDesc, video: videoDesc, url: file.url })); }); subtitles.forEach(function(subtitle) { var extension = subtitle.type; subtitlesItems.push({ url: 'https:' + subtitle.url, format: extension, description: subtitle.lang }) }); return { title: (info.title != null ? info.title : '') + (info.subtitle != null ? ' ' + info.subtitle : ''), cards: { videos: {items: items}, subtitles: {items: subtitlesItems} } } } throw new Exception(config.error.noSource, window.location.href); }; var removeUnsupportedVideoFormats = function(files){ var result = []; files.forEach(function (file) { if (file['type'] === 'any_native') { result.push(file); } }); return result; }; this.setup = function(){ Common.run(properties); }; }); var TVN = (function() { var properties = new Configurator({ observer: { anchor: 'body', selector: 'div.cover-buttons > a[href="#"], #player-container, div.custom-alert-inner-wrapper' }, injection: { selector: 'div.right-side' }, inject: function(){ var icon = $('<i>').addClass('fas fa-video'); var button = $('<div>') .attr('id', properties.injection.id).attr('title', 'pobierz video') .append(icon).addClass('btn btn-login'); return button; }, chains: { videos: [ new Step({ urlTemplate: 'http://player.pl/api/?platform=ConnectedTV&terminal=Panasonic&format=json' + '&authKey=064fda5ab26dc1dd936f5c6e84b7d3c2&v=3.1&m=getItem&id=#videoId', before: function(input){ return idParser(); }, after: function(output) { return grabVideoData(output); } }) ] }, }); var idParser = function(){ var watchingNow = $('.watching-now').closest('.embed-responsive').find('.embed-responsive-item'); if(watchingNow.length > 0){ return watchingNow.attr('href').split(',').pop(); } return episodeIdParser(); }; var episodeIdParser = function () { var match = window.location.href.match(/odcinki,(\d+)\/.*,(\d+)/); if(match && match[2]){ return match[2]; } return serialIdParser(); }; var serialIdParser = function () { var match = window.location.href.match(/odcinki,(\d+)/); if(match && match[1]){ throw new Exception(config.error.tvnId, Tool.getRealUrl()); } return vodIdParser(); }; var vodIdParser = function(){ var match = window.location.href.match(/,(\d+)/); if(match && match[1]){ return match[1]; } throw new Exception(config.error.tvnId, Tool.getRealUrl()); }; var grabVideoData = function(data){ var items = []; var main = ((data.item || {}).videos || {}).main || {}; var video_content = main.video_content || {}; if(main.video_content_license_type !== 'WIDEVINE' && video_content && video_content.length > 0){ $.each(video_content, function( index, value ) { items.push(Tool.mapDescription({ source: 'TVN', key: value.profile_name, video: value.profile_name, url: value.url })); }); return { title: getTitle(data), cards: {videos: {items: items}} } } throw new Exception(config.error.noSource, Tool.getRealUrl()); }; var getTitle = function(data){ var episode = data.item.episode ? 'E'+Tool.pad(data.item.episode, 3) : ''; var season = data.item.season != null ? 'S'+Tool.pad(data.item.season, 2) : ''; var serie_title = data.item.serie_title != null ? data.item.serie_title : ''; var episodeTitle = data.item.title ? ' ' + data.item.title : ''; var seasonAndEpisode = season + episode; return serie_title + (seasonAndEpisode !== '' ? ' - ' + seasonAndEpisode : '') + (episodeTitle !== '' ? ' - ' + episodeTitle : ''); }; var inVodFrame = function(){ var regexp = new RegExp('https:\/\/player\.pl(.*)'); var match = regexp.exec(window.location.href); if(match[1]) { window.sessionStorage.setItem(config.storage.topWindowLocation, 'https://vod.pl' + match[1]); } }; this.setup = function(){ if(!Tool.isTopWindow() && $('#app').length) { inVodFrame(); } Common.run(properties); }; }); var IPLA = (function() { var properties = new Configurator({ observer: { anchor: 'app-root', selector: 'div.player-wrapper, div.promo-box:visible, div.player-error-presentation:visible' }, chainSelector: function(){ return ['videos', 'subtitles']; }, chains: { videos: [ new Step({ urlTemplateParts: [ 'ua=www_iplatv_html5/12345', 'ua=mipla_ios/122' ], urlTemplate: 'https://getmedia.redefine.pl/vods/get_vod/?cpid=1&~&media_id=#videoId', retryErrorCodes: [404], before: function (input) { return grabVideoIdFromUrl(); }, after: function(data){ return grabVideoData(data); } }) ], subtitles: [ new Step({ urlTemplate: 'https://b2c.redefine.pl/rpc/navigation/', method: 'POST', methodParam: function(){ return getParamsForSubtitles(); }, after: Common.grabIplaSubtitlesData }) ] } }); var grabVideoData = function(data){ var items = []; var vod = data.vod || {}; if(vod.copies && vod.copies.length > 0 && !vod.drm){ $.each(vod.copies, function( index, value ) { var videoDesc = value.quality_p + ', ' + value.bitrate; items.push(Tool.mapDescription({ source: 'IPLA', key: value.quality_p, video: videoDesc, url: value.url })); }); return { title: vod.title, cards: {videos: {items: items}} } } throw new Exception(config.error.noSource, Tool.getRealUrl()); }; var getParamsForSubtitles = function(){ var mediaId = grabVideoIdFromUrl(); return { jsonrpc: "2.0", id: 1, method: "prePlayData", params: { userAgentData: { application: "firefox", portal: "ipla" }, cpid: 1, mediaId: mediaId } } }; this.setup = function(){ Common.run(properties); }; var matchingId = function(input, failureAction){ input = input ? input : ''; var match = matchingHexId(input); if(!match){ match = matchingDecId(input); } return match ? match : failureAction(); }; var matchingHexId = function(input){ var match = input.match(/[0-9a-f]{32}/); if(match && match[0]) { return match[0]; } return null; }; var matchingDecId = function(input) { var match = input.match(/([\d]+)?(\?.*)$/); if(match && match[1]) { return match[1]; } return null; }; var grabVideoIdFromUrl = function(){ return matchingId(location.href, grabVideoIdFromWatchingNowElement); }; var grabVideoIdFromWatchingNowElement = function(){ return matchingId($('div.vod-image-wrapper__overlay').closest('a').attr('href'), grabVideoIdFromHtmlElement); }; var grabVideoIdFromHtmlElement = function(){ var frameSrc = $('app-commercial-wallpaper iframe:first-child').attr('src'); if(frameSrc !== undefined) { return Tool.getUrlParameter('vid', frameSrc); } throw new Exception(config.error.id, Tool.getRealUrl()); }; }); var VOD = (function() { var properties = new Configurator({ observer: { selector: '#v_videoPlayer' }, injection: { class: 'right_margin' }, chains: { videos: [ new Step({ urlTemplate: 'https://player-api.dreamlab.pl/?body[id]=#videoId&body[jsonrpc]=2.0' + '&body[method]=get_asset_detail&body[params][ID_Publikacji]=#videoId' + '&body[params][Service]=vod.onet.pl&content-type=application/jsonp' + '&x-onet-app=player.front.onetapi.pl&callback=', before: function (input) { return idParser(); }, after: function (output) { return grabVideoData(output); } }) ] } }); var idParser = function () { var id = $(".mvp").attr('id'); if(id !== undefined){ return id.match(/mvp:(.+)/)[1]; } return parseFromJS(); }; var parseFromJS = function(){ var scripts = $('script[type="text/javascript"]').filter(':not([src])'); for (var i = 0; i < scripts.length; i++) { var match = $(scripts[i]).text().match(/\"mvpId\"\s*:\s*\"(\d+\.\d+)\"/); if(match && match[1]){ return match[1]; } } throw new Exception(config.error.id, Tool.getRealUrl()); }; var grabVideoData = function (data) { var items = []; var subtitlesItems = []; var video = (((data.result || new Array())[0] || {}).formats || {}).wideo || {}; var meta = ((data.result || new Array())[0] || {}).meta || {}; var subtitles = meta.subtitles || []; var videoData = video['mp4-uhd'] && video['mp4-uhd'].length > 0 ? video['mp4-uhd'] : video['mp4']; if(videoData && videoData.length > 0){ videoData.forEach(function(value) { var videoDesc = value.vertical_resolution + ', ' + value.video_bitrate; items.push(Tool.mapDescription({ source: 'VOD', key: value.vertical_resolution, video: videoDesc, url: value.url })); }); subtitles.forEach(function(subtitle) { var extension = subtitle.name.split('.').pop(); subtitlesItems.push({ url: subtitle.url, format: extension, description: subtitle.name }) }); return { title: meta.title, cards: { videos: {items: items}, subtitles: {items: subtitlesItems} } } } throw new Exception(config.error.noSource, Tool.getRealUrl()); }; var iplaDetected = function(){ return $('#v_videoPlayer div.pulsembed_embed').length > 0; }; var workWithSubService = function(){ var src = 'https://pulsembed.eu'; var frameSelector = 'iframe[src^="' + src + '"]'; var properties = Common.createProperties('div.pulsembed_embed', frameSelector); ElementDetector.detect(properties, function () { MessageReceiver.postUntilConfirmed({ windowReference: $(frameSelector).get(0).contentWindow, origin: src, message: { location: window.location.href } }); }); }; this.setup = function(){ if(iplaDetected()) { workWithSubService(); } else if(Tool.isTopWindow()){ Common.run(properties); } }; }); var VOD_IPLA = (function() { var properties = new Configurator({ observer: { anchor: 'body', selector: '#player-wrapper, #playerContainer' }, injection: { class: 'left_margin' }, chainSelector: function(){ return ['videos', 'subtitles']; }, chains: { videos: [ new Step({ urlTemplate: 'https://distro.redefine.pl/partner_api/v1/2yRS5K/media/#media_id/vod/player_data?' + 'dev=pc&os=linux&player=html&app=firefox&build=12345', before: function (input) { return {media_id: idParser()}; }, after: function(data){ return grabVideoData(data); } }) ], subtitles: [ new Step({ after: function (input) { return Common.grabIplaSubtitlesData(getJson()); } }) ] } }); var grabVideoData = function(data){ var items = []; var displayInfo = (data.mediaItem || {}).displayInfo || {}; var mediaSources = ((data.mediaItem || {}).playback || {}).mediaSources || {}; var videos = $.grep(mediaSources, function(source) { return source.accessMethod === 'direct'; }); if(videos && videos.length > 0){ $.each(videos, function( index, value ) { items.push(Tool.mapDescription({ source: 'IPLA', key: value.quality, video: value.quality, url: value.url })); }); return { title: displayInfo.title, cards: {videos: {items: items}} } } throw new Exception(config.error.noSource, Tool.getRealUrl()); }; var getJson = function(){ var match = $('script:not(:empty)').text().match(/(window\.CP\.embedSetup\()(.*)\);/); if(match) { var jsonObject = JSON.parse(match[2]); return JSON.parse(jsonObject[0].media); } return {}; }; var idParser = function(){ try { if($('#player-wrapper').length > 0) { return (((getJson() || {}).result || {}).mediaItem || {}).id; } else if($('#playerContainer').length > 0){ return getMediaId(); } } catch(e){ throw new Exception(config.error.id, Tool.getRealUrl()); } }; var getMediaId = function(){ var match = $('script:not(:empty)').text().match(/mediaId: "(\w+)",/); return match[1]; }; this.setup = function(){ var callback = function(data) { window.sessionStorage.setItem(config.storage.topWindowLocation, data.location); Common.run(properties); }; MessageReceiver.awaitMessage({ origin: 'https://pulsembed.eu', windowReference: window.parent }, callback); }; }); var WP = (function() { var properties = new Configurator({ observer: { anchor: 'body', selector: 'div.npp-container' }, chains: { videos: [ new Step({ urlTemplate: 'https://wideo.wp.pl/player/mid,#videoId,embed.json', before: function (input) { return idParser(); }, after: function (output) { return grabVideoData(output); } }) ] } }); var idParser = function () { try { var id = window.location.href.match(/^(.*)-(\d+)v$/)[2]; //__NEXT_DATA__ is a variable on page return __NEXT_DATA__.props.initialPWPState.material[id].mid; } catch(e){ throw new Exception(config.error.id, window.location.href); } }; var grabVideoData = function(data){ var items = []; var urls = (data.clip || {}).url || {}; if(urls && urls.length > 0){ $.each(urls, function( index, value ) { if(value.type === 'mp4@avc'){ var videoDesc = value.quality + ', ' + value.resolution; items.push(Tool.mapDescription({ source: 'WP', key: value.quality, video: videoDesc, url: value.url })); } }); return { title: data.clip.title, cards: {videos: {items: items}} } } throw new Exception(config.error.noSource, window.location.href); }; this.setup = function(){ Common.run(properties); }; }); var CDA = (function() { var properties = new Configurator({ observer: { selector: '.pb-player-content' }, injection: { class: 'right_margin' }, chains: { videos: [ new Step({ before: function(input){ return getDestinationUrl(); }, after: function (input) { return grabVideoData(input); } }) ] } }); var getDestinationUrl = function(){ var url = $("video.pb-video-player").attr('src'); if(url !== undefined){ if(!url.match(/blank\.mp4/)){ return url; } else if(l !== undefined){ return l; } } throw new Exception(config.error.id, window.location.href); }; var grabVideoData = function(data){ var items = []; var title = $('meta[property="og:title"]'); var quality = $('.quality-btn-active'); var videoDesc = quality.length > 0 ? quality.text() : '-'; items.push(Tool.mapDescription({ source: 'CDA', key: videoDesc, video: videoDesc, audio: '-', url: data })); return { title: title.length > 0 ? title.attr('content').trim() : 'brak danych', cards: {videos: {items: items}} }; }; this.setup = function(){ Common.run(properties); }; }); var NINATEKA = (function() { var properties = new Configurator({ observer: { selector: '#videoPlayer, #player' }, chains: { videos: [ new Step({ before: function(input){ return getVideoUrls(); }, after: function (input) { return grabVideoData(input); } }) ] } }); var grabVideoData = function(sources){ var videoItems = []; var streamItems = []; var title = $('meta[name="title"]').attr('content').trim(); if(sources && sources.length > 0){ $.each(sources, function(i, v ) { if(sources[i].type && sources[i].type.match(/mp4/g)){ videoItems.push(Tool.mapDescription({ source: 'NINATEKA', key: v.type, url: v.src })); } else if(sources[i].type && (sources[i].type.match(/dash\+xml/g) || sources[i].type.match(/mpegURL/g))){ streamItems.push(Tool.mapDescription({ source: 'NINATEKA', key: v.type, url: v.src })); } }); return { title: title.length > 0 ? title : 'brak danych', cards: { videos: {items: videoItems}, streams: {items: streamItems} } } } throw new Exception(config.error.noSource, window.location.href); }; var getVideoUrls = function(){ var videoPlayer = $('#videoPlayer').data('player-setup'); var sources = (videoPlayer || {}).sources || []; if(sources.length == 0){ var scripts = $('script[type="text/javascript"]').filter(':not([src])'); for (var i = 0; i < scripts.length; i++) { var match = $(scripts[i]).text().match(/fn_\S+\(playerOptionsWithMainSource,\s*\d+\)\.sources/g); if(match && match[0]){ sources = eval(match[0]); break; } } } return sources; }; this.setup = function(){ Common.run(properties); }; }); var ARTE = (function() { var properties = new Configurator({ observer: { anchor: 'div.video-thumbnail', selector: 'div.avp-player' }, chains: { videos: [ new Step({ urlTemplate: 'https://api.arte.tv/api/player/v1/config/#langCode/#videoId', before: function (input) { return idParser(); }, after: function (output) { return grabVideoData(output); } }) ] }, formatter: function(data) { data.cards['videos'].items.sort(function (a, b) { return a.index - b.index; }); var sortingOrder = {'POL': 1}; data.cards['videos'].items.sort(function (a, b) { var aLangOrder = sortingOrder[a.langCode] ? sortingOrder[a.langCode] : -1, bLangOrder = sortingOrder[b.langCode] ? sortingOrder[b.langCode] : -1; return bLangOrder - aLangOrder; }); } }); var detectLanguage = function() { var regexp = new RegExp('https:\/\/www.arte\.tv\/(\\w{2})\/'); var match = regexp.exec(window.location.href); return match[1]; }; var detectVideoId = function(){ var regexp = new RegExp('https:\/\/www.arte\.tv\/\\w{2}\/videos\/([\\w-]+)\/'); var match = regexp.exec(window.location.href); return match[1]; }; var idParser = function() { try { return { videoId: detectVideoId(), langCode: detectLanguage() }; } catch(e){ throw new Exception(config.error.id, window.location.href); } }; var grabVideoData = function(data){ var items = []; var title = (((data || {}).videoJsonPlayer || {}).eStat || {}).streamName || ''; var streams = ((data || {}).videoJsonPlayer || {}).VSR || {}; if(streams){ Object.keys(streams).filter(function(k, i) { return k.startsWith("HTTPS"); }).forEach(function(k) { var stream = streams[k]; var videoDesc = stream.width + 'x' + stream.height + ', ' + stream.bitrate; items.push(Tool.mapDescription({ source: 'ARTE', key: stream.bitrate, video: videoDesc, langCode: stream.versionShortLibelle, language: stream.versionLibelle, url: stream.url })); }); return { title: title, cards: {videos: {items: items}} } } throw new Exception(config.error.noSource, window.location.href); }; this.setup = function(){ Common.run(properties); }; }); var TV_TRWAM = (function() { var properties = new Configurator({ observer: { anchor: '#ipott', mode: 'removed', selector: 'div[data-name="playerWindowPlace"]' }, injection: { class: 'left_margin' }, chains: { videos: [ new Step({ urlTemplate: 'https://api-trwam.app.insysgo.pl/v1/Tile/GetTiles', headers: {'Content-Type': 'application/json'}, method: 'POST', methodParam: function(){ return getParamsForVideo(); }, after: function(json) { return getCodename(json); } }), new Step({ urlTemplate: 'https://api-trwam.app.insysgo.pl/v1/Player/AcquireContent?platformCodename=www&' + 'codename=#codename', after: function(output) { return grabData(output); } }) ] } }); var grabVideoIdFromUrl = function(input){ var match = input.match(/\/(vod\.[\d]+)$/); if(match && match[1]) { return match[1]; } throw new Exception(config.error.id, Tool.getRealUrl()); }; var getParamsForVideo = function(){ var mediaId = grabVideoIdFromUrl(window.location.href); return { platformCodename: "www", tilesIds:[mediaId] } }; var getCodename = function(json){ var tile = (json.Tiles || [])[0] || {}; return { title: tile.Title || {}, codename: tile.Codename || {} }; }; var grabData = function(data){ var streams = (((data || {}).MediaFiles || [])[0] || {}).Formats || []; var videoItems = grabVideoData(streams); var streamItems = grabStreamData(streams); if(videoItems.length > 0 || streamItems.length > 0){ return { cards: { videos: {items: videoItems}, streams: {items: streamItems} } } } throw new Exception(config.error.noSource, window.location.href); }; var grabVideoData = function(streams){ var items = []; $.each(streams, function( index, value ) { if(value.Type === 3){ items.push(Tool.mapDescription({ source: 'TRWAM', key: value.Type, url: value.Url })); } }); return items; }; var grabStreamData = function(streams){ var items = []; var types = [2, 9]; $.each(streams, function( index, value ) { if($.inArray(value.Type, types) > -1){ items.push(Tool.mapDescription({ source: 'TRWAM', key: value.Type, url: value.Url })); } }); return items; } this.setup = function(){ Common.run(properties); }; }); var VOD_FRAME = (function() { this.setup = function(){ var callback = function(data) { var srcArray = ['https://redir.atmcdn.pl', 'https://partner.ipla.tv']; setupDetector(srcArray, data); }; MessageReceiver.awaitMessage({ origin: 'https://vod.pl', windowReference: window.parent }, callback); }; var setupDetector = function(srcArray, data){ var selectors = createArrySelectors(srcArray); var multiSelector = createMultiSelector(selectors); var properties = Common.createProperties('div.iplaContainer', multiSelector); ElementDetector.detect(properties, function() { selectors.forEach(function(element){ if($(element.frameSelector).length > 0){ MessageReceiver.postUntilConfirmed({ windowReference: $(element.frameSelector).get(0).contentWindow, origin: element.src, message: { location: data.location } }); } }); }); }; var createArrySelectors = function(srcArray){ return jQuery.map(srcArray, function(src) { return { src: src, frameSelector: 'iframe[src^="' + src + '"]' } }); }; var createMultiSelector = function(selectors){ return $.map(selectors, function(src){ return src.frameSelector }).join(', '); } }); var Starter = (function(Starter) { var sources = [ {objectName: 'TVP', urlPattern: new RegExp( '^https:\/\/(vod|cyfrowa)\.tvp\.pl\/video\/.*$|' + '^https?:\/\/.*\.tvp\.(pl|info)\/sess\/TVPlayer2\/embed.*$|' + '^https?:\/\/((?!wiadomosci).)*\.tvp\.pl\/\\d{6,}\/.*$|' + '^https?:\/\/w{3}\.tvpparlament\.pl\/sess\/.*' ) }, {objectName: 'TVN', urlPattern: new RegExp('^https:\/\/(?:w{3}\.)?(?:tvn)?player\.pl\/')}, {objectName: 'CDA', urlPattern: new RegExp('^https:\/\/.*\.cda\.pl\/')}, {objectName: 'VOD', urlPattern: new RegExp('^https:\/\/vod.pl\/')}, {objectName: 'VOD_IPLA', urlPattern: new RegExp( '^https:\/\/partner\.ipla\.tv\/embed\/|' + '^https:\/\/.*\.redcdn\.pl\/file\/o2\/redefine\/partner\/' ) }, {objectName: 'IPLA', urlPattern: new RegExp('^https:\/\/w{3}\.ipla\.tv\/')}, {objectName: 'WP', urlPattern: new RegExp('^https:\/\/wideo\.wp\.pl\/')}, {objectName: 'NINATEKA', urlPattern: new RegExp('^https:\/\/ninateka.pl\/')}, {objectName: 'ARTE', urlPattern: new RegExp('^https:\/\/w{3}\.arte\.tv\/.*\/videos\/')}, {objectName: 'VOD_FRAME', urlPattern: new RegExp('^https:\/\/pulsembed\.eu\/')}, {objectName: 'TV_TRWAM', urlPattern: new RegExp('^https:\/\/tv-trwam\.pl\/local-vods\/')} ]; Starter.start = function() { sources.some(function(source){ if(source.urlPattern.exec(location.href)){ console.info('voddownloader: context: ' + source.objectName + ', url: ' + location.href); var object = eval('new ' + source.objectName + '()'); object.setup(); return true; } }); }; return Starter; }(Starter || {})); $(document).ready(function(){ DomTamper.injectStylesheet(window, config.include.fontawesome); DomTamper.injectStyle(window, 'buttons_css'); Starter.start(); }); }).bind(this)(jQuery, platform, Waves);