· 6 years ago · Dec 11, 2019, 12:16 PM
1// ==UserScript==
2// @name Skrypt umożliwiający pobieranie materiałów ze znanych serwisów VOD.
3// @version 5.11.1
4// @description Skrypt służący do pobierania materiałów ze znanych serwisów VOD.
5// Działa poprawnie tylko z rozszerzeniem Tampermonkey.
6// Cześć kodu pochodzi z:
7// miniskrypt.blogspot.com,
8// miniskrypt.hubaiitv.pl
9// @author Przmus, zacny
10// @namespace http://www.ipla.tv/
11// @source https://github.com/zacny/voddownloader
12// @include https://vod.tvp.pl/video/*
13// @include /^https://(bialystok|katowice|lodz|rzeszow|bydgoszcz|kielce|olsztyn|szczecin|gdansk|krakow|opole|warszawa|gorzow|lublin|poznan|wroclaw).tvp.pl/\d{6,}/
14// @include https://cyfrowa.tvp.pl/video/*
15// @include https://www.ipla.tv/*
16// @include https://player.pl/*
17// @include https://*.cda.pl/*
18// @include https://vod.pl/*
19// @include https://redir.atmcdn.pl/*
20// @include https://*.redcdn.pl/file/o2/redefine/partner/*
21// @include https://video.wp.pl/*
22// @include https://ninateka.pl/*
23// @include https://www.arte.tv/player/*
24// @exclude https://vod.pl/playerpl*
25// @exclude http://www.tvp.pl/sess/*
26// @exclude https://www.cda.pl/iframe/*
27// @grant GM_getResourceText
28// @grant GM_xmlhttpRequest
29// @grant GM_download
30// @grant GM_setClipboard
31// @grant GM_info
32// @connect tvp.pl
33// @connect getmedia.redefine.pl
34// @connect player-api.dreamlab.pl
35// @connect api.arte.tv
36// @run-at document-end
37// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js
38// @require https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js
39// @require https://cdnjs.cloudflare.com/ajax/libs/platform/1.3.5/platform.min.js
40// @require https://gitcdn.xyz/cdn/zacny/voddownloader/4b17a120f521eaddf476d6e8fe3be152d506f244/lib/js/mdb-with-waves-patch.js
41// @resource buttons_css https://raw.githubusercontent.com/zacny/voddownloader/master/lib/css/voddownloader-buttons.css
42// @resource content_css https://raw.githubusercontent.com/zacny/voddownloader/master/lib/css/voddownloader-content.css
43// ==/UserScript==
44
45(function vodDownloader($, platform, Waves) {
46 'use strict';
47
48 function Exception(error, templateParams) {
49 this.error = error;
50 this.templateParams = Array.isArray(templateParams) ? templateParams : [templateParams];
51 }
52
53 function Format(data) {
54 this.bitrate = 'brak danych';
55 this.playable = true;
56 $.extend(true, this, data);
57 }
58
59 var Tool = (function(Tool) {
60 Tool.deleteParametersFromUrl = function(url){
61 return decodeURIComponent(url.replace(/\?.*/,''));
62 };
63
64 Tool.getUrlParameter = function(paramName, url){
65 var results = new RegExp('[\?&]' + paramName + '=([^&#]*)').exec(url);
66 if (results==null) {
67 return null;
68 }
69 return decodeURIComponent(results[1]) || 0;
70 };
71
72 Tool.formatConsoleMessage = function(message, params){
73 console.log.apply(this, $.merge([message], params));
74 };
75
76 Tool.downloadFile = function(fileUrl, title){
77 var extension = Tool.deleteParametersFromUrl(fileUrl.split('.').pop());
78 var movieTitle = (title !== undefined && title !== '' ) ? title : 'nieznany';
79 var name = movieTitle + '.' + extension;
80 GM_download(fileUrl, name);
81 };
82
83 Tool.template = function(templates, ...keys){
84 return (function(...values) {
85 var dict = values[values.length - 1] || {};
86 var result = [templates[0]];
87 keys.forEach(function(key, i) {
88 var value = Number.isInteger(key) ? values[key] : dict[key];
89 result.push(value, templates[i + 1]);
90 });
91 return result.join('');
92 });
93 };
94
95 return Tool;
96 }(Tool || {}));
97
98 const config = {
99 attempts: 10,
100 attemptTimeout: 1500,
101 storageItem: 'voddownloader.doNotwarnIfIncorrectPluginSettingsDetected',
102 include: {
103 fontawesome: {
104 id: 'fontawesome',
105 css: 'https://use.fontawesome.com/releases/v5.8.2/css/all.css'
106 },
107 bootstrap: {
108 id: 'bootstrap',
109 css: 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css'
110 },
111 mdb: {
112 id: 'mdb',
113 css: 'https://cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.8.2/css/mdb.min.css',
114 }
115 },
116 error: {
117 id: {
118 caption: 'Nie udało się odnaleźć idetyfikatora.',
119 template: Tool.template`Algorytm rozpoznawania identyfikatora wideo na stronie: "${0}" \
120 zakończył się niepowodzeniem. Może to oznaczać błąd skryptu.`,
121 },
122 tvnId: {
123 caption: 'Nie udało się odnaleźć idetyfikatora.',
124 template: Tool.template`Algorytm rozpoznawania identyfikatora wideo na stronie: "${0}" \
125 zakończył się niepowodzeniem.\nJeżeli jest to główna strona programu oznacza to, \
126 że nie udało się odnaleźć identyfikatora ostatniego odcinka. Wejdź na stronę odcinka \
127 i spróbuj ponownie.\nMoże to również oznaczać błąd skryptu.`,
128 },
129 call: {
130 caption: 'Błąd pobierania informacji o materiale.',
131 template: Tool.template`Błąd w wykonaniu kroku asynchronicznego z indeksem: ${0} na stronie: "${1}"
132 Zgłoś problem autorom skryptu.`,
133 },
134 noSource: {
135 caption: 'Nie udało się odnaleźć źródeł do materiału.',
136 template: Tool.template`Materiał ze strony ${0} nie posiada zdefiniowanych źródeł, które mogłyby zostać \
137 wyświetlone. \nMoże to oznaczać, że nie jest on publicznie dostępny, dostępne źródła nie mogą zostać \
138 wyświetlone w przeglądarce bez dodatkowego oprogramowania lub jest umieszczony w płatnej strefie.`,
139 type: 'info'
140 },
141 timeout: {
142 caption: 'Zbyt długi czas odpowiedzi.',
143 template: Tool.template`Dla kroku asychronicznego z indeksem: ${0} na stronie "${1}" nie dotarły \
144 informacje zwrotne.\nPrzypuszczalnie jest to problem sieciowy. Spróbuj ponownie za jakiś czas.`
145 }
146 }
147 };
148
149
150 var AsyncStep = (function(AsyncStep){
151 AsyncStep.setup = function(properties){
152 var step = {
153 urlTemplate: '',
154 beforeStep: function(input){return input},
155 afterStep: function (output) {return output},
156 resolveUrl: function (input) {
157 var url = this.urlTemplate;
158 var urlParams = {};
159 $.each(input, function (key, value) {
160 url = url.replace(new RegExp('#'+key,'g'), value);
161 urlParams[key] = value;
162 });
163
164 return {
165 url: url,
166 urlParams: urlParams
167 };
168 }
169 };
170
171 return $.extend(true, step, properties);
172 };
173 return AsyncStep;
174 }(AsyncStep || {}));
175
176 var Notification = (function(Notification) {
177 var create = function(title, bodyContent, special) {
178 var specialContentClasses = special ? ' special-color white-text' : '';
179 var content = $('<div>').addClass('toast notification' + specialContentClasses).attr('role', 'alert')
180 .attr('aria-live', 'assertive').attr('aria-atomic', 'true')
181 .attr('name', special ? 'special' : 'normal').attr('data-delay', '5000');
182 var header = $('<div>').addClass('toast-header special-color-dark white-text');
183 var warnIcon = $('<i>').addClass('fas fa-exclamation-triangle pr-2');
184 var notificationTitle = $('<strong>').addClass('mr-auto').text(title);
185 var time = $('<small>').text(new Date().toLocaleTimeString());
186 var close = $('<button>').attr('type', 'button').addClass('ml-2 mb-1 close white-text')
187 .attr('data-dismiss', 'toast').attr('aria-label', 'Close')
188 .append($('<span>').attr('aria-hidden', 'true').text('\u00D7'));
189
190 if(special){
191 header.append(warnIcon);
192 content.attr('data-autohide', 'false');
193 }
194 header.append(notificationTitle).append(time).append(close);
195 var body = $('<div>').addClass('toast-body notification-body').append(bodyContent);
196
197 content.append(header).append(body);
198 return content;
199 };
200
201 Notification.show = function(options, w){
202 options = options || {};
203 var special = false;
204 if (options.hasOwnProperty('special')) {
205 special = options.special;
206 }
207 if(!options.hasOwnProperty('title') || !options.hasOwnProperty('content')){
208 return;
209 }
210
211 var rootElement = $(w.document.body);
212 var notification = create(options.title, options.content, special);
213 $('#notification-container', rootElement).append(notification);
214 $('.toast', rootElement).toast('show');
215 $('.toast', rootElement).on('hidden.bs.toast', function (){
216 $.each($(this), function(index, value) {
217 var element = $(value);
218 element.remove();
219 });
220 })
221 };
222
223 return Notification;
224 }(Notification || {}));
225
226 var PluginSettingsDetector = (function(PluginSettingsDetector){
227 var prepareWarningNotification = function(w) {
228 var bodyContent = $('<div>')
229 .append('Twój dodatek ma nieprawidłowe ustawienia, przez co nie możesz korzystać z opcji ')
230 .append('bezpośredniego pobierania plików. Możesz skorygować je w następujący sposób:');
231 var list = $('<ol>').addClass('m-0')
232 .append($('<li>').text('Otwórz Panel sterowania Tampermonkey i kliknij ustawienia.'))
233 .append($('<li>').text('Ogólne > Tryb konfiguracji > Expert'))
234 .append($('<li>').text('Pobieranie BETA > Tryb pobierania > API przeglądarki'))
235 .append($('<li>').text('Zapisz ustawienia, a jeżeli przeglądarka zapyta o możliwość zarządzania' +
236 ' pobieranymi plikami, należy się zgodzić'));
237 bodyContent.append(list).append(createButton(w));
238 var options = {title: 'Wykryto problem', content: bodyContent, special: true};
239 Notification.show(options, w);
240 };
241
242 var createButton = function(w){
243 return $('<button>').attr('type', 'button').addClass('btn btn-dark btn-sm m-1 pl-3 pr-3')
244 .append($('<i>').addClass('fas pr-1 fa-window-close')).append('Nie pokazuj więcej').click(function(){
245 var rootElement = $(w.document.body);
246 w.localStorage.setItem(config.storageItem, true);
247 $('.toast.special-color', rootElement).toast('hide');
248 setTimeout(function(){
249 $('.toast.special-color', rootElement).remove();
250 }, 1000);
251 });
252 };
253
254 var disableDownload = function(w){
255 var rootElement = $(w.document.body);
256 $('.fa-save', rootElement).closest('button').attr('disabled', true);
257 };
258
259 PluginSettingsDetector.detect = function(w){
260 var downloadMode = GM_info.downloadMode;
261 if(downloadMode !== 'browser'){
262 disableDownload(w);
263 var value = w.localStorage.getItem(config.storageItem);
264 if(value !== 'true'){
265 prepareWarningNotification(w);
266 }
267 }
268 };
269 return PluginSettingsDetector;
270 }(PluginSettingsDetector || {}));
271
272 var DomTamper = (function(DomTamper){
273
274 DomTamper.injectStyle = function(w, name){
275 var head = $(w.document.head);
276 if(!head.find('style[name="' + name + '"]').length){
277 var styleElement = $('<style>').attr('type', 'text/css')
278 .attr('name', name).text((GM_getResourceText(name)));
279 head.append(styleElement);
280 }
281 };
282
283 var injectStylesheet = function (w, setting) {
284 var head = $(w.document.head);
285 if(!head.find('link[name="' + setting.id + '"]').length){
286 var stylesheet = $('<link>').attr('name', setting.id).attr('type', 'text/css').attr('rel', 'stylesheet')
287 .attr('href', setting.css);
288 head.append(stylesheet);
289 }
290 };
291
292 var prepareHead = function(w){
293 injectStylesheet(w, config.include.fontawesome);
294 injectStylesheet(w, config.include.bootstrap);
295 injectStylesheet(w, config.include.mdb);
296 DomTamper.injectStyle(w, 'content_css');
297 };
298
299 var createBugReportLink = function(w, additionalClass){
300 var button = $('<button>').attr('id', 'bug-report-button').attr('type', 'button')
301 .addClass('btn btn-sm m-0').addClass(additionalClass)
302 .append($('<i>').addClass('fas fa-bug'));
303 button.click(function(){
304 w.open('https://github.com/zacny/voddownloader/issues');
305 });
306 return $('<div>').addClass('bug-report-position').append(button);
307 };
308
309 var prepareBody = function(w, pageContent, detection) {
310 appendOrReplace(w, pageContent);
311 attachWaveEffect(w, pageContent);
312 if(detection) {
313 PluginSettingsDetector.detect(w);
314 }
315 };
316
317 var appendOrReplace = function (w, pageContent) {
318 var body = $(w.document.body);
319 if(body.children().length > 0){
320 body.children(":first").replaceWith(pageContent);
321 }
322 else {
323 body.append(pageContent);
324 }
325 };
326
327 var attachWaveEffect = function(w, pageContent){
328 var buttons = pageContent.find('.btn:not(.btn-flat), .btn-floating');
329 Waves.attach(buttons, ['waves-light']);
330 Waves.init({}, w);
331 };
332
333 DomTamper.handleError = function(exception, w){
334 if(w === undefined){
335 w = window.open();
336 }
337
338 prepareHead(w);
339 var errorData = getErrorData(exception);
340 var pageContent = $('<div>').addClass('page-content');
341 pageContent.append(createErrorContent(errorData));
342 pageContent.append(createBugReportLink(w, errorData.type === 'error' ?
343 'btn-danger' : 'special-color white-text'));
344 prepareBody(w, pageContent);
345 };
346
347 var getErrorData = function(exception){
348 var type = 'error';
349 var caption = 'Niespodziewany błąd';
350 var message = 'Natrafiono na niespodziewany błąd: ' + exception;
351 if(exception.error){
352 message = exception.error.template.apply(this, exception.templateParams).replace(/\n/g, '<br/>');
353 caption = exception.error.caption;
354 type = exception.error.type !== undefined ? exception.error.type : 'error';
355 }
356
357 return {
358 message: message,
359 caption: caption,
360 type: type
361 }
362 };
363
364 var createErrorContent = function(errorData){
365 var typeClass = errorData.type === 'error' ? 'bg-danger' : 'bg-dark';
366 var card = $('<div>').addClass('card text-white mb-3').addClass(typeClass);
367 var cardHeader = $('<div>').addClass('card-header')
368 .text('Niestety natrafiono na problem, który uniemożliwił dalsze działanie');
369 var cardBody = $('<div>').addClass('card-body')
370 .append($('<h5>').addClass('card-title').text(errorData.caption))
371 .append($('<div>').addClass('card-text text-white mb-3').append(errorData.message))
372 .append($('<div>').addClass('card-text text-white')
373 .append('Informacje o systemie: ').append(platform.description))
374 .append($('<div>').addClass('card-text text-white')
375 .append('Wersja pluginu: ').append(GM_info.version));
376 card.append(cardHeader).append(cardBody);
377 return card;
378 };
379
380 DomTamper.createButton = function(properties){
381 properties.wrapper.get().find('#'+properties.button.id).remove();
382 var button = $('<input>').attr('id', properties.button.id).attr('type', 'button')
383 .attr('style', properties.button.style).attr('value', 'Pobierz video').addClass(properties.button.class);
384 button.bind('click', properties.button.click);
385 properties.wrapper.get().append(button);
386 };
387
388 DomTamper.createLoader = function(w){
389 prepareHead(w);
390 var pageContent = $('<div>').addClass('page-content');
391 pageContent.append(createLoaderContent());
392 pageContent.append(createBugReportLink(w, 'special-color white-text'));
393 prepareBody(w, pageContent);
394 Unloader.init(w);
395 };
396
397 var createLoaderContent = function(){
398 var card = $('<div>').addClass('card text-white bg-dark');
399 var cardHeader = $('<div>').addClass('card-header').text('Poczekaj trwa wczytywanie danych...');
400 var cardBody = $('<div>').addClass('card-body');
401 var bodyContainer = $('<div>').addClass('d-flex justify-content-center m-3');
402 var spinner = $('<div>').addClass('spinner-border spinner-size').attr('role', 'status')
403 .append($('<span>').addClass('sr-only').text('Loading...'));
404 cardBody.append(bodyContainer.append(spinner));
405 card.append(cardHeader).append(cardBody);
406
407 return card;
408 };
409
410 var createAction = function(iconClass, label){
411 return $('<button>').attr('type', 'button').addClass('btn btn-dark btn-sm m-1 pl-3 pr-3')
412 .append($('<i>').addClass('fas pr-1').addClass(iconClass)).append(label);
413 };
414
415 var downloadActionClick = function (data, w) {
416 var options = {title: 'Rozpoczęto pobieranie pliku', content: data.title};
417 Tool.downloadFile(data.value.url, data.title);
418 Notification.show(options, w);
419 };
420
421 var copyActionClick = function (data, w) {
422 GM_setClipboard(data.value.url);
423 var options = {title: 'Kopiowanie', content: 'Skopiowano do schowka'};
424 Notification.show(options, w);
425 };
426
427 var openActionClick = function (data, w) {
428 w.open(data.value.url);
429 };
430
431 var createRow = function(data, rowClass, w){
432 var actions = $('<td>').attr('scope', 'row').addClass('actions-row');
433 actions.append(createAction('fa-save', 'Zapisz').click(
434 function(){downloadActionClick(data, w)})
435 );
436 actions.append(createAction('fa-clone', 'Kopiuj').click(
437 function() {copyActionClick(data, w)})
438 );
439 actions.append(createAction('fa-film', 'Otwórz').click(
440 function() {openActionClick(data, w)})
441 );
442
443 var descriptionHtml = $('<div>').append($('<b>').text('bitrate: ')).append($('<span>').text(data.value.bitrate));
444 if(data.value.quality) {
445 descriptionHtml.append($('<span>').text(', ')).append($('<b>').text('rozdzielczość: '))
446 .append($('<span>').text(data.value.quality));
447 }
448 if(data.value.langDesc){
449 descriptionHtml.append($('<span>').text(', ')).append($('<b>').text('wersja językowa: '))
450 .append($('<span>').text(data.value.langDesc));
451 }
452 var description = $('<td>').html(descriptionHtml);
453
454 return $('<tr>').append(actions).append(description);
455 };
456
457 var createTable = function(data, w){
458 var table = $('<table>').addClass('table table-bordered table-striped btn-table')
459 .append($('<thead>').addClass('black white-text')
460 .append($('<tr>').append($('<th>').attr('scope', 'col').attr('colspan', 2).text(data.title)))
461 );
462 var tbody = $('<tbody>');
463 table.append(tbody);
464 $.each(data.formats, function(index, value) {
465 var rowClass = index === 0 ? 'best-quality' : '';
466 var params = {
467 value: value,
468 title: data.title
469 };
470 tbody.append(createRow(params, rowClass, w));
471 });
472
473 return table;
474 };
475
476 var setWindowTitle = function(data, w){
477 var head = $(w.document.head);
478 var title = head.find('title');
479 if(title.length) {
480 title.text(data.title);
481 }
482 else {
483 head.append($('<title>').text(data.title));
484 }
485 };
486
487 DomTamper.createDocument = function(service, data, w){
488 service.formatter(data);
489
490 prepareHead(w);
491 setWindowTitle(data, w);
492 var pageContent = $('<div>').addClass('page-content');
493 pageContent.append(createTable(data, w));
494 pageContent.append(createBugReportLink(w, 'special-color white-text'));
495 pageContent.append(createNotificationContainer());
496 prepareBody(w, pageContent, true);
497 Unloader.init(w);
498 };
499 var createNotificationContainer = function(){
500 return $('<div>').attr('id', 'notification-container')
501 .attr('aria-live', 'polite').attr('aria-atomic', 'true').addClass('notification-container');
502 };
503
504 return DomTamper;
505 }(DomTamper || {}));
506
507 var Executor = (function(Executor){
508 var executeAsync = function(service, options, w){
509 var exceptionParams = [options.stepIndex, window.location.href];
510 var resolveUrl = beforeStep(service, options);
511 var requestParams = {
512 method: 'GET',
513 url: resolveUrl.url,
514 responseType: 'json',
515 onload: function(data) {
516 options.data = data.response || {};
517 asyncCallback(service, options, w);
518 },
519 onerror: function(){
520 DomTamper.handleError(new Exception(config.error.call, exceptionParams), w);
521 },
522 ontimeout: function(){
523 DomTamper.handleError(new Exception(config.error.timeout, exceptionParams), w);
524 }
525 };
526 GM_xmlhttpRequest(requestParams);
527 };
528
529 var beforeStep = function(service, options){
530 var steps = service.asyncChains[options.chainName];
531 var currentStep = steps[options.stepIndex];
532 var result = currentStep.beforeStep(options.data);
533 if(typeof result === 'string' || typeof result == 'number'){
534 result = {
535 videoId: result
536 }
537 }
538 if(options.urlParams){
539 $.extend(true, options.urlParams, result);
540 }
541 else {
542 options.urlParams = result;
543 }
544 return currentStep.resolveUrl(options.urlParams);
545 };
546
547 var afterStep = function(service, options) {
548 var steps = service.asyncChains[options.chainName];
549 var currentStep = steps[options.stepIndex];
550 var output = currentStep.afterStep(options.data);
551 options.data = output;
552 options.stepIndex += 1;
553 return steps[options.stepIndex];
554 };
555
556 var asyncCallback = function(service, options, w){
557 try {
558 var nextStep = afterStep(service, options);
559 if(nextStep !== undefined) {
560 return Promise.resolve().then(
561 Executor.asyncChain(service, options, w)
562 );
563 }
564 else {
565 return Promise.resolve().then(
566 service.onDone(options.data, w)
567 );
568 }
569 }
570 catch(e){
571 DomTamper.handleError(e, w);
572 }
573 };
574
575 Executor.asyncChain = function(service, options, w){
576 try {
577 if(w === undefined){
578 w = window.open();
579 DomTamper.createLoader(w);
580 }
581
582 executeAsync(service, options, w);
583 }
584 catch(e){
585 DomTamper.handleError(e, w);
586 }
587 };
588
589 return Executor;
590 }(Executor || {}));
591
592 var Configurator = (function(Configurator){
593 Configurator.setup = function(properties){
594 var service = {
595 wrapper: {
596 selector: '',
597 get: function(){
598 return $(service.wrapper.selector);
599 },
600 exist: function(){
601 return $(service.wrapper.selector).length > 0;
602 }
603 },
604 button: {
605 id: 'direct-download',
606 style: '',
607 class: '',
608 click: function(){
609 var chainName = service.chainSelector();
610 Executor.asyncChain(service, {
611 stepIndex: 0,
612 chainName: chainName
613 });
614 }
615 },
616 asyncChains: {
617 default: []
618 },
619 chainSelector: function(){
620 return "default";
621 },
622 formatter: function(data){
623 data.formats.sort(function (a, b) {
624 return b.bitrate - a.bitrate;
625 });
626 },
627 onDone: function(data, w) {
628 DomTamper.createDocument(service, data, w);
629 }
630 };
631
632 return $.extend(true, service, properties);
633 };
634 return Configurator;
635 }(Configurator || {}));
636
637 var ChangeVideoDetector = (function(ChangeVideoDetector){
638 var checkVideoChange = function(oldSrc, videoChangeCallback) {
639 var src = window.location.href;
640 if(src !== undefined && oldSrc !== src){
641 return Promise.resolve().then(videoChangeCallback);
642 }
643 else {
644 return Promise.resolve().then(
645 setTimeout(checkVideoChange, config.attemptTimeout, oldSrc, videoChangeCallback)
646 );
647 }
648 };
649
650 ChangeVideoDetector.run = function(videoChangeCallback){
651 var src = window.location.href;
652 checkVideoChange(src, videoChangeCallback);
653 };
654 return ChangeVideoDetector;
655 }(ChangeVideoDetector || {}));
656
657 var WrapperDetector = (function(WrapperDetector){
658 var onWrapperExist = function(properties){
659 if(properties.wrapper.exist()) {
660 DomTamper.createButton(properties);
661 }
662 else {
663 console.info("Nie mam nic do zrobienia");
664 }
665 };
666
667 var checkWrapperExist = function(attempt, properties){
668 logWrapperMessage(properties.wrapper, attempt);
669 if (properties.wrapper.exist() || attempt == 0) {
670 return Promise.resolve().then(onWrapperExist(properties));
671 } else {
672 attempt = (attempt > 0) ? attempt-1 : attempt;
673 return Promise.resolve().then(
674 setTimeout(checkWrapperExist, config.attemptTimeout, attempt, properties)
675 );
676 }
677 };
678
679 var logWrapperMessage = function(wrapper, attempt){
680 var existColor = wrapper.exist() ? 'color:green' : 'color:red';
681 var params = [
682 existColor, wrapper.selector, 'color:gray',
683 'color:black;font-weight: bold', attempt, 'color:gray'
684 ];
685 Tool.formatConsoleMessage('check for: "%c%s%c" [%c%s%c]', params);
686 };
687
688 WrapperDetector.run = function(properties, videoChangeCallback) {
689 checkWrapperExist(config.attempts, properties);
690 if(typeof videoChangeCallback === "function"){
691 ChangeVideoDetector.run(videoChangeCallback);
692 }
693 };
694 return WrapperDetector;
695 }(WrapperDetector || {}));
696
697 var Unloader = (function(Unloader) {
698 var win;
699
700 Unloader.init = function(w){
701 win = w;
702 $(window).bind('beforeunload', function(){
703 if(!win.closed) {
704 win.close();
705 }
706 });
707 };
708
709 return Unloader;
710 }(Unloader || {}));
711
712 var VOD_TVP = (function(VOD_TVP) {
713 var properties = Configurator.setup({
714 wrapper: {
715 selector: 'div.playerContainerWrapper'
716 },
717 button: {
718 class: 'video-block__btn tvp_vod_downlaod_button',
719 },
720 asyncChains: {
721 default: [
722 AsyncStep.setup({
723 urlTemplate: 'https://tvp.pl/pub/stat/videofileinfo?video_id=#videoId',
724 beforeStep: function (input) {
725 return idParser();
726 }
727 }),
728 AsyncStep.setup({
729 urlTemplate: 'https://www.tvp.pl/shared/cdn/tokenizer_v2.php?object_id=#videoId',
730 beforeStep: function (json) {
731 return getRealVideoId(json);
732 },
733 afterStep: function (output) {
734 return VOD_TVP.grabVideoFormats(output);
735 }
736 })
737 ]
738 }
739 });
740
741 var idParser = function() {
742 var src = $('div.playerContainer').attr('data-id');
743 if(src !== undefined){
744 return {
745 videoId: src.split("/").pop()
746 };
747 }
748
749 throw new Exception(config.error.id, window.location.href);
750 };
751
752 var getRealVideoId = function(json){
753 var videoId = json.copy_of_object_id !== undefined ?
754 json.copy_of_object_id : json.video_id;
755 return {
756 videoId: videoId
757 };
758 };
759
760 VOD_TVP.grabVideoFormats = function(data){
761 var formats = [];
762 if(data.status == 'OK' && data.formats !== undefined){
763 $.each(data.formats, function( index, value ) {
764 if(value.adaptive == false){
765 formats.push(new Format({
766 bitrate: value.totalBitrate,
767 url: value.url
768 }));
769 }
770 });
771 return {
772 title: data.title,
773 formats: formats
774 };
775 }
776 throw new Exception(config.error.noSource, window.location.href);
777 };
778
779 VOD_TVP.waitOnWrapper = function(){
780 WrapperDetector.run(properties);
781 };
782
783 return VOD_TVP;
784 }(VOD_TVP || {}));
785
786 var CYF_TVP = (function(CYF_TVP) {
787 var properties = Configurator.setup({
788 wrapper: {
789 selector: 'div.playerContainerWrapper'
790 },
791 button: {
792 class: 'video-block__btn tvp_cyf_downlaod_button'
793 },
794 asyncChains: {
795 default: [
796 AsyncStep.setup({
797 urlTemplate: 'https://www.tvp.pl/shared/cdn/tokenizer_v2.php?object_id=#videoId',
798 beforeStep: function (input) {
799 return idParser();
800 },
801 afterStep: function (output) {
802 return VOD_TVP.grabVideoFormats(output);
803 }
804 })
805 ]
806 }
807 });
808
809 var idParser = function(){
810 var src = $('iframe#JS-TVPlayer').attr('src');
811 if(src !== undefined) {
812 return src.split("/").pop();
813 }
814
815 throw new Exception(config.error.id, window.location.href);
816 };
817
818 CYF_TVP.waitOnWrapper = function(){
819 WrapperDetector.run(properties);
820 };
821
822 return CYF_TVP;
823 }(CYF_TVP || {}));
824
825 var TVP_REG = (function(TVP_REG) {
826 var properties = Configurator.setup({
827 wrapper: {
828 selector: 'div.js-video'
829 },
830 button: {
831 class: 'tvp_reg_download_button'
832 },
833 asyncChains: {
834 default: [
835 AsyncStep.setup({
836 urlTemplate: 'https://www.tvp.pl/shared/cdn/tokenizer_v2.php?object_id=#videoId',
837 beforeStep: function (input) {
838 return idParser();
839 },
840 afterStep: function (output) {
841 return VOD_TVP.grabVideoFormats(output);
842 }
843 })
844 ]
845 }
846 });
847
848 var idParser = function(){
849 var dataId = $('div.js-video').attr('data-object-id');
850 if(dataId != undefined) {
851 return dataId;
852 }
853
854 throw new Exception(config.error.id, window.location.href);
855 };
856
857 TVP_REG.waitOnWrapper = function(){
858 WrapperDetector.run(properties);
859 };
860
861 return TVP_REG;
862 }(TVP_REG || {}));
863
864 var TVN = (function(TVN) {
865 var properties = Configurator.setup({
866 wrapper: {
867 selector: '#player-container'
868 },
869 button: {
870 class: 'btn btn-primary tvn_download_button'
871 },
872 asyncChains: {
873 default: [
874 AsyncStep.setup({
875 urlTemplate: '/api/?platform=ConnectedTV&terminal=Panasonic&format=json' +
876 '&authKey=064fda5ab26dc1dd936f5c6e84b7d3c2&v=3.1&m=getItem&id=#videoId',
877 beforeStep: function(input){
878 return idParser();
879 },
880 afterStep: function(output) {
881 return formatParser(output);
882 }
883 })
884 ]
885 }
886 });
887
888 var idParser = function(){
889 var watchingNow = $('.watching-now').closest('.embed-responsive').find('.embed-responsive-item');
890 if(watchingNow.length > 0){
891 return watchingNow.attr('href').split(',').pop();
892 }
893
894 return episodeIdParser();
895 };
896
897 var episodeIdParser = function () {
898 var match = window.location.href.match(/odcinki,(\d+)\/.*,(\d+)/);
899 if(match && match[2]){
900 return match[2];
901 }
902
903 return serialIdParser();
904 };
905
906 var serialIdParser = function () {
907 var match = window.location.href.match(/odcinki,(\d+)/);
908 if(match && match[1]){
909 throw new Exception(config.error.tvnId, window.location.href);
910 }
911
912 return vodIdParser();
913 };
914
915 var vodIdParser = function(){
916 var match = window.location.href.match(/,(\d+)/);
917 if(match && match[1]){
918 return match[1];
919 }
920
921 throw new Exception(config.error.tvnId, window.location.href);
922 };
923
924 var formatParser = function(data){
925 var formats = [];
926 var title;
927 var video_content = (((data.item || {}).videos || {}).main || {}).video_content || {};
928 if(video_content && video_content.length > 0){
929 $.each(video_content, function( index, value ) {
930 var lastPartOfUrl = Tool.deleteParametersFromUrl(value.url).split("/").pop();
931 var bitrate = lastPartOfUrl.match(/\d{2,}/g);
932 formats.push(new Format({
933 quality: value.profile_name,
934 bitrate: bitrate,
935 url: value.url
936 }));
937 });
938 title = data.item.episode != null ? 'E'+data.item.episode : '';
939 title = data.item.season != null ? 'S'+data.item.season + title : title;
940 if(data.item.serie_title != null){
941 title = data.item.serie_title + (title != '' ? ' - ' + title : '');
942 }
943
944 return {
945 title: title,
946 formats: formats
947 }
948 }
949 throw new Exception(config.error.noSource, window.location.href);
950 };
951
952 TVN.waitOnWrapper = function(){
953 WrapperDetector.run(properties, TVN.waitOnWrapper);
954 };
955
956 return TVN;
957 }(TVN || {}));
958
959 var IPLA = (function(IPLA) {
960 var properties = Configurator.setup({
961 wrapper: {
962 selector: 'div.player-wrapper:visible:first-child, div.promo-box:visible:first-child,' +
963 ' div.player-error-presentation:visible:first-child'
964 },
965 button: {
966 class: 'ipla_download_button'
967 },
968 asyncChains: {
969 default: [
970 AsyncStep.setup({
971 urlTemplate: 'https://getmedia.redefine.pl/vods/get_vod/?cpid=1' +
972 '&ua=www_iplatv_html5/12345&media_id=#videoId',
973 beforeStep: function (input) {
974 return idParser();
975 },
976 afterStep: function (output) {
977 return IPLA.grabVideoFormats(output);
978 }
979 })
980 ]
981 }
982 });
983
984 var idParser = function(){
985 var match = location.href.match(/[\a-z\d]{32}/);
986 if(match && match[0]) {
987 return match[0];
988 }
989
990 return grabVideoIdFromWatchingNowElement();
991 };
992
993 IPLA.waitOnWrapper = function(){
994 WrapperDetector.run(properties, IPLA.waitOnWrapper);
995 };
996
997 IPLA.grabVideoFormats = function(data){
998 var formats = [];
999 var vod = data.vod || {};
1000 if(vod.copies && vod.copies.length > 0){
1001 $.each(vod.copies, function( index, value ) {
1002 formats.push(new Format({
1003 bitrate: value.bitrate,
1004 url: value.url,
1005 quality: value.quality_p
1006 }));
1007 });
1008 return {
1009 title: vod.title,
1010 formats: formats
1011 }
1012 }
1013 throw new Exception(config.error.noSource, window.location.href);
1014 };
1015
1016 var grabVideoIdFromWatchingNowElement = function(){
1017 var href = $('div.vod-image-wrapper__overlay').closest('a').attr('href');
1018 if(href !== undefined){
1019 var match = href.match(/[\a-z\d]{32}/);
1020 if(match && match[0]){
1021 return match[0];
1022 }
1023 }
1024 return grabVideoIdFromHtmlElement();
1025 };
1026
1027 var grabVideoIdFromHtmlElement = function(){
1028 var frameSrc = $('app-commercial-wallpaper iframe:first-child').attr('src');
1029 if(frameSrc !== undefined) {
1030 return Tool.getUrlParameter('vid', frameSrc);
1031 }
1032
1033 throw new Exception(config.error.id, window.location.href);
1034 };
1035
1036 return IPLA;
1037 }(IPLA || {}));
1038
1039 var VOD = (function(VOD) {
1040 var properties = Configurator.setup({
1041 wrapper: {
1042 selector: '#v_videoPlayer'
1043 },
1044 button: {
1045 class: 'vod_download_button'
1046 },
1047 asyncChains: {
1048 default: [
1049 AsyncStep.setup({
1050 urlTemplate: 'https://player-api.dreamlab.pl/?body[id]=#videoId&body[jsonrpc]=2.0' +
1051 '&body[method]=get_asset_detail&body[params][ID_Publikacji]=#videoId' +
1052 '&body[params][Service]=vod.onet.pl&content-type=application/jsonp' +
1053 '&x-onet-app=player.front.onetapi.pl&callback=',
1054 beforeStep: function (input) {
1055 return idParser();
1056 },
1057 afterStep: function (output) {
1058 return formatParser(output);
1059 }
1060 })
1061 ]
1062 }
1063 });
1064
1065 var idParser = function () {
1066 var id = $(".mvp").attr('id');
1067 if(id !== undefined){
1068 return id.match(/mvp:(.+)/)[1];
1069 }
1070
1071 return parseFromJS();
1072 };
1073
1074 var parseFromJS = function(){
1075 var scripts = $('script[type="text/javascript"]').filter(':not([src])');
1076 for (var i = 0; i < scripts.length; i++) {
1077 var match = $(scripts[i]).text().match(/\"mvpId\"\s*:\s*\"(\d+\.\d+)\"/);
1078 if(match && match[1]){
1079 return match[1];
1080 }
1081 }
1082
1083 throw new Exception(config.error.id, window.location.href);
1084 };
1085
1086 var formatParser = function (data) {
1087 var formats = [];
1088 var video = (((data.result || new Array())[0] || {}).formats || {}).wideo || {};
1089 var meta = ((data.result || new Array())[0] || {}).meta || {};
1090 var videoData = video['mp4-uhd'] && video['mp4-uhd'].length > 0 ? video['mp4-uhd'] : video['mp4'];
1091 if(videoData && videoData.length > 0){
1092 $.each(videoData, function( index, value ) {
1093 formats.push(new Format({
1094 quality: value.vertical_resolution,
1095 bitrate: value.video_bitrate,
1096 url: value.url
1097 }));
1098 });
1099
1100 return {
1101 title: meta.title,
1102 formats: formats
1103 }
1104 }
1105 throw new Exception(config.error.noSource, window.location.href);
1106 };
1107
1108 var isTopWindow = function(){
1109 return window.top === window.self;
1110 };
1111
1112 var iplaSectionDetected = function(){
1113 return $('#v_videoPlayer div.pulsembed_embed').length > 0;
1114 };
1115
1116 VOD.waitOnWrapper = function(){
1117 if(isTopWindow() && !iplaSectionDetected()){
1118 WrapperDetector.run(properties);
1119 }
1120 };
1121
1122 return VOD;
1123 }(VOD || {}));
1124
1125 var VOD_IPLA = (function(VOD_IPLA) {
1126 var properties = Configurator.setup({
1127 wrapper: {
1128 selector: '#player-wrapper'
1129 },
1130 button: {
1131 class: 'vod_ipla_downlaod_button'
1132 },
1133 asyncChains: {
1134 default: [
1135 AsyncStep.setup({
1136 urlTemplate: 'https://getmedia.redefine.pl/vods/get_vod/?cpid=1&ua=www_iplatv_html5/12345' +
1137 '&media_id=#videoId',
1138 beforeStep: function (input) {
1139 return idParser();
1140 },
1141 afterStep: function (output) {
1142 return IPLA.grabVideoFormats(output);
1143 }
1144 })
1145 ]
1146 }
1147 });
1148
1149 var idParser = function(){
1150 try {
1151 var match = $('script:not(:empty)').text().match(/(window\.CP\.embedSetup\()(.*)\);/);
1152 var jsonObject = JSON.parse(match[2]);
1153 return JSON.parse(jsonObject[0].media).result.mediaItem.id;
1154 }
1155 catch(e){
1156 throw new Exception(config.error.id, window.location.href);//incorrect page url
1157 }
1158 };
1159
1160 VOD_IPLA.waitOnWrapper = function(){
1161 WrapperDetector.run(properties);
1162 };
1163
1164 return VOD_IPLA;
1165 }(VOD_IPLA || {}));
1166
1167 var WP = (function(WP) {
1168 var properties = Configurator.setup({
1169 wrapper: {
1170 selector: '#mainPlayer'
1171 },
1172 button: {
1173 class: 'material__category wp_download_button'
1174 },
1175 asyncChains: {
1176 default: [
1177 AsyncStep.setup({
1178 urlTemplate: 'https://video.wp.pl/player/mid,#videoId,embed.json',
1179 beforeStep: function (input) {
1180 return idParser();
1181 },
1182 afterStep: function (output) {
1183 return grabVideoFormats(output);
1184 }
1185 })
1186 ]
1187 }
1188 });
1189
1190 var idParser = function () {
1191 try {
1192 var pageURL = window.location.href;
1193 var regexp = new RegExp('mid,(\\d+),cid');
1194 var match = regexp.exec(pageURL);
1195 return match[1];
1196 }
1197 catch(e){
1198 throw new Exception(config.error.id, window.location.href);
1199 }
1200 };
1201
1202 var grabVideoFormats = function(data){
1203 var formats = [];
1204 var urls = (data.clip || {}).url || {};
1205 if(urls && urls.length > 0){
1206 $.each(urls, function( index, value ) {
1207 if(value.type === 'mp4@avc'){
1208 formats.push(new Format({
1209 bitrate: value.quality,
1210 url: 'http:' + value.url,
1211 quality: value.resolution
1212 }));
1213 }
1214 });
1215 }
1216 return {
1217 title: data.clip.title,
1218 formats: formats
1219 }
1220 };
1221
1222 WP.waitOnWrapper = function(){
1223 WrapperDetector.run(properties, WP.waitOnWrapper);
1224 };
1225
1226 return WP;
1227 }(WP || {}));
1228
1229 var CDA = (function(CDA) {
1230 var properties = Configurator.setup({
1231 wrapper: {
1232 selector: '.pb-video-player-wrap'
1233 },
1234 button: {
1235 class: 'cda_download_button',
1236 click: function(){
1237 clickButton();
1238 }
1239 }
1240 });
1241
1242 var clickButton = function(){
1243 var w = window.open();
1244 try {
1245 var url = $("video.pb-video-player").attr('src');
1246 if(url !== undefined){
1247 if(!url.match(/blank\.mp4/)){
1248 prepareResult(url, w);
1249 }
1250 else if(l !== undefined){
1251 prepareResult(l, w);
1252 }
1253 else {
1254 throw new Exception(config.error.id, window.location.href);
1255 }
1256 }
1257 }catch(e){
1258 DomTamper.handleError(e, w);
1259 }
1260 };
1261
1262 var prepareResult = function(url, w) {
1263 var title = $('meta[property="og:title"]');
1264 var quality = $('.quality-btn-active');
1265 var data = {
1266 title: title.length > 0 ? title.attr('content').trim() : 'brak danych',
1267 formats: [new Format({
1268 url: url,
1269 quality: quality.length > 0 ? quality.text() : undefined
1270 })]
1271 };
1272
1273 DomTamper.createDocument(properties, data, w);
1274 };
1275
1276 CDA.waitOnWrapper = function(){
1277 WrapperDetector.run(properties);
1278 };
1279
1280 return CDA;
1281 }(CDA || {}));
1282
1283 var NINATEKA = (function(NINATEKA) {
1284 var properties = Configurator.setup({
1285 wrapper: {
1286 selector: '#videoPlayer, #player'
1287 },
1288 button: {
1289 class: 'ninateka_download_button',
1290 click: function(){
1291 clickButton();
1292 }
1293 }
1294 });
1295
1296 var prepareResult = function(url, w) {
1297 var title = $('meta[name="title"]');
1298 var data = {
1299 title: title.length > 0 ? title.attr('content').trim() : 'brak danych',
1300 formats: [new Format({
1301 url: url,
1302 quality: undefined
1303 })]
1304 };
1305
1306 DomTamper.createDocument(properties, data, w);
1307 };
1308
1309 var getMp4Source = function(w, sources){
1310 for(var i = 0; i < sources.length; i++){
1311 if(sources[i].type && sources[i].type.match(/mp4/g)){
1312 prepareResult(sources[i].src, w);
1313 return;
1314 }
1315 }
1316
1317 throw new Exception(config.error.id, window.location.href);
1318 };
1319
1320 var clickButton = function(){
1321 var w = window.open();
1322 try {
1323 var videoPlayer = $('#videoPlayer').data('player-setup');
1324 var sources = (videoPlayer || {}).sources || {};
1325 if(sources.length > 0){
1326 getMp4Source(w, sources);
1327 }
1328 else {
1329 var scripts = $('script[type="text/javascript"]').filter(':not([src])');
1330 for (var i = 0; i < scripts.length; i++) {
1331 var match = $(scripts[i]).text().match(/fn_\S+\(playerOptionsWithMainSource,\s*\d+\)\.sources/g);
1332 if(match && match[0]){
1333 sources = eval(match[0]);
1334 getMp4Source(w, sources);
1335 break;
1336 }
1337 }
1338 }
1339 }catch(e){
1340 DomTamper.handleError(e, w);
1341 }
1342 };
1343
1344 NINATEKA.waitOnWrapper = function(){
1345 WrapperDetector.run(properties);
1346 };
1347
1348 return NINATEKA;
1349 }(NINATEKA || {}));
1350
1351 var ARTE = (function(ARTE) {
1352 var properties = Configurator.setup({
1353 wrapper: {
1354 selector: 'div.avp-player'
1355 },
1356 button: {
1357 class: 'arte_download_button',
1358 },
1359 asyncChains: {
1360 default: [
1361 AsyncStep.setup({
1362 urlTemplate: 'https://api.arte.tv/api/player/v1/config/#langCode/#videoId',
1363 beforeStep: function (input) {
1364 return idParser();
1365 },
1366 afterStep: function (output) {
1367 return grabVideoFormats(output);
1368 }
1369 })
1370 ]
1371 },
1372 formatter: function(data) {
1373 data.formats.sort(function (a, b) {
1374 return b.bitrate - a.bitrate;
1375 });
1376 data.formats.sort(function (a, b) {
1377 var aLang = a.langCode, bLang = b.langCode;
1378 if(aLang !== 'POL' && bLang !== 'POL'){
1379 return ('' + a.langCode).localeCompare(b.langCode);
1380 }
1381 else if(aLang === 'POL' && bLang !== 'POL'){
1382 return -1;
1383 }
1384 else if(aLang !== 'POL' && bLang === 'POL'){
1385 return 1;
1386 }
1387 else {
1388 return 0;
1389 }
1390 });
1391 }
1392 });
1393
1394 var detectLanguage = function() {
1395 var language = $('header > div > div > button > span');
1396 return language.length > 0 ? language.text().toLowerCase() : 'pl';
1397 };
1398
1399 var idParser = function() {
1400 try {
1401 var metaUrl = $('meta[property="og:url"]').attr('content');
1402 var url = decodeURIComponent(Tool.getUrlParameter('json_url', metaUrl));
1403 return {
1404 videoId: Tool.deleteParametersFromUrl(url).split('/').pop(),
1405 langCode: detectLanguage()
1406 };
1407 }
1408 catch(e){
1409 throw new Exception(config.error.id, window.location.href);
1410 }
1411 };
1412
1413 var grabVideoFormats = function(data){
1414 var formats = [];
1415 var title = (((data || {}).videoJsonPlayer || {}).eStat || {}).streamName || '';
1416 var streams = ((data || {}).videoJsonPlayer || {}).VSR || {};
1417 if(streams){
1418 Object.keys(streams).filter(function(k, i) {
1419 return k.startsWith("HTTPS");
1420 }).forEach(function(k) {
1421 var stream = streams[k];
1422 formats.push(new Format({
1423 bitrate: stream.bitrate,
1424 quality: stream.width + 'x' + stream.height,
1425 langCode: stream.versionShortLibelle,
1426 langDesc: stream.versionLibelle,
1427 url: stream.url
1428 }));
1429 });
1430 return {
1431 title: title,
1432 formats: formats
1433 };
1434 }
1435 throw new Exception(config.error.noSource, window.location.href);
1436 };
1437
1438 ARTE.waitOnWrapper = function(){
1439 WrapperDetector.run(properties);
1440 };
1441
1442 return ARTE;
1443 }(ARTE || {}));
1444
1445 var Starter = (function(Starter) {
1446 var tvZones = [
1447 'bialystok', 'katowice', 'lodz', 'rzeszow', 'bydgoszcz', 'kielce', 'olsztyn', 'szczecin',
1448 'gdansk', 'krakow', 'opole', 'warszawa', 'gorzow', 'lublin', 'poznan', 'wroclaw'
1449 ];
1450
1451 var matcher = [
1452 {action: VOD_TVP.waitOnWrapper, pattern: /^https:\/\/vod\.tvp\.pl\/video\//},
1453 {action: CYF_TVP.waitOnWrapper, pattern: /^https:\/\/cyfrowa\.tvp\.pl\/video\//},
1454 {action: TVP_REG.waitOnWrapper, pattern: new RegExp('^https:\/\/(' + tvZones.join('|') + ')\.tvp\.pl\/\\d{6,}\/')},
1455 {action: TVN.waitOnWrapper, pattern: /^https:\/\/(?:w{3}\.)?(?:tvn)?player\.pl\//},
1456 {action: CDA.waitOnWrapper, pattern: /^https:\/\/.*\.cda\.pl\//},
1457 {action: VOD.waitOnWrapper, pattern: /^https:\/\/vod.pl\//},
1458 {action: VOD_IPLA.waitOnWrapper, pattern: /^https:\/\/.*\.redcdn.pl\/file\/o2\/redefine\/partner\//},
1459 {action: IPLA.waitOnWrapper, pattern: /^https:\/\/www\.ipla\.tv\//},
1460 {action: WP.waitOnWrapper, pattern: /^https:\/\/video\.wp\.pl\//},
1461 {action: NINATEKA.waitOnWrapper, pattern: /^https:\/\/ninateka.pl\//},
1462 {action: ARTE.waitOnWrapper, pattern: /^https:\/\/www.arte.tv\/player\//}
1463 ];
1464
1465 Starter.start = function() {
1466 matcher.some(function(item){
1467 if(location.href.match(item.pattern)){
1468 item.action();
1469 return true;
1470 }
1471 });
1472 };
1473
1474 return Starter;
1475 }(Starter || {}));
1476
1477 $(document).ready(function(){
1478 console.info('voddownloader with jQuery v' + $().jquery);
1479 DomTamper.injectStyle(window, 'buttons_css');
1480 Starter.start();
1481 });
1482
1483}).bind(this)(jQuery, platform, Waves);