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