· 6 years ago · Nov 01, 2019, 01:42 PM
1// ==UserScript==
2// @name Skrypt umożliwiający pobieranie materiałów ze znanych serwisów VOD.
3// @version 6.5.3
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://partner.ipla.tv/embed/*
22// @include https://video.wp.pl/*
23// @include https://ninateka.pl/*
24// @include https://www.arte.tv/*/videos/*
25// @include https://pulsembed.eu/*
26// @exclude http://www.tvp.pl/sess/*
27// @exclude https://www.cda.pl/iframe/*
28// @grant GM_getResourceText
29// @grant GM_xmlhttpRequest
30// @grant GM_download
31// @grant GM_setClipboard
32// @grant GM_info
33// @connect tvp.pl
34// @connect getmedia.redefine.pl
35// @connect distro.redefine.pl
36// @connect player-api.dreamlab.pl
37// @connect api.arte.tv
38// @connect b2c.redefine.pl
39// @connect player.pl
40// @run-at document-end
41// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js
42// @require https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js
43// @require https://cdnjs.cloudflare.com/ajax/libs/platform/1.3.5/platform.min.js
44// @require https://gitcdn.xyz/cdn/zacny/voddownloader/4b17a120f521eaddf476d6e8fe3be152d506f244/lib/js/mdb-with-waves-patch.js
45// @resource buttons_css https://raw.githubusercontent.com/zacny/voddownloader/master/lib/css/voddownloader-buttons.css
46// @resource content_css https://raw.githubusercontent.com/zacny/voddownloader/master/lib/css/voddownloader-content.css
47// ==/UserScript==
48
49(function vodDownloader($, platform, Waves) {
50 'use strict';
51
52 var Exception = (function(error, templateParams) {
53 this.error = error;
54 this.templateParams = Array.isArray(templateParams) ? templateParams : [templateParams];
55 });
56
57 var Tool = (function(Tool) {
58 Tool.deleteParametersFromUrl = function(url){
59 return decodeURIComponent(url.replace(/\?.*/,''));
60 };
61
62 Tool.getUrlParameter = function(paramName, url){
63 var results = new RegExp('[\?&]' + paramName + '=([^&#]*)').exec(url);
64 if (results==null) {
65 return null;
66 }
67 return decodeURIComponent(results[1]) || 0;
68 };
69
70 Tool.formatConsoleMessage = function(message, params){
71 console.log.apply(this, $.merge([message], params));
72 };
73
74 Tool.downloadFile = function(fileUrl, title){
75 var extension = Tool.deleteParametersFromUrl(fileUrl.split('.').pop());
76 var movieTitle = (title !== undefined && title !== '' ) ? title : 'nieznany';
77 var name = movieTitle + '.' + extension;
78 GM_download(fileUrl, name);
79 };
80
81 Tool.template = function(templates, ...keys){
82 return (function(...values) {
83 var dict = values[values.length - 1] || {};
84 var result = [templates[0]];
85 keys.forEach(function(key, i) {
86 var value = Number.isInteger(key) ? values[key] : dict[key];
87 result.push(value, templates[i + 1]);
88 });
89 return result.join('');
90 });
91 };
92
93 Tool.getRealUrl = function(){
94 var topUrl = window.sessionStorage.getItem(config.storage.topWindowLocation);
95 return topUrl !== null ? topUrl : window.location.href;
96 };
97
98 Tool.isTopWindow = function(){
99 return window.top === window.self;
100 };
101
102 Tool.pad = function(number, characters){
103 return(1e15+number+"").slice(-characters)
104 };
105
106 Tool.mapDescription = function(data){
107 var defaults = config.description.defaults;
108 var sourceDescriptions = config.description.sources[data.source] || {};
109 var descriptionVariant = sourceDescriptions[data.key] || {};
110 var output = {
111 video: descriptionVariant.video ? descriptionVariant.video : data.video,
112 index: descriptionVariant.index ? descriptionVariant.index : 99,
113 audio: data.audio ? data.audio : defaults.audio,
114 language: data.language ? data.language: defaults.language,
115 url: data.url
116 };
117 return $.extend(true, data, output);
118 };
119
120 return Tool;
121 }(Tool || {}));
122
123 const config = {
124 attempts: 10,
125 attemptTimeout: 1500,
126 storage: {
127 doNotWarn: 'voddownloader.doNotwarnIfIncorrectPluginSettingsDetected',
128 topWindowLocation: 'voddownloader.topWindowLocation'
129 },
130 include: {
131 fontawesome: {
132 id: 'fontawesome',
133 css: 'https://use.fontawesome.com/releases/v5.8.2/css/all.css'
134 },
135 bootstrap: {
136 id: 'bootstrap',
137 css: 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css'
138 },
139 mdb: {
140 id: 'mdb',
141 css: 'https://cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.8.2/css/mdb.min.css',
142 }
143 },
144 error: {
145 id: {
146 caption: 'Nie udało się odnaleźć idetyfikatora.',
147 template: Tool.template`Algorytm rozpoznawania identyfikatora wideo na stronie: ${0} \
148 zakończył się niepowodzeniem. Może to oznaczać błąd skryptu.`,
149 },
150 tvnId: {
151 caption: 'Nie udało się odnaleźć idetyfikatora.',
152 template: Tool.template`Algorytm rozpoznawania identyfikatora wideo na stronie: ${0} \
153 zakończył się niepowodzeniem.\nJeżeli jest to główna strona programu oznacza to, \
154 że nie udało się odnaleźć identyfikatora ostatniego odcinka. Wejdź na stronę odcinka \
155 i spróbuj ponownie.\nMoże to również oznaczać błąd skryptu.`,
156 },
157 call: {
158 caption: 'Błąd pobierania informacji o materiale.',
159 template: Tool.template`Wystąpił błąd w wykonaniu skryptu w kroku: ${0} na stronie: ${1} \
160 Zgłoś problem autorom skryptu.`,
161 },
162 noSource: {
163 caption: 'Nie udało się odnaleźć źródeł do materiału.',
164 template: Tool.template`Materiał ze strony ${0} nie posiada zdefiniowanych źródeł, które mogłyby zostać \
165 wyświetlone. \nMoże to oznaczać, że nie jest on publicznie dostępny, dostępne źródła nie mogą zostać \
166 wyświetlone w przeglądarce bez dodatkowego oprogramowania lub jest umieszczony w płatnej strefie.`,
167 type: 'info'
168 },
169 timeout: {
170 caption: 'Zbyt długi czas odpowiedzi.',
171 template: Tool.template`Dla kroku: ${0} na stronie "${1}" nie dotarły \
172 informacje zwrotne.\nPrzypuszczalnie jest to problem sieciowy. Spróbuj ponownie za jakiś czas.`
173 },
174 noParent: {
175 caption: 'Brak zakładki ze stroną główną.',
176 template: Tool.template`Została zamknięta zakładka ze stroną na której został uruchomiony skrypt. \
177 Ta zakładka nie może przez to działać poprawnie. Otwórz ponownie stronę główną: \n ${0} \n
178 by przywrócić prawidłowe funkcjonowanie skryptu.`
179 }
180 },
181 description: {
182 defaults: {
183 language: 'polski',
184 audio: 'MPEG ACC'
185 },
186 sources: {
187 IPLA: {
188 '1080p': {video: 'H264 MPEG-4 AVC, 4011 kb/s, 1920x1080, 25fps, 16:9', index: 1},
189 '720p': {video: 'H264 MPEG-4 AVC, 1672 kb/s, 1280x720, 25fps, 16:9', index: 2},
190 '576p': {video: 'H264 MPEG-4 AVC, 1175 kb/s, 1024x576, 25fps, 16:9', index: 3}
191 },
192 WP: {
193 HQ: {video: 'H264 MPEG-4 AVC, 1804 kb/s, 1280x720, 24fps, 16:9', index: 1},
194 LQ: {video: 'H264 MPEG-4 AVC, 616 kb/s, 640x360, 24fps, 16:9', index: 2}
195 },
196 TVN: {
197 'HD': {video: 'H264 MPEG-4 AVC, 2776 kb/s, 1280x720, 25fps, 16:9', index: 1},
198 'Bardzo wysoka': {video: 'H264 MPEG-4 AVC, 1786 kb/s, 1280x720, 25fps, 16:9', index: 2},
199 'Wysoka': {video: 'H264 MPEG-4 AVC, 1191 kb/s, 720x576, 25fps, 5:4', index: 3},
200 'Standard': {video: 'H264 MPEG-4 AVC, 794 kb/s, 720x576, 25fps, 5:4', index: 4},
201 'Średnia': {video: 'H264 MPEG-4 AVC, 596 kb/s, 640x480, 25fps, 4:3', index: 5},
202 'Niska': {video: 'H264 MPEG-4 AVC, 417 kb/s, 512x384, 25fps, 4:3', index: 6},
203 'Bardzo niska': {video: 'H264 MPEG-4 AVC, 238 kb/s, 320x240, 25fps, 4:3', index: 7}
204 },
205 VOD: {
206 '720': {video: 'H264 MPEG-4 AVC, 2467 kb/s, 1280x720, 25fps, 16:9', index: 1},
207 '576': {video: 'H264 MPEG-4 AVC,1810 kb/s, 1024x576, 25fps, 16:9', index: 2},
208 '480': {video: 'H264 MPEG-4 AVC, 911 kb/s, 854x480, 25fps, 16:9', index: 3},
209 '360': {video: 'H264 MPEG-4 AVC, 450 kb/s, 640x360, 25fps, 16:9', index: 4},
210 '240': {video: 'H264 MPEG-4 AVC, 200 kb/s, 426x240, 25fps, 16:9', index: 5}
211 },
212 TVP: {
213 '9100000': {video: 'H264 MPEG-4 AVC, 21030 kb/s, 1920x1080, 25fps, 16:9', index: 1},
214 '5420000': {video: 'H264 MPEG-4 AVC, 9875 kb/s, 1280x720, 25fps, 16:9', index: 2},
215 '2850000': {video: 'H264 MPEG-4 AVC, 4661 kb/s, 960x540, 25fps, 16:9', index: 3},
216 '1750000': {video: 'H264 MPEG-4 AVC, 1782 kb/s, 800x450, 25fps, 16:9', index: 4},
217 '1250000': {video: 'H264 MPEG-4 AVC, 1255 kb/s, 640x360, 25fps, 16:9', index: 5},
218 '820000': {video: 'H264 MPEG-4 AVC, 809 kb/s, 480x270, 25fps, 16:9', index: 6},
219 '590000': {video: 'H264 MPEG-4 AVC, 581 kb/s, 398x224, 25fps, 199:112', index: 7}
220 },
221 ARTE: {
222 '2200': {video: 'H264 MPEG-4 AVC, 2438 kb/s, 1280x720, 25fps, 16:9', index: 1},
223 '1500': {video: 'H264 MPEG-4 AVC, 1619 kb/s, 720x406, 25fps, 360:203', index: 2},
224 '800': {video: 'H264 MPEG-4 AVC, 805 kb/s, 640x360, 25fps, 16:9', index: 3},
225 '300': {video: 'H264 MPEG-4 AVC, 357 kb/s, 384x216, 25fps, 16:9', index: 4}
226 },
227 NINATEKA: {
228 def: {video: 'H264 MPEG-4 AVC, 900 kb/s, 640x360, 25fps, 16:9', index: 1}
229 },
230 CDA: {
231 '1080p': {video: 'H264 MPEG-4 AVC, 1920x1080, 16:9', index: 1},
232 '720p': {video: 'H264 MPEG-4 AVC, 1280x720, 16:9', index: 2},
233 '480p': {video: 'H264 MPEG-4 AVC, 854x480, 427:240', index: 3},
234 '360p': {video: 'H264 MPEG-4 AVC, 640x360, 16:9', index: 4},
235 }
236 }
237 }
238 };
239
240
241 var Step = (function(properties){
242 var step = {
243 urlTemplate: '',
244 beforeStep: function(input){return input},
245 afterStep: function (output) {return output},
246 resolveUrl: function (input) {
247 var url = this.urlTemplate;
248 var urlParams = {};
249 $.each(input, function (key, value) {
250 url = url.replace(new RegExp('#'+key,'g'), value);
251 urlParams[key] = value;
252 });
253
254 return {
255 url: url,
256 urlParams: urlParams
257 };
258 },
259 isRemote: function(){
260 return this.urlTemplate.length > 0;
261 },
262 method: 'GET',
263 methodParam: function(){return {}}
264 };
265
266 return $.extend(true, step, properties);
267 });
268
269 var Notification = (function(Notification) {
270 var create = function(title, bodyContent, special) {
271 var specialContentClasses = special ? ' special-color white-text' : '';
272 var content = $('<div>').addClass('toast notification' + specialContentClasses).attr('role', 'alert')
273 .attr('aria-live', 'assertive').attr('aria-atomic', 'true')
274 .attr('name', special ? 'special' : 'normal').attr('data-delay', '5000');
275 var header = $('<div>').addClass('toast-header special-color-dark white-text');
276 var warnIcon = $('<i>').addClass('fas fa-exclamation-triangle pr-2');
277 var notificationTitle = $('<strong>').addClass('mr-auto').text(title);
278 var time = $('<small>').text(new Date().toLocaleTimeString());
279 var close = $('<button>').attr('type', 'button').addClass('ml-2 mb-1 close white-text')
280 .attr('data-dismiss', 'toast').attr('aria-label', 'Close')
281 .append($('<span>').attr('aria-hidden', 'true').text('\u00D7'));
282
283 if(special){
284 header.append(warnIcon);
285 content.attr('data-autohide', 'false');
286 }
287 header.append(notificationTitle).append(time).append(close);
288 var body = $('<div>').addClass('toast-body notification-body').append(bodyContent);
289
290 content.append(header).append(body);
291 return content;
292 };
293
294 Notification.show = function(options, w){
295 options = options || {};
296 var special = false;
297 if (options.hasOwnProperty('special')) {
298 special = options.special;
299 }
300 if(!options.hasOwnProperty('title') || !options.hasOwnProperty('content')){
301 return;
302 }
303
304 var rootElement = $(w.document.body);
305 var notification = create(options.title, options.content, special);
306 $('#notification-container', rootElement).append(notification);
307 $('.toast', rootElement).toast('show');
308 $('.toast', rootElement).on('hidden.bs.toast', function (){
309 $.each($(this), function(index, value) {
310 var element = $(value);
311 element.remove();
312 });
313 })
314 };
315
316 return Notification;
317 }(Notification || {}));
318
319 var PluginSettingsDetector = (function(PluginSettingsDetector){
320 var prepareWarningNotification = function(w) {
321 var bodyContent = $('<div>')
322 .append('Twój dodatek ma nieprawidłowe ustawienia, przez co nie możesz korzystać z opcji ')
323 .append('bezpośredniego pobierania plików. Możesz skorygować je w następujący sposób:');
324 var list = $('<ol>').addClass('m-0')
325 .append($('<li>').text('Otwórz Panel sterowania Tampermonkey i kliknij ustawienia.'))
326 .append($('<li>').text('Ogólne > Tryb konfiguracji > Expert'))
327 .append($('<li>').text('Pobieranie BETA > Tryb pobierania > API przeglądarki'))
328 .append($('<li>').text('Zapisz ustawienia, a jeżeli przeglądarka zapyta o możliwość zarządzania' +
329 ' pobieranymi plikami, należy się zgodzić'));
330 bodyContent.append(list).append(createButton(w));
331 var options = {title: 'Wykryto problem', content: bodyContent, special: true};
332 Notification.show(options, w);
333 };
334
335 var createButton = function(w){
336 return $('<button>').attr('type', 'button').addClass('btn btn-dark btn-sm m-1 pl-3 pr-3')
337 .append($('<i>').addClass('fas pr-1 fa-window-close')).append('Nie pokazuj więcej').click(function(){
338 var rootElement = $(w.document.body);
339 w.localStorage.setItem(config.storage.doNotWarn, true);
340 $('.toast.special-color', rootElement).toast('hide');
341 setTimeout(function(){
342 $('.toast.special-color', rootElement).remove();
343 }, 1000);
344 });
345 };
346
347 var disableDownload = function(w){
348 var rootElement = $(w.document.body);
349 $('.fa-save', rootElement).closest('button').attr('disabled', true);
350 };
351
352 PluginSettingsDetector.detect = function(w){
353 var downloadMode = GM_info.downloadMode;
354 if(downloadMode !== 'browser'){
355 disableDownload(w);
356 var value = w.localStorage.getItem(config.storage.doNotWarn);
357 if(value !== 'true'){
358 prepareWarningNotification(w);
359 }
360 }
361 };
362 return PluginSettingsDetector;
363 }(PluginSettingsDetector || {}));
364
365 var DomTamper = (function(DomTamper){
366
367 DomTamper.injectStyle = function(w, name){
368 var head = $(w.document.head);
369 if(!head.find('style[name="' + name + '"]').length){
370 var styleElement = $('<style>').attr('type', 'text/css')
371 .attr('name', name).text((GM_getResourceText(name)));
372 head.append(styleElement);
373 }
374 };
375
376 var injectStylesheet = function (w, setting) {
377 var head = $(w.document.head);
378 if(!head.find('link[name="' + setting.id + '"]').length){
379 var stylesheet = $('<link>').attr('name', setting.id).attr('type', 'text/css').attr('rel', 'stylesheet')
380 .attr('href', setting.css);
381 head.append(stylesheet);
382 }
383 };
384
385 var prepareHead = function(w){
386 injectStylesheet(w, config.include.fontawesome);
387 injectStylesheet(w, config.include.bootstrap);
388 injectStylesheet(w, config.include.mdb);
389 DomTamper.injectStyle(w, 'content_css');
390 };
391
392 var createLinks = function(w, additionalClass){
393 var links = [
394 {
395 url: 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RWX4EUR77CMKU',
396 icon: 'fa-hand-holding-usd',
397 tooltip: 'dotacje'
398 },
399 {
400 url: 'https://greasyfork.org/pl/scripts/6049-skrypt-umo%C5%BCliwiaj%C4%85cy-pobieranie-' +
401 'materia%C5%82%C3%B3w-ze-znanych-serwis%C3%B3w-vod/feedback',
402 icon: 'fa-comments',
403 tooltip: 'problemy, komentarze'
404 },
405 {
406 url: 'https://github.com/zacny/voddownloader/issues',
407 icon: 'fa-bug',
408 tooltip: 'zgłoś błąd'
409 }
410 ];
411 var container = $('<div>').addClass('links-position');
412 links.forEach(function(link){
413 var button = $('<button>').attr('type', 'button').attr('title', link.tooltip)
414 .addClass('btn btn-sm m-1 p-2').addClass(additionalClass)
415 .append($('<i>').addClass('fas').addClass(link.icon).addClass('fa-2x'));
416 button.click(function(){
417 w.open(link.url);
418 });
419 container.append(button);
420 });
421 return container;
422 };
423
424 var prepareBody = function(w, pageContent, detection) {
425 appendOrReplace(w, pageContent);
426 attachWaveEffect(w, pageContent);
427 if(detection) {
428 PluginSettingsDetector.detect(w);
429 }
430 };
431
432 var appendOrReplace = function (w, pageContent) {
433 var body = $(w.document.body);
434 if(body.children().length > 0){
435 body.children(":first").replaceWith(pageContent);
436 }
437 else {
438 body.append(pageContent);
439 }
440 };
441
442 var attachWaveEffect = function(w, pageContent){
443 var buttons = pageContent.find('.btn:not(.btn-flat), .btn-floating');
444 Waves.attach(buttons, ['waves-light']);
445 Waves.init({}, w);
446 };
447
448 DomTamper.handleError = function(exception, w){
449 if(w === undefined){
450 w = window.open();
451 }
452
453 prepareHead(w);
454 var errorData = getErrorData(exception);
455 var pageContent = $('<div>').addClass('page-content');
456 pageContent.append(createErrorContent(errorData));
457 pageContent.append(createLinks(w, errorData.type === 'error' ?
458 'btn-danger' : 'special-color white-text'));
459 prepareBody(w, pageContent);
460 };
461
462 var getErrorData = function(exception){
463 var type = 'error';
464 var caption = 'Niespodziewany błąd';
465 var message = 'Natrafiono na niespodziewany błąd: ' + exception;
466 if(exception.error){
467 message = exception.error.template.apply(this, exception.templateParams).replace(/\n/g, '<br/>');
468 caption = exception.error.caption;
469 type = exception.error.type !== undefined ? exception.error.type : 'error';
470 }
471
472 return {
473 message: linkify(message),
474 caption: caption,
475 type: type
476 }
477 };
478
479 var linkify = function(text) {
480 var linkDetectionRegex = /(((https?:\/\/)|(www\.))[^\s]+)/g;
481 return text.replace(linkDetectionRegex, function(url) {
482 return '<u><a class="text-white" href="' + url + '">' + url + '</a></u>';
483 });
484 };
485
486 var createErrorContent = function(errorData){
487 var typeClass = errorData.type === 'error' ? 'bg-danger' : 'bg-dark';
488 var card = $('<div>').addClass('card text-white mb-3').addClass(typeClass);
489 var cardHeader = $('<div>').addClass('card-header')
490 .text('Niestety natrafiono na problem, który uniemożliwił dalsze działanie');
491 var cardBody = $('<div>').addClass('card-body')
492 .append($('<h5>').addClass('card-title').text(errorData.caption))
493 .append($('<div>').addClass('card-text text-white mb-3').append(errorData.message))
494 .append($('<div>').addClass('card-text text-white')
495 .append('Informacje o systemie: ').append(platform.description))
496 .append($('<div>').addClass('card-text text-white')
497 .append('Wersja pluginu: ').append(GM_info.version));
498 card.append(cardHeader).append(cardBody);
499 return card;
500 };
501
502 DomTamper.createButton = function(properties){
503 properties.wrapper.get().find('#'+properties.button.id).remove();
504 var button = $('<input>').attr('id', properties.button.id).attr('type', 'button')
505 .attr('style', properties.button.style).attr('value', 'Pobierz video').addClass(properties.button.class);
506 button.bind('click', properties.button.click);
507 properties.wrapper.get().append(button);
508 };
509
510 DomTamper.createLoader = function(w){
511 prepareHead(w);
512 var pageContent = $('<div>').addClass('page-content');
513 pageContent.append(createLoaderContent());
514 pageContent.append(createLinks(w, 'special-color white-text'));
515 prepareBody(w, pageContent);
516 Unloader.init(w);
517 };
518
519 var createLoaderContent = function(){
520 var card = $('<div>').addClass('card text-white bg-dark');
521 var cardHeader = $('<div>').addClass('card-header').text('Poczekaj trwa wczytywanie danych...');
522 var cardBody = $('<div>').addClass('card-body');
523 var bodyContainer = $('<div>').addClass('d-flex justify-content-center m-3');
524 var spinner = $('<div>').addClass('spinner-border spinner-size').attr('role', 'status')
525 .append($('<span>').addClass('sr-only').text('Loading...'));
526 cardBody.append(bodyContainer.append(spinner));
527 card.append(cardHeader).append(cardBody);
528
529 return card;
530 };
531
532 var setWindowTitle = function(data, w){
533 var head = $(w.document.head);
534 var title = head.find('title');
535 if(title.length) {
536 title.text(data.title);
537 }
538 else {
539 head.append($('<title>').text(data.title));
540 }
541 };
542
543 DomTamper.createDocument = function(data, w){
544 prepareHead(w);
545 setWindowTitle(data, w);
546 var pageContent = $('<div>').addClass('page-content');
547 pageContent.append(Accordion.create(w, data));
548 pageContent.append(createLinks(w, 'special-color white-text'));
549 pageContent.append(createNotificationContainer());
550 prepareBody(w, pageContent, true);
551 Unloader.init(w);
552 Accordion.bindActions(w, data);
553 };
554
555 var createNotificationContainer = function(){
556 return $('<div>').attr('id', 'notification-container')
557 .attr('aria-live', 'polite').attr('aria-atomic', 'true').addClass('notification-container');
558 };
559
560 return DomTamper;
561 }(DomTamper || {}));
562
563 var Accordion = (function(Accordion) {
564 Accordion.create = function(w, data){
565 var mainCardTitle = $('<div>').addClass('card-header').text(data.title);
566
567 var accordion = $('<div>').addClass('accordion md-accordion').attr('id', 'accordion')
568 .attr('role', 'tablist').attr('aria-multiselectable', 'true');
569
570 createCards(accordion, data);
571
572 var mainCardBody = $('<div>').addClass('card-body p-0').append(accordion);
573 return $('<div>').addClass('card').append(mainCardTitle).append(mainCardBody);
574 };
575
576 var createCards = function(accordion, data) {
577 for(var key in data.cards) {
578 var card = createCard({
579 card: data.cards[key],
580 key: key,
581 title: data.title
582 });
583 accordion.append(card);
584 }
585 };
586
587 var createCard = function(data){
588 var accordionCard = $('<div>').addClass('border border-top-0');
589 var content = $('<div>').addClass('card-body pt-0');
590
591 var badgeClass = 'badge-light';
592 var textMuted = 'text-muted';
593 if(data.card.items.length > 0){
594 badgeClass = 'badge-danger';
595 textMuted = 'text-dark';
596 content.append(createCardContent(data));
597 }
598
599 var icon = $('<i>').addClass('fas').addClass(data.card.icon).addClass('pr-2');
600 var badge = $('<span>').addClass('badge mr-3 float-right').addClass(badgeClass)
601 .text(data.card.items.length);
602 var cardTitle = $('<h6>').addClass('mb-0').addClass(textMuted).append(icon).append(badge)
603 .append($('<span>').text(data.card.label));
604 var link = $('<a>').append(cardTitle);
605 var cardHeader = $('<div>').addClass('ml-3 p-2').attr('role', 'tab').attr('id', data.key).append(link);
606
607 var cardBody = $('<div>').addClass('collapse').attr('role', 'tabpanel')
608 .attr('aria-labelledby', data.key).append(content);
609 if(data.card.collapse){
610 cardBody.addClass('show');
611 }
612
613 accordionCard.append(cardHeader);
614 accordionCard.append(cardBody);
615 return accordionCard;
616 };
617
618 var createCardContent = function(data){
619 var table = $('<table>').addClass('table table-bordered table-striped btn-table');
620 var tbody = $('<tbody>');
621 table.append(tbody);
622 createRows(tbody, data);
623
624 return table;
625 };
626
627 var createRows = function(tableBody, data){
628 data.card.items.forEach(function(item) {
629 tableBody.append(createRow({
630 item: item,
631 info: data.card.info,
632 title: data.title,
633 actions: data.card.actions
634 }));
635 });
636 };
637
638 var createRow = function(data){
639 var actions = $('<td>').attr('scope', 'row').addClass('action-row-' + data.actions.length);
640 data.actions.forEach(function(action){
641 actions.append(createButton(action, data));
642 });
643
644 var description = $('<td>').html(createDescriptionHtml(data));
645 return $('<tr>').append(actions).append(description);
646 };
647
648 var createDescriptionHtml = function(data){
649 var descriptionHtml = $('<div>');
650
651 createDescription(data).forEach(function(item, idx, array){
652 descriptionHtml.append($('<b>').text(item.desc + ': '))
653 .append($('<span>').text(item.value));
654 if(idx !== array.length - 1) {//not last
655 descriptionHtml.append($('<span>').text(', '));
656 }
657 });
658 return descriptionHtml;
659 };
660
661 var itemExist = function(data, info){
662 return data.item.hasOwnProperty(info.name) && data.item[info.name] != null
663 };
664
665 var createDescription = function(data){
666 var description = [];
667 data.info.forEach(function(info){
668 if (itemExist(data, info)) {
669 description.push({
670 desc: info.desc,
671 value: data.item[info.name]
672 });
673 }
674 });
675 return description;
676 };
677
678 var createButton = function(action, data){
679 return $('<button>').attr('type', 'button').attr('data-url', data.item.url).attr('data-title', data.title)
680 .addClass('btn btn-dark btn-sm m-1 pl-3 pr-3')
681 .append($('<i>').addClass('fas pr-1').addClass(action.icon)).append(action.label);
682 };
683
684 Accordion.bindActions = function(w, data){
685 cardActions(w, data);
686 buttonActions(w);
687 };
688
689 var cardActions = function(w, data){
690 for(var key in data.cards) {
691 var cardHeader = $(w.document.body).find('#' + key);
692 var disabled = cardHeader.find('h6.text-muted');
693 if(disabled.length){
694 disabled.addClass('cursor-normal');
695 return;
696 }
697
698 $(w.document.body).find('#' + key).click(function() {
699 var id = $(this).attr('id');
700 $(w.document.body).find('div[aria-labelledby="' + id + '"]').toggle();
701 });
702 }
703 };
704
705 var buttonActions = function(w){
706 getButton(w, '.fa-clone').click(function(){ copyActionClick($(this), w) });
707 getButton(w, '.fa-film').click(function(){ openActionClick($(this), w) });
708 getButton(w, '.fa-download').click(function(){ downloadActionClick($(this), w) });
709 };
710
711 var getButton = function(w, iconClass){
712 return $(w.document.body).find(iconClass).parent();
713 };
714
715 var downloadActionClick = function (element, w) {
716 var options = {title: 'Rozpoczęto pobieranie pliku', content: element.attr('data-title')};
717 Tool.downloadFile(element.attr('data-url'), element.attr('data-title'));
718 Notification.show(options, w);
719 };
720
721 var copyActionClick = function (element, w) {
722 GM_setClipboard(element.attr('data-url'));
723 var options = {title: 'Kopiowanie', content: 'Skopiowano do schowka'};
724 Notification.show(options, w);
725 };
726
727 var openActionClick = function (element, w) {
728 w.open(element.attr('data-url'));
729 };
730
731 return Accordion;
732 }(Accordion || {}));
733
734 var Executor = (function(Executor){
735 var execute = function(service, options, w){
736 var setup = setupStep(service, options);
737 logStepInfo(options, setup);
738 if(setup.isRemote){
739 executeAsync(service, setup, options, w);
740 }
741 else {
742 options.temporaryData = {};
743 callback(service, options, w);
744 }
745 };
746
747 var executeAsync = function(service, setup, options, w){
748 var chain = options.chainNames[options.chainIndex];
749 var chainStep = chain + '[' + options.stepIndex + ']';
750 var exceptionParams = [chainStep, Tool.getRealUrl()];
751 var requestParams = {
752 method: setup.method,
753 url: setup.resolveUrl.url,
754 data: JSON.stringify(setup.methodParam),
755 responseType: 'json',
756 onload: function(data) {
757 options.temporaryData = data.response || {};
758 callback(service, options, w);
759 },
760 onerror: function(){
761 DomTamper.handleError(new Exception(config.error.call, exceptionParams), w);
762 },
763 ontimeout: function(){
764 DomTamper.handleError(new Exception(config.error.timeout, exceptionParams), w);
765 }
766 };
767 GM_xmlhttpRequest(requestParams);
768 };
769
770 var logStepInfo = function(options, setup){
771 var chain = options.chainNames[options.chainIndex];
772 var step = chain + '[' + options.stepIndex + ']';
773 var stepParams = $.isEmptyObject(setup.methodParam) ? '' : JSON.stringify(setup.methodParam);
774 var params = [
775 'color:blue', step, 'color:red', setup.isRemote ? setup.method : '---',
776 'color:black;font-weight: bold', setup.resolveUrl.url, 'color:magenta', stepParams
777 ];
778 Tool.formatConsoleMessage('%c%s%c %s %c %s %c%s', params);
779 };
780
781 var setupStep = function(service, options){
782 var currentStep = getCurrentStep(service, options);
783 var result = currentStep.beforeStep(options.temporaryData);
784 if(typeof result === 'string' || typeof result == 'number'){
785 result = {
786 videoId: result
787 }
788 }
789 if(options.urlParams){
790 $.extend(true, options.urlParams, result);
791 }
792 else {
793 options.urlParams = result;
794 }
795
796 return {
797 resolveUrl: currentStep.resolveUrl(options.urlParams),
798 method: currentStep.method,
799 methodParam: currentStep.methodParam(),
800 isRemote: currentStep.isRemote()
801 };
802 };
803
804 var getCurrentStep = function(service, options){
805 var chain = options.chainNames[options.chainIndex];
806 var steps = service.asyncChains[chain];
807 return steps[options.stepIndex];
808 };
809
810 var hasNextStep = function(service, options){
811 var chain = options.chainNames[options.chainIndex];
812 var steps = service.asyncChains[chain];
813 return steps.length - 1 > options.stepIndex;
814 };
815
816 var hasNextChain = function(service, options){
817 return options.chainNames.length - 1 > options.chainIndex;
818 };
819
820 var setChainResult = function(options){
821 var chain = options.chainNames[options.chainIndex];
822 if(!options.hasOwnProperty('results')){
823 options.results = {};
824 }
825 var chainResult = options.results;
826 chainResult[chain] = options.temporaryData;
827 options.temporaryData = {};
828 };
829
830 var pushChain = function(service, options){
831 setChainResult(options);
832 if(hasNextChain(service, options)){
833 options.chainIndex += 1;
834 options.stepIndex = 0;
835 return true;
836 }
837 return false;
838 };
839
840 var pushStep = function(service, options) {
841 if(hasNextStep(service, options)){
842 options.stepIndex += 1;
843 return true;
844 }
845 return false;
846 };
847
848 var afterStep = function(service, options) {
849 var currentStep = getCurrentStep(service, options);
850 var output = currentStep.afterStep(options.temporaryData);
851 options.temporaryData = output;
852 };
853
854 var callback = function(service, options, w){
855 try {
856 afterStep(service, options);
857 if(pushStep(service, options) || pushChain(service, options)) {
858 return Promise.resolve().then(
859 Executor.chain(service, options, w)
860 );
861 }
862 else {
863 return Promise.resolve().then(
864 service.onDone(options.results, w)
865 );
866 }
867 }
868 catch(e){
869 DomTamper.handleError(e, w);
870 }
871 };
872
873 Executor.chain = function(service, options, w){
874 try {
875 if(w === undefined){
876 w = window.open();
877 DomTamper.createLoader(w);
878 }
879
880 execute(service, options, w);
881 }
882 catch(e){
883 DomTamper.handleError(e, w);
884 }
885 };
886
887 return Executor;
888 }(Executor || {}));
889
890 function Configurator(properties){
891 var service = {
892 wrapper: {
893 selector: '',
894 get: function(){
895 return $(service.wrapper.selector);
896 },
897 exist: function(){
898 return $(service.wrapper.selector).length > 0;
899 }
900 },
901 button: {
902 id: 'direct-download',
903 style: '',
904 class: '',
905 click: function(){
906 var chainNames = service.chainSelector();
907 Executor.chain(service, {
908 stepIndex: 0,
909 chainIndex: 0,
910 chainNames: chainNames
911 });
912 }
913 },
914 cardsData: {
915 title: '',
916 cards: {
917 videos: {
918 icon: 'fa-video', label: 'Video', collapse: true, items: [],
919 info: [
920 {name: 'video', desc: 'video'},
921 {name: 'audio', desc: 'audio'},
922 {name: 'language', desc: 'wersja językowa'}
923 ],
924 actions: [
925 {label: 'Pobierz', icon: 'fa-download'},
926 {label: 'Kopiuj', icon: 'fa-clone'},
927 {label: 'Otwórz', icon: 'fa-film'}
928 ]
929 },
930 subtitles: {
931 icon: 'fa-file-alt', label: 'Napisy', collapse: false, items: [],
932 info: [
933 {name: 'description', desc: 'opis'},
934 {name: 'format', desc: 'format'},
935 ],
936 actions: [
937 {label: 'Pobierz', icon: 'fa-download'}
938 ]
939 }
940 }
941 },
942 asyncChains: {
943 videos: []
944 },
945 chainSelector: function(){
946 return ['videos'];
947 },
948 formatter: function(data){
949 data.cards['videos'].items.sort(function (a, b) {
950 return a.index - b.index;
951 });
952 data.cards['subtitles'].items.sort(function (a, b) {
953 return ('' + a.format).localeCompare(b.format);
954 });
955 },
956 aggregate: function(data){
957 var aggregatedData = {};
958 $.extend(true, aggregatedData, service.cardsData);
959 var chains = service.chainSelector();
960 chains.forEach(function(chain){
961 $.extend(true, aggregatedData, data[chain]);
962 });
963 return aggregatedData;
964 },
965 onDone: function(data, w) {
966 var aggregatedData = service.aggregate(data);
967 service.formatter(aggregatedData);
968 DomTamper.createDocument(aggregatedData, w);
969 }
970 };
971
972 return $.extend(true, service, properties);
973 }
974
975 var Detector = (function(conf) {
976 var configuration = conf;
977
978 var logMessage = function(attempt){
979 var color = configuration.logStyle || 'color:black;font-weight:bold';
980 var existColor = configuration.success() ? 'color:green' : 'color:red';
981 if(configuration.unlimited){
982 var params = [
983 existColor, configuration.target, 'color:black'
984 ];
985 Tool.formatConsoleMessage('[%c%s%c]', params);
986 }
987 else {
988 var params = [
989 'color:black', color, configuration.target, 'color:black',
990 existColor + ';font-weight:bold', attempt, 'color:black'
991 ];
992 Tool.formatConsoleMessage('%c[%c%s%c] [%c%s%c]', params);
993 }
994 };
995
996 var check = function(attempt){
997 logMessage(attempt);
998 if (configuration.success()) {
999 return Promise.resolve().then(
1000 configuration.successCallback()
1001 );
1002 } else if(configuration.unlimited || attempt > 0){
1003 attempt = attempt-1;
1004 return Promise.resolve().then(
1005 setTimeout(check, config.attemptTimeout, attempt)
1006 );
1007 }
1008 };
1009
1010 this.detect = function() {
1011 check(config.attempts);
1012 };
1013 });
1014
1015 var ChangeVideoDetector = (function(ChangeVideoDetector){
1016 ChangeVideoDetector.run = function(videoChangeCallback) {
1017 var detector = new Detector({
1018 unlimited: true,
1019 previousLocation: window.location.href,
1020 target: 'video-change',
1021 success: function(){
1022 return this.previousLocation !== window.location.href
1023 },
1024 successCallback: videoChangeCallback
1025 });
1026 detector.detect();
1027 };
1028 return ChangeVideoDetector;
1029 }(ChangeVideoDetector || {}));
1030
1031 var WrapperDetector = (function(WrapperDetector){
1032 WrapperDetector.run = function(properties, videoChangeCallback) {
1033 var detector = new Detector({
1034 logStyle: 'color:orange',
1035 target: properties.wrapper.selector,
1036 success: properties.wrapper.exist,
1037 successCallback: function(){
1038 DomTamper.createButton(properties);
1039 }
1040 });
1041 detector.detect();
1042
1043 if(typeof videoChangeCallback === "function"){
1044 ChangeVideoDetector.run(videoChangeCallback);
1045 }
1046 };
1047 return WrapperDetector;
1048 }(WrapperDetector || {}));
1049
1050 var ElementDetector = (function(ElementDetector){
1051 ElementDetector.detect = function(selector, callback){
1052 var detector = new Detector({
1053 logStyle: 'color:dodgerblue',
1054 target: selector,
1055 success: function(){
1056 return $(this.target).length > 0;
1057 },
1058 successCallback: callback
1059 });
1060 detector.detect();
1061 };
1062
1063 return ElementDetector;
1064 }(ElementDetector || {}));
1065
1066 var Unloader = (function(Unloader) {
1067 var win;
1068 var url;
1069
1070 Unloader.init = function(w){
1071 win = w;
1072 url = Tool.getRealUrl();
1073 $(window).bind('beforeunload', function(){
1074 if(!win.closed) {
1075 DomTamper.handleError(new Exception(config.error.noParent, url), win);
1076 }
1077 });
1078 };
1079
1080 return Unloader;
1081 }(Unloader || {}));
1082
1083 var MessageReceiver = (function(MessageReceiver) {
1084 var win;
1085 var origin;
1086 var callbackFunction;
1087 var alreadyConfirmed = false;
1088 var alreadyPosted = false;
1089
1090 var receiveMessage = function(event, callback){
1091 if (event.origin !== origin) {
1092 return;
1093 }
1094
1095 var data = JSON.parse(event.data);
1096 if(data.confirmation){
1097 alreadyConfirmed = true;
1098 }
1099 else {
1100 data.confirmation = true;
1101 if(!alreadyPosted) {
1102 window.removeEventListener('message', callbackFunction);
1103 alreadyPosted = true;
1104 postMessage(data);
1105 callback(data);
1106 }
1107 }
1108 };
1109
1110 var postMessage = function(data){
1111 data = JSON.stringify(data);
1112 win.postMessage(data, '*');
1113 };
1114
1115 MessageReceiver.awaitMessage = function(object, callback){
1116 initCommunication(object, callback);
1117 };
1118
1119 var initCommunication = function(object, callback){
1120 callbackFunction = function(e){
1121 receiveMessage(e, callback);
1122 };
1123 window.addEventListener('message', callbackFunction);
1124 win = getProperty(object, 'windowReference');
1125 origin = getProperty(object, 'origin');
1126 };
1127
1128 var getProperty = function(object, prop){
1129 if(object.hasOwnProperty(prop)){
1130 return object[prop];
1131 }
1132 };
1133
1134 MessageReceiver.postUntilConfirmed = function(object){
1135 initCommunication(object);
1136 isMessageConfirmed(config.attempts, getProperty(object, 'message'))
1137 };
1138
1139 var isMessageConfirmed = function(attempt, message){
1140 if (alreadyConfirmed || attempt <= 0) {
1141 return Promise.resolve().then(function(){
1142 window.removeEventListener('message', callbackFunction);
1143 if(attempt <= 0){
1144 console.warn("Nie udało się przekazać adresu z okna głównego.");
1145 }
1146 });
1147 } else if(attempt > 0){
1148 attempt = attempt-1;
1149 postMessage(message);
1150 return Promise.resolve().then(
1151 setTimeout(isMessageConfirmed, config.attemptTimeout, attempt, message)
1152 );
1153 }
1154 };
1155
1156 return MessageReceiver;
1157 }(MessageReceiver || {}));
1158
1159 var COMMON_SOURCE = (function(COMMON_SOURCE) {
1160 COMMON_SOURCE.grabIplaSubtitlesData = function(data){
1161 var items = [];
1162 var subtitles = (((data.result || {}).mediaItem || {}).displayInfo || {}).subtitles || [];
1163 subtitles.forEach(function(subtitle) {
1164 items.push({
1165 url: subtitle.src,
1166 description: subtitle.name,
1167 format: subtitle.format
1168 })
1169 });
1170 return {
1171 cards: {subtitles: {items: items}}
1172 };
1173 };
1174
1175 COMMON_SOURCE.grabTvpVideoData = function(data){
1176 var items = [];
1177 if(data.status == 'OK' && data.formats !== undefined){
1178 $.each(data.formats, function( index, value ) {
1179 if(value.adaptive == false){
1180 var videoDesc = value.totalBitrate;
1181 items.push(Tool.mapDescription({
1182 source: 'TVP',
1183 key: value.totalBitrate,
1184 video: videoDesc,
1185 url: value.url
1186 }));
1187 }
1188 });
1189 return {
1190 title: data.title,
1191 cards: {videos: {items: items}}
1192 }
1193 }
1194 throw new Exception(config.error.noSource, window.location.href);
1195 };
1196
1197 return COMMON_SOURCE;
1198 }(COMMON_SOURCE || {}));
1199 var VOD_TVP = (function() {
1200 var properties = new Configurator({
1201 wrapper: {
1202 selector: 'div.playerContainerWrapper'
1203 },
1204 button: {
1205 class: 'video-block__btn tvp_vod_downlaod_button',
1206 },
1207 asyncChains: {
1208 videos: [
1209 new Step({
1210 urlTemplate: 'https://tvp.pl/pub/stat/videofileinfo?video_id=#videoId',
1211 beforeStep: function (input) {
1212 return idParser();
1213 }
1214 }),
1215 new Step({
1216 urlTemplate: 'https://www.tvp.pl/shared/cdn/tokenizer_v2.php?object_id=#videoId',
1217 beforeStep: function (json) {
1218 return getRealVideoId(json);
1219 },
1220 afterStep: COMMON_SOURCE.grabTvpVideoData
1221 })
1222 ]
1223 }
1224 });
1225
1226 var idParser = function() {
1227 var src = $('div.playerContainer').attr('data-id');
1228 if(src !== undefined){
1229 return {
1230 videoId: src.split("/").pop()
1231 };
1232 }
1233
1234 throw new Exception(config.error.id, window.location.href);
1235 };
1236
1237 var getRealVideoId = function(json){
1238 var videoId = json.copy_of_object_id !== undefined ?
1239 json.copy_of_object_id : json.video_id;
1240 return {
1241 videoId: videoId
1242 };
1243 };
1244
1245 this.setup = function(){
1246 WrapperDetector.run(properties);
1247 };
1248 });
1249
1250 var CYF_TVP = (function() {
1251 var properties = new Configurator({
1252 wrapper: {
1253 selector: 'div.playerContainerWrapper'
1254 },
1255 button: {
1256 class: 'tvp_cyf_downlaod_button'
1257 },
1258 asyncChains: {
1259 videos: [
1260 new Step({
1261 urlTemplate: 'https://www.tvp.pl/shared/cdn/tokenizer_v2.php?object_id=#videoId',
1262 beforeStep: function (input) {
1263 return idParser();
1264 },
1265 afterStep: COMMON_SOURCE.grabTvpVideoData
1266 })
1267 ]
1268 }
1269 });
1270
1271 var idParser = function(){
1272 var src = $('iframe#JS-TVPlayer').attr('src');
1273 if(src !== undefined) {
1274 return src.split("/").pop();
1275 }
1276 else {
1277 var div = $('div.playerWidget');
1278 if(div !== undefined){
1279 return div.attr('data-video-id');
1280 }
1281 }
1282
1283 throw new Exception(config.error.id, window.location.href);
1284 };
1285
1286 this.setup = function(){
1287 WrapperDetector.run(properties);
1288 };
1289 });
1290
1291 var TVP_REG = (function() {
1292 var properties = new Configurator({
1293 wrapper: {
1294 selector: 'div.js-video'
1295 },
1296 button: {
1297 class: 'tvp_reg_download_button'
1298 },
1299 asyncChains: {
1300 videos: [
1301 new Step({
1302 urlTemplate: 'https://www.tvp.pl/shared/cdn/tokenizer_v2.php?object_id=#videoId',
1303 beforeStep: function (input) {
1304 return idParser();
1305 },
1306 afterStep: COMMON_SOURCE.grabTvpVideoData
1307 })
1308 ]
1309 }
1310 });
1311
1312 var idParser = function(){
1313 var dataId = $('div.js-video').attr('data-object-id');
1314 if(dataId != undefined) {
1315 return dataId;
1316 }
1317
1318 throw new Exception(config.error.id, window.location.href);
1319 };
1320
1321 this.setup = function(){
1322 WrapperDetector.run(properties);
1323 };
1324 });
1325
1326 var TVN = (function() {
1327 var properties = new Configurator({
1328 wrapper: {
1329 selector: '#player-container'
1330 },
1331 button: {
1332 class: 'btn btn-primary tvn_download_button'
1333 },
1334 asyncChains: {
1335 videos: [
1336 new Step({
1337 urlTemplate: 'http://player.pl/api/?platform=ConnectedTV&terminal=Panasonic&format=json' +
1338 '&authKey=064fda5ab26dc1dd936f5c6e84b7d3c2&v=3.1&m=getItem&id=#videoId',
1339 beforeStep: function(input){
1340 return idParser();
1341 },
1342 afterStep: function(output) {
1343 return grabVideoData(output);
1344 }
1345 })
1346 ]
1347 },
1348 });
1349
1350 var idParser = function(){
1351 var watchingNow = $('.watching-now').closest('.embed-responsive').find('.embed-responsive-item');
1352 if(watchingNow.length > 0){
1353 return watchingNow.attr('href').split(',').pop();
1354 }
1355
1356 return episodeIdParser();
1357 };
1358
1359 var episodeIdParser = function () {
1360 var match = window.location.href.match(/odcinki,(\d+)\/.*,(\d+)/);
1361 if(match && match[2]){
1362 return match[2];
1363 }
1364
1365 return serialIdParser();
1366 };
1367
1368 var serialIdParser = function () {
1369 var match = window.location.href.match(/odcinki,(\d+)/);
1370 if(match && match[1]){
1371 throw new Exception(config.error.tvnId, Tool.getRealUrl());
1372 }
1373
1374 return vodIdParser();
1375 };
1376
1377 var vodIdParser = function(){
1378 var match = window.location.href.match(/,(\d+)/);
1379 if(match && match[1]){
1380 return match[1];
1381 }
1382
1383 throw new Exception(config.error.tvnId, Tool.getRealUrl());
1384 };
1385
1386 var grabVideoData = function(data){
1387 var items = [];
1388 var main = ((data.item || {}).videos || {}).main || {};
1389 var video_content = main.video_content || {};
1390 if(main.video_content_license_type !== 'WIDEVINE' && video_content && video_content.length > 0){
1391 $.each(video_content, function( index, value ) {
1392 items.push(Tool.mapDescription({
1393 source: 'TVN',
1394 key: value.profile_name,
1395 video: value.profile_name,
1396 url: value.url
1397 }));
1398 });
1399
1400 return {
1401 title: getTitle(data),
1402 cards: {videos: {items: items}}
1403 }
1404 }
1405 throw new Exception(config.error.noSource, Tool.getRealUrl());
1406 };
1407
1408 var getTitle = function(data){
1409 var episode = data.item.episode ? 'E'+Tool.pad(data.item.episode, 2) : '';
1410 var season = data.item.season != null ? 'S'+Tool.pad(data.item.season, 2) : '';
1411 var serie_title = data.item.serie_title != null ? data.item.serie_title : '';
1412 var episodeTitle = data.item.title ? ' ' + data.item.title : '';
1413 var seasonAndEpisode = season + episode;
1414
1415 return serie_title + (seasonAndEpisode !== '' ? ' - ' + seasonAndEpisode : '') +
1416 (episodeTitle !== '' ? ' - ' + episodeTitle : '');
1417 };
1418
1419 var inVodFrame = function(){
1420 var regexp = new RegExp('https:\/\/player\.pl(.*)');
1421 var match = regexp.exec(window.location.href);
1422 if(match[1]) {
1423 window.sessionStorage.setItem(config.storage.topWindowLocation, 'https://vod.pl' + match[1]);
1424 }
1425 };
1426
1427 this.setup = function(){
1428 if(!Tool.isTopWindow()) {
1429 inVodFrame();
1430 }
1431
1432 WrapperDetector.run(properties, this.setup);
1433 };
1434 });
1435
1436 var IPLA = (function() {
1437 var properties = new Configurator({
1438 wrapper: {
1439 selector: 'div.player-wrapper:visible:first-child, div.promo-box:visible:first-child,' +
1440 ' div.player-error-presentation:visible:first-child'
1441 },
1442 button: {
1443 class: 'ipla_download_button'
1444 },
1445 chainSelector: function(){
1446 return ['videos', 'subtitles'];
1447 },
1448 asyncChains: {
1449 videos: [
1450 new Step({
1451 urlTemplate: 'https://getmedia.redefine.pl/vods/get_vod/?cpid=1' +
1452 '&ua=www_iplatv_html5/12345&media_id=#videoId',
1453 beforeStep: function (input) {
1454 return idParser();
1455 },
1456 afterStep: function(data){
1457 return grabVideoData(data);
1458 }
1459 })
1460 ],
1461 subtitles: [
1462 new Step({
1463 urlTemplate: 'https://b2c.redefine.pl/rpc/navigation/',
1464 method: 'POST',
1465 methodParam: function(){
1466 return getParamsForSubtitles();
1467 },
1468 afterStep: COMMON_SOURCE.grabIplaSubtitlesData
1469 })
1470 ]
1471 }
1472 });
1473
1474 var grabVideoData = function(data){
1475 var items = [];
1476 var vod = data.vod || {};
1477 if(vod.copies && vod.copies.length > 0){
1478 $.each(vod.copies, function( index, value ) {
1479 var videoDesc = value.quality_p + ', ' + value.bitrate;
1480 items.push(Tool.mapDescription({
1481 source: 'IPLA',
1482 key: value.quality_p,
1483 video: videoDesc,
1484 url: value.url
1485 }));
1486 });
1487 return {
1488 title: vod.title,
1489 cards: {videos: {items: items}}
1490 }
1491 }
1492 throw new Exception(config.error.noSource, Tool.getRealUrl());
1493 };
1494
1495 var getParamsForSubtitles = function(){
1496 var mediaId = idParser();
1497 return {
1498 jsonrpc: "2.0",
1499 id: 1,
1500 method: "prePlayData",
1501 params: {
1502 userAgentData: {
1503 application: "firefox",
1504 portal: "ipla"
1505 },
1506 cpid: 1,
1507 mediaId: mediaId
1508 }
1509 }
1510 };
1511
1512 var idParser = function(){
1513 var match = location.href.match(/[\a-z\d]{32}/);
1514 if(match && match[0]) {
1515 return match[0];
1516 }
1517
1518 return grabVideoIdFromWatchingNowElement();
1519 };
1520
1521 this.setup = function(){
1522 WrapperDetector.run(properties, this.setup);
1523 };
1524
1525 var grabVideoIdFromWatchingNowElement = function(){
1526 var href = $('div.vod-image-wrapper__overlay').closest('a').attr('href');
1527 if(href !== undefined){
1528 var match = href.match(/[\a-z\d]{32}/);
1529 if(match && match[0]){
1530 return match[0];
1531 }
1532 }
1533 return grabVideoIdFromHtmlElement();
1534 };
1535
1536 var grabVideoIdFromHtmlElement = function(){
1537 var frameSrc = $('app-commercial-wallpaper iframe:first-child').attr('src');
1538 if(frameSrc !== undefined) {
1539 return Tool.getUrlParameter('vid', frameSrc);
1540 }
1541
1542 throw new Exception(config.error.id, Tool.getRealUrl());
1543 };
1544 });
1545
1546 var VOD = (function() {
1547 var properties = new Configurator({
1548 wrapper: {
1549 selector: '#v_videoPlayer'
1550 },
1551 button: {
1552 class: 'vod_download_button'
1553 },
1554 asyncChains: {
1555 videos: [
1556 new Step({
1557 urlTemplate: 'https://player-api.dreamlab.pl/?body[id]=#videoId&body[jsonrpc]=2.0' +
1558 '&body[method]=get_asset_detail&body[params][ID_Publikacji]=#videoId' +
1559 '&body[params][Service]=vod.onet.pl&content-type=application/jsonp' +
1560 '&x-onet-app=player.front.onetapi.pl&callback=',
1561 beforeStep: function (input) {
1562 return idParser();
1563 },
1564 afterStep: function (output) {
1565 return grabVideoData(output);
1566 }
1567 })
1568 ]
1569 }
1570 });
1571
1572 var idParser = function () {
1573 var id = $(".mvp").attr('id');
1574 if(id !== undefined){
1575 return id.match(/mvp:(.+)/)[1];
1576 }
1577
1578 return parseFromJS();
1579 };
1580
1581 var parseFromJS = function(){
1582 var scripts = $('script[type="text/javascript"]').filter(':not([src])');
1583 for (var i = 0; i < scripts.length; i++) {
1584 var match = $(scripts[i]).text().match(/\"mvpId\"\s*:\s*\"(\d+\.\d+)\"/);
1585 if(match && match[1]){
1586 return match[1];
1587 }
1588 }
1589
1590 throw new Exception(config.error.id, Tool.getRealUrl());
1591 };
1592
1593 var grabVideoData = function (data) {
1594 var items = [];
1595 var subtitlesItems = [];
1596 var video = (((data.result || new Array())[0] || {}).formats || {}).wideo || {};
1597 var meta = ((data.result || new Array())[0] || {}).meta || {};
1598 var subtitles = meta.subtitles || [];
1599 var videoData = video['mp4-uhd'] && video['mp4-uhd'].length > 0 ? video['mp4-uhd'] : video['mp4'];
1600 if(videoData && videoData.length > 0){
1601 videoData.forEach(function(value) {
1602 var videoDesc = value.vertical_resolution + ', ' + value.video_bitrate;
1603 items.push(Tool.mapDescription({
1604 source: 'VOD',
1605 key: value.vertical_resolution,
1606 video: videoDesc,
1607 url: value.url
1608 }));
1609 });
1610
1611 subtitles.forEach(function(subtitle) {
1612 var extension = subtitle.name.split('.').pop();
1613 subtitlesItems.push({
1614 url: subtitle.url,
1615 format: extension,
1616 description: subtitle.name
1617 })
1618 });
1619
1620 return {
1621 title: meta.title,
1622 cards: {
1623 videos: {items: items},
1624 subtitles: {items: subtitlesItems}
1625 }
1626 }
1627 }
1628 throw new Exception(config.error.noSource, Tool.getRealUrl());
1629 };
1630
1631 var iplaDetected = function(){
1632 return $('#v_videoPlayer div.pulsembed_embed').length > 0;
1633 };
1634
1635 var workWithSubService = function(){
1636 var src = 'https://pulsembed.eu';
1637 var frameSelector = 'iframe[src^="' + src + '"]';
1638
1639 ElementDetector.detect(frameSelector, function () {
1640 MessageReceiver.postUntilConfirmed({
1641 windowReference: $(frameSelector).get(0).contentWindow,
1642 origin: src,
1643 message: {
1644 location: window.location.href
1645 }
1646 });
1647 });
1648 };
1649
1650 this.setup = function(){
1651 if(iplaDetected()) {
1652 workWithSubService();
1653 }
1654 else if(Tool.isTopWindow()){
1655 WrapperDetector.run(properties);
1656 }
1657 };
1658 });
1659
1660 var VOD_IPLA = (function() {
1661 var properties = new Configurator({
1662 wrapper: {
1663 selector: '#player-wrapper, #playerContainer'
1664 },
1665 button: {
1666 class: 'vod_ipla_downlaod_button'
1667 },
1668 chainSelector: function(){
1669 return ['videos', 'subtitles'];
1670 },
1671 asyncChains: {
1672 videos: [
1673 new Step({
1674 urlTemplate: 'https://distro.redefine.pl/partner_api/v1/2yRS5K/media/#media_id/vod/player_data?' +
1675 'dev=pc&os=linux&player=html&app=firefox&build=12345',
1676 beforeStep: function (input) {
1677 return {media_id: idParser()};
1678 },
1679 afterStep: function(data){
1680 return grabVideoData(data);
1681 }
1682 })
1683 ],
1684 subtitles: [
1685 new Step({
1686 afterStep: function (output) {
1687 return parseSubtitleData();
1688 }
1689 })
1690 ]
1691 }
1692 });
1693
1694 var grabVideoData = function(data){
1695 var items = [];
1696 var displayInfo = (data.mediaItem || {}).displayInfo || {};
1697 var mediaSources = ((data.mediaItem || {}).playback || {}).mediaSources || {};
1698 var videos = $.grep(mediaSources, function(source) {
1699 return source.accessMethod === 'direct';
1700 });
1701 if(videos && videos.length > 0){
1702 $.each(videos, function( index, value ) {
1703 items.push(Tool.mapDescription({
1704 source: 'IPLA',
1705 key: value.quality,
1706 video: value.quality,
1707 url: value.url
1708 }));
1709 });
1710 return {
1711 title: displayInfo.title,
1712 cards: {videos: {items: items}}
1713 }
1714 }
1715 throw new Exception(config.error.noSource, Tool.getRealUrl());
1716 };
1717
1718 var getJson = function(){
1719 var match = $('script:not(:empty)').text().match(/(window\.CP\.embedSetup\()(.*)\);/);
1720 if(match) {
1721 var jsonObject = JSON.parse(match[2]);
1722 return JSON.parse(jsonObject[0].media);
1723 }
1724
1725 return {};
1726 };
1727
1728 var idParser = function(){
1729 try {
1730 if($('#player-wrapper').length > 0) {
1731 return (((getJson() || {}).result || {}).mediaItem || {}).id;
1732 }
1733 else if($('#playerContainer').length > 0){
1734 return getMediaId();
1735 }
1736 }
1737 catch(e){
1738 throw new Exception(config.error.id, Tool.getRealUrl());
1739 }
1740 };
1741
1742 var getMediaId = function(){
1743 var match = $('script:not(:empty)').text().match(/mediaId: "(\w+)",/);
1744 return match[1];
1745 };
1746
1747 var parseSubtitleData = function(){
1748 return COMMON_SOURCE.grabIplaSubtitlesData(getJson());
1749 };
1750
1751 this.setup = function(){
1752 var callback = function(data) {
1753 window.sessionStorage.setItem(config.storage.topWindowLocation, data.location);
1754 WrapperDetector.run(properties);
1755 };
1756 MessageReceiver.awaitMessage({
1757 origin: 'https://pulsembed.eu',
1758 windowReference: window.parent
1759 }, callback);
1760 };
1761 });
1762
1763 var WP = (function() {
1764 var properties = new Configurator({
1765 wrapper: {
1766 selector: '#Player0 > div'
1767 },
1768 button: {
1769 class: 'wp_download_button material__category'
1770 },
1771 asyncChains: {
1772 videos: [
1773 new Step({
1774 urlTemplate: 'https://video.wp.pl/player/mid,#videoId,embed.json',
1775 beforeStep: function (input) {
1776 return idParser();
1777 },
1778 afterStep: function (output) {
1779 return grabVideoData(output);
1780 }
1781 })
1782 ]
1783 }
1784 });
1785
1786 var idParser = function () {
1787 try {
1788 var id = window.location.href.match(/^(.*)-(\d+)v$/)[2];
1789 //__NEXT_DATA__ is a variable on page
1790 return __NEXT_DATA__.props.initialPWPState.material[id].mid;
1791 }
1792 catch(e){
1793 throw new Exception(config.error.id, window.location.href);
1794 }
1795 };
1796
1797 var grabVideoData = function(data){
1798 var items = [];
1799 var urls = (data.clip || {}).url || {};
1800 if(urls && urls.length > 0){
1801 $.each(urls, function( index, value ) {
1802 if(value.type === 'mp4@avc'){
1803 var videoDesc = value.quality + ', ' + value.resolution;
1804 items.push(Tool.mapDescription({
1805 source: 'WP',
1806 key: value.quality,
1807 video: videoDesc,
1808 url: value.url
1809 }));
1810 }
1811 });
1812 return {
1813 title: data.clip.title,
1814 cards: {videos: {items: items}}
1815 }
1816 }
1817 throw new Exception(config.error.noSource, window.location.href);
1818 };
1819
1820 this.setup = function(){
1821 WrapperDetector.run(properties, this.setup);
1822 };
1823 });
1824
1825 var CDA = (function() {
1826 var properties = new Configurator({
1827 wrapper: {
1828 selector: '.pb-video-player-wrap'
1829 },
1830 button: {
1831 class: 'cda_download_button',
1832 click: function(){
1833 clickButton();
1834 }
1835 }
1836 });
1837
1838 var clickButton = function(){
1839 var w = window.open();
1840 try {
1841 var url = $("video.pb-video-player").attr('src');
1842 if(url !== undefined){
1843 if(!url.match(/blank\.mp4/)){
1844 prepareResult(url, w);
1845 }
1846 else if(l !== undefined){
1847 prepareResult(l, w);
1848 }
1849 else {
1850 throw new Exception(config.error.id, window.location.href);
1851 }
1852 }
1853 }catch(e){
1854 DomTamper.handleError(e, w);
1855 }
1856 };
1857
1858 var prepareResult = function(url, w) {
1859 var cardsData = properties.cardsData;
1860 var title = $('meta[property="og:title"]');
1861 var quality = $('.quality-btn-active');
1862 cardsData.title = title.length > 0 ? title.attr('content').trim() : 'brak danych';
1863 var videoDesc = quality.length > 0 ? quality.text() : '-';
1864 cardsData.cards['videos'].items = [
1865 Tool.mapDescription({
1866 source: 'CDA',
1867 key: videoDesc,
1868 video: videoDesc,
1869 audio: '-',
1870 url: url
1871 })
1872 ];
1873
1874 DomTamper.createDocument(cardsData, w);
1875 };
1876
1877 this.setup = function(){
1878 WrapperDetector.run(properties);
1879 };
1880 });
1881
1882 var NINATEKA = (function() {
1883 var properties = new Configurator({
1884 wrapper: {
1885 selector: '#videoPlayer, #player'
1886 },
1887 button: {
1888 class: 'ninateka_download_button',
1889 click: function(){
1890 clickButton();
1891 }
1892 }
1893 });
1894
1895 var prepareResult = function(url, w) {
1896 var title = $('meta[name="title"]');
1897 var cardsData = properties.cardsData;
1898 cardsData.title = title.length > 0 ? title.attr('content').trim() : 'brak danych';
1899 cardsData.cards['videos'].items = [
1900 Tool.mapDescription({
1901 source: 'NINATEKA',
1902 key: 'def',
1903 url: url
1904 })
1905 ];
1906 DomTamper.createDocument(cardsData, w);
1907 };
1908
1909 var getMp4Source = function(w, sources){
1910 for(var i = 0; i < sources.length; i++){
1911 if(sources[i].type && sources[i].type.match(/mp4/g)){
1912 prepareResult(sources[i].src, w);
1913 return;
1914 }
1915 }
1916
1917 throw new Exception(config.error.id, window.location.href);
1918 };
1919
1920 var clickButton = function(){
1921 var w = window.open();
1922 try {
1923 var videoPlayer = $('#videoPlayer').data('player-setup');
1924 var sources = (videoPlayer || {}).sources || {};
1925 if(sources.length > 0){
1926 getMp4Source(w, sources);
1927 }
1928 else {
1929 var scripts = $('script[type="text/javascript"]').filter(':not([src])');
1930 for (var i = 0; i < scripts.length; i++) {
1931 var match = $(scripts[i]).text().match(/fn_\S+\(playerOptionsWithMainSource,\s*\d+\)\.sources/g);
1932 if(match && match[0]){
1933 sources = eval(match[0]);
1934 getMp4Source(w, sources);
1935 break;
1936 }
1937 }
1938 }
1939 }catch(e){
1940 DomTamper.handleError(e, w);
1941 }
1942 };
1943
1944 this.setup = function(){
1945 WrapperDetector.run(properties);
1946 };
1947 });
1948
1949 var ARTE = (function() {
1950 var properties = new Configurator({
1951 wrapper: {
1952 selector: 'div.avp-player'
1953 },
1954 button: {
1955 class: 'arte_download_button',
1956 },
1957 asyncChains: {
1958 videos: [
1959 new Step({
1960 urlTemplate: 'https://api.arte.tv/api/player/v1/config/#langCode/#videoId',
1961 beforeStep: function (input) {
1962 return idParser();
1963 },
1964 afterStep: function (output) {
1965 return grabVideoData(output);
1966 }
1967 })
1968 ]
1969 },
1970 formatter: function(data) {
1971 data.cards['videos'].items.sort(function (a, b) {
1972 return a.index - b.index;
1973 });
1974
1975 var sortingOrder = {'POL': 1};
1976 data.cards['videos'].items.sort(function (a, b) {
1977 var aLangOrder = sortingOrder[a.langCode] ? sortingOrder[a.langCode] : -1,
1978 bLangOrder = sortingOrder[b.langCode] ? sortingOrder[b.langCode] : -1;
1979 return bLangOrder - aLangOrder;
1980
1981 });
1982 }
1983 });
1984
1985 var detectLanguage = function() {
1986 var regexp = new RegExp('https:\/\/www.arte\.tv\/(\\w{2})\/');
1987 var match = regexp.exec(window.location.href);
1988 return match[1];
1989 };
1990
1991 var detectVideoId = function(){
1992 var regexp = new RegExp('https:\/\/www.arte\.tv\/\\w{2}\/videos\/([\\w-]+)\/');
1993 var match = regexp.exec(window.location.href);
1994 return match[1];
1995 };
1996
1997 var idParser = function() {
1998 try {
1999 return {
2000 videoId: detectVideoId(),
2001 langCode: detectLanguage()
2002 };
2003 }
2004 catch(e){
2005 throw new Exception(config.error.id, window.location.href);
2006 }
2007 };
2008
2009 var grabVideoData = function(data){
2010 var items = [];
2011 var title = (((data || {}).videoJsonPlayer || {}).eStat || {}).streamName || '';
2012 var streams = ((data || {}).videoJsonPlayer || {}).VSR || {};
2013 if(streams){
2014 Object.keys(streams).filter(function(k, i) {
2015 return k.startsWith("HTTPS");
2016 }).forEach(function(k) {
2017 var stream = streams[k];
2018 var videoDesc = stream.width + 'x' + stream.height + ', ' + stream.bitrate;
2019 items.push(Tool.mapDescription({
2020 source: 'ARTE',
2021 key: stream.bitrate,
2022 video: videoDesc,
2023 langCode: stream.versionShortLibelle,
2024 language: stream.versionLibelle,
2025 url: stream.url
2026 }));
2027 });
2028 return {
2029 title: title,
2030 cards: {videos: {items: items}}
2031 }
2032 }
2033 throw new Exception(config.error.noSource, window.location.href);
2034 };
2035
2036 this.setup = function(){
2037 WrapperDetector.run(properties);
2038 };
2039
2040 });
2041
2042 var VOD_FRAME = (function() {
2043 this.setup = function(){
2044 var callback = function(data) {
2045 var srcArray = ['https://redir.atmcdn.pl', 'https://partner.ipla.tv'];
2046 setupDetector(srcArray, data);
2047 };
2048 MessageReceiver.awaitMessage({
2049 origin: 'https://vod.pl',
2050 windowReference: window.parent
2051 }, callback);
2052 };
2053
2054 var setupDetector = function(srcArray, data){
2055 var selectors = createArrySelectors(srcArray);
2056 var multiSelector = createMultiSelector(selectors);
2057
2058 ElementDetector.detect(multiSelector, function() {
2059 selectors.forEach(function(element){
2060 if($(element.frameSelector).length > 0){
2061 MessageReceiver.postUntilConfirmed({
2062 windowReference: $(element.frameSelector).get(0).contentWindow,
2063 origin: element.src,
2064 message: {
2065 location: data.location
2066 }
2067 });
2068 }
2069 });
2070 });
2071 };
2072
2073 var createArrySelectors = function(srcArray){
2074 return jQuery.map(srcArray, function(src) {
2075 return {
2076 src: src,
2077 frameSelector: 'iframe[src^="' + src + '"]'
2078 }
2079 });
2080 };
2081
2082 var createMultiSelector = function(selectors){
2083 return $.map(selectors, function(src){
2084 return src.frameSelector
2085 }).join(', ');
2086 }
2087 });
2088
2089 var Starter = (function(Starter) {
2090 var tvZones = [
2091 'bialystok', 'katowice', 'lodz', 'rzeszow', 'bydgoszcz', 'kielce', 'olsztyn', 'szczecin',
2092 'gdansk', 'krakow', 'opole', 'warszawa', 'gorzow', 'lublin', 'poznan', 'wroclaw'
2093 ];
2094
2095 var sources = [
2096 {objectName: 'VOD_TVP', urlPattern: /^https:\/\/vod\.tvp\.pl\/video\//},
2097 {objectName: 'CYF_TVP', urlPattern: /^https:\/\/cyfrowa\.tvp\.pl\/video\//},
2098 {objectName: 'TVP_REG', urlPattern: new RegExp('^https:\/\/(' + tvZones.join('|') + ')\.tvp\.pl\/\\d{6,}\/')},
2099 {objectName: 'TVN', urlPattern: /^https:\/\/(?:w{3}\.)?(?:tvn)?player\.pl\//},
2100 {objectName: 'CDA', urlPattern: /^https:\/\/.*\.cda\.pl\//},
2101 {objectName: 'VOD', urlPattern: /^https:\/\/vod.pl\//},
2102 {objectName: 'VOD_IPLA', urlPattern: /^https:\/\/partner\.ipla\.tv\/embed\/|^https:\/\/.*\.redcdn.pl\/file\/o2\/redefine\/partner\//},
2103 {objectName: 'IPLA', urlPattern: /^https:\/\/www\.ipla\.tv\//},
2104 {objectName: 'WP', urlPattern: /^https:\/\/video\.wp\.pl\//},
2105 {objectName: 'NINATEKA', urlPattern: /^https:\/\/ninateka.pl\//},
2106 {objectName: 'ARTE', urlPattern: /^https:\/\/www.arte.tv\/.*\/videos\//},
2107 {objectName: 'VOD_FRAME', urlPattern: /^https:\/\/pulsembed\.eu\//}
2108 ];
2109
2110 Starter.start = function() {
2111 sources.some(function(source){
2112 if(location.href.match(source.urlPattern)){
2113 var object = eval('new ' + source.objectName + '()');
2114 console.info('voddownloader: jQuery v' + $().jquery + ', context: ' + source.objectName);
2115 object.setup();
2116 return true;
2117 }
2118 });
2119 };
2120
2121 return Starter;
2122 }(Starter || {}));
2123
2124 $(document).ready(function(){
2125 DomTamper.injectStyle(window, 'buttons_css');
2126 Starter.start();
2127 });
2128
2129}).bind(this)(jQuery, platform, Waves);