Greasy Fork

Skrypt umożliwiający pobieranie materiałów ze znanych serwisów VOD.

Skrypt umożliwiający pobieranie materiałów ze znanych serwisów VOD. Działa poprawnie tylko z rozszerzeniem Tampermonkey.

当前为 2018-12-09 提交的版本,查看 最新版本

// ==UserScript==
// @name         Skrypt umożliwiający pobieranie materiałów ze znanych serwisów VOD.
// @namespace    http://www.ipla.tv/
// @include      https://vod.tvp.pl/video/*
// @include      https://cyfrowa.tvp.pl/video/*
// @include      https://www.ipla.tv/*
// @include      https://player.pl/*
// @include      https://www.cda.pl/*
// @exclude      https://www.cda.pl/iframe/*
// @include      https://vod.pl/filmy/*
// @include      https://vod.pl/programy-onetu/*
// @include      https://vod.pl/da-vinci/*
// @include      https://vod.pl/seriale/*
// @include      https://vod.pl/programy-tv/*
// @include      https://redir.atmcdn.pl/*
// @include      https://*.redcdn.pl/file/o2/redefine/partner/*
// @version      5.0.3
// @description  Skrypt umożliwiający pobieranie materiałów ze znanych serwisów VOD. Działa poprawnie tylko z rozszerzeniem Tampermonkey.
//               Cześć kodu pochodzi z:
//               miniskrypt.blogspot.com,
//               miniskrypt.hubaiitv.pl
// @author       Przmus, zacny
// @grant        GM_getResourceText
// @grant        GM_addStyle
// @run-at document-end
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
// @resource css https://raw.githubusercontent.com/zacny/voddownloader/master/voddownloader.css
// ==/UserScript==

(function vodDownloader() {
    'use strict';

    var $ = window.jQuery.noConflict(true);
    var cssText = GM_getResourceText('css');
    GM_addStyle(cssText);

    var ATTEMPTS = 10;
    var ATTEMPT_TIMEOUT = 1500;
    var NO_ID_ERROR_MESSAGE = 'Nie udało się pobrać idetyfikatora.';
    var CALL_ERROR_MESSAGE = 'Błąd pobierania informacji o materiale.';

    var Configurator = (function(Configurator){
        Configurator.setup = function(properties){
            var settings = {
                wrapper: {
                    selector: '',
                    get: function(){
                        return $(settings.wrapper.selector);
                    },
                    exist: function(){
                        return $(settings.wrapper.selector).length > 0;
                    }
                },
                button: {
                    id: 'direct-download',
                    style: '',
                    class: '',
                    click: function(){
                        VideoGrabber.grabVideoData(settings, 0);
                    }
                },
                grabber: {
                    urlTemplates: [],
                    idParser: function(){return null},
                    formatParser: function(data){return {title: null, formats: new Array()}}
                }
            };

            return $.extend(true, settings, properties);
        };
        return Configurator;
    }(Configurator || {}));

    var ChangeVideoDetector = (function(ChangeVideoDetector){
        var checkVideoChange = function(oldSrc, videoChangeCallback) {
            var src = window.location.href;
            if(src !== undefined && oldSrc !== src){
                return Promise.resolve().then(videoChangeCallback);
            }
            else {
                return Promise.resolve().then(setTimeout(checkVideoChange, ATTEMPT_TIMEOUT, oldSrc, videoChangeCallback));
            }
        };

        ChangeVideoDetector.run = function(videoChangeCallback){
            var src = window.location.href;
            checkVideoChange(src, videoChangeCallback);
        };
        return ChangeVideoDetector;
    }(ChangeVideoDetector || {}));

    var WrapperDetector = (function(WrapperDetector){
        var onWrapperExist = function(properties, videoChangeCallback){
            if(properties.wrapper.exist()) {
                DomTamper.createButton(properties);
            }
            else {
                console.info("Nie mam nic do zrobienia");
            }
        };

        var checkWrapperExist = function(attempt, properties, videoChangeCallback){
            //console.log('check: ' + properties.wrapper.exist() + ', [' + attempt + ']');
            if (properties.wrapper.exist() || attempt == 0) {
                return Promise.resolve().then(onWrapperExist(properties, videoChangeCallback));
            } else {
                attempt = (attempt > 0) ? attempt-1 : attempt;
                return Promise.resolve().then(setTimeout(checkWrapperExist, ATTEMPT_TIMEOUT, attempt, properties, videoChangeCallback));
            }
        };

        WrapperDetector.run = function(properties, videoChangeCallback) {
            checkWrapperExist(ATTEMPTS, properties, videoChangeCallback);
            if(typeof videoChangeCallback === "function"){
                ChangeVideoDetector.run(videoChangeCallback);
            }
        };
        return WrapperDetector;
    }(WrapperDetector || {}));

    var VideoGrabber = (function(VideoGrabber){
        var getVideoData = function(url, w){
            return $.ajax({
                url: url,
                method: 'GET',
                dataType: 'json'
            });
        };

        VideoGrabber.grabVideoData = function(vod, templateIndex, w){
            try {
                var idn = vod.grabber.idParser();
                var templates = vod.grabber.urlTemplates;
                var url = templates[templateIndex].replace(/\$idn/g, idn);
                w = (w === undefined) ? window.open(): w;
                //console.log(url);
                getVideoData(url, w).then(function(data){
                    try {
                        var formatData = vod.grabber.formatParser(data);
                        DomTamper.createDocument(formatData, w);
                    }
                    catch(e){
                        DomTamper.handleError(e, w);
                    }
                }, function(data){
                    if(templates[templateIndex+1] !== undefined) {
                        VideoGrabber.grabVideoData(vod, templateIndex+1, w);
                    }
                    else {
                        DomTamper.handleError(CALL_ERROR_MESSAGE, w);
                    }
                });
            }
            catch(e){
                DomTamper.handleError(e, w);
            }
        };
        return VideoGrabber;
    }(VideoGrabber || {}));

    var DomTamper = (function(DomTamper){
        var cssFileContent = cssText;

        var injectStyle = function(w){
            var style = $('<style>').text(cssFileContent);
            $(w.document.head).append(style);
        };

        var prepareContent = function(w){
            injectStyle(w);
            return $('<div>').addClass('download_content');
        };

        DomTamper.handleError = function(message, w){
            if(w === undefined){
                w = window.open();
            }
            injectStyle(w);
            var par = $('<p>').addClass('download_error_message').text(message);
            $(w.document.body).append(prepareContent(w).append(par));
        };

        DomTamper.createButton = function(properties){
            properties.wrapper.get().find('#'+properties.button.id).remove();
            var button = $('<input>').attr('id', properties.button.id).attr('type', 'button')
                .attr('style', properties.button.style).attr('value', 'Pobierz video').addClass(properties.button.class);
            button.bind('click', properties.button.click);
            properties.wrapper.get().append(button);
        };

        var clearPreviousClick = function(body){
            body.find('[id^=contentPar] > input').each(function(event){
                $(this).removeClass('link_copy_click');
            });
            $('#copyTitle', body).removeClass('title_copy_click');
        };

        var videoLinkCopyButtonClick = function(body, par){
            clearPreviousClick(body);

            Tool.copyToClipboard(par.find("a").text());
            par.find("input").addClass('link_copy_click');
        };

        var titleCopyButtonClick = function(body){
            clearPreviousClick(body);

            Tool.copyToClipboard($('#title', body).text());
            $('#copyTitle', body).addClass('title_copy_click');
        };

        var prepareContentActions = function(w, content){
            var body = $(w.document.body);
            body.append(content);

            $(w.document).ready(function() {
                body.find('[id^=contentPar]').each(function(event){
                    var par = $(this);
                    $(this).find("input").click(function(event){
                        videoLinkCopyButtonClick(body, par);
                    });
                });
                $('#copyTitle', body).click(function(){
                    titleCopyButtonClick(body);
                })
            });
        };

        DomTamper.createDocument = function(data, w){
            Tool.numberModeSort(data.formats);

            var content = prepareContent(w);
            var titlePar = $('<p>');
            $('<span>').text('Tytuł: ').appendTo(titlePar);
            $('<span>').attr('id', 'title').text(data.title).appendTo(titlePar);
            $('<input>').attr('id', 'copyTitle').attr('value', 'Kopiuj tytuł').attr('type', 'button')
                .addClass('title_copy_button').appendTo(titlePar);
            titlePar.appendTo(content);
            $.each(data.formats, function( index, value ) {
                var par = $('<p>').attr('id', 'contentPar'+ index).text('Bitrate: ' + value.bitrate);
                if(value.quality !== undefined){
                    par.append(", Jakość: " + value.quality);
                }
                par.append('<br/>').append('Link do materiału:');
                $('<input>').attr('value', 'Kopiuj').attr('type', 'button')
                    .addClass('link_copy_button').appendTo(par);
                par.append('<br/>');
                var link = $('<a>').attr('target', '_blank').attr('href', value.url).text(value.url);
                index === 0 ? link.addClass('best_quility_link_color') : link.addClass('link_color');
                link.appendTo(par);
                par.appendTo(content);
            });

            prepareContentActions(w, content);
        };

        return DomTamper;
    }(DomTamper || {}));

    var Tool = (function(Tool) {
        Tool.copyToClipboard = function(text) {
            var $temp = $("<input>");
            $("body").append($temp);
            $temp.val(text).select();
            document.execCommand("copy");
            $temp.remove();
        };

        Tool.deleteParametersFromUrl = function(url){
            return decodeURIComponent(url.replace(/\?.*/,''));
        };

        Tool.getUrlParameter = function(paramName, url){
            var results = new RegExp('[\?&]' + paramName + '=([^&#]*)').exec(url);
            if (results==null) {
                return null;
            }
            return decodeURIComponent(results[1]) || 0;
        };

        Tool.numberModeSort = function(formats){
            formats.sort(function (a, b) {
                return b.bitrate - a.bitrate;
            });
        };

        return Tool;
    }(Tool || {}));

    var CDA = (function(CDA) {
        var properties = Configurator.setup({
            wrapper: {
                selector: '#player'
            },
            button: {
                class: 'cda_download_button',
                click: function(){
                    var url = $("video.pb-video-player").attr('src');
                    if(url !== undefined){
                        var w = window.open();
                        w.location.href = url;
                    }
                    else {
                        DomTamper.handleError(NO_ID_ERROR_MESSAGE, w);
                    }
                }
            }
        });

        CDA.waitOnWrapper = function(){
            WrapperDetector.run(properties);
        };

        return CDA;
    }(CDA || {}));

    var VOD = (function(VOD) {
        var properties = Configurator.setup({
            wrapper: {
                selector: '#v_videoPlayer'
            },
            button: {
                class: 'vod_download_button'
            },
            grabber: {
                urlTemplates: [
                    'https://player-api.dreamlab.pl/?body[id]=$idn&body[jsonrpc]=2.0&body[method]=get_asset_detail&body[params][ID_Publikacji]=$idn' +
                    '&body[params][Service]=vod.onet.pl&content-type=application/jsonp&x-onet-app=player.front.onetapi.pl&callback=',
                    'https://qi.ckm.onetapi.pl/?body[id]=$idn&body[jsonrpc]=2.0&body[method]=get_asset_detail&body[params][ID_Publikacji]=$idn' +
                    '&body[params][Service]=vod.onet.pl&content-type=application/jsonp&x-onet-app=player.front.onetapi.pl&_=1487536996333'
                ],
                idParser: function(){
                    try {
                        var id = $(".mvp").attr('id')
                        return id.match(/mvp:(.+)/)[1];
                    }
                    catch(e){
                        throw(NO_ID_ERROR_MESSAGE);
                    }
                },
                formatParser: function(data){
                    var formats = [];
                    var wideo = (((data.result || new Array())[0] || {}).formats || {}).wideo || {};
                    var meta = ((data.result || new Array())[0] || {}).meta || {};
                    if(wideo.mp4 && wideo.mp4.length > 0){
                        $.each(wideo.mp4, function( index, value ) {
                            formats.push({
                                quality: value.vertical_resolution,
                                bitrate: value.video_bitrate,
                                url: value.url
                            });
                        });
                    }
                    return {
                        title: meta.title,
                        formats: formats
                    }
                }
            }
        });

        var isTopWindow = function(){
            return window.top === window.self;
        };

        var iplaSectionDetected = function(){
            return $('#v_videoPlayer div.pulsembed_embed').length > 0;
        };

        VOD.waitOnWrapper = function(){
            if(isTopWindow() && !iplaSectionDetected()){
                WrapperDetector.run(properties);
            }
        };

        return VOD;
    }(VOD || {}));

    var VOD_IPLA = (function(VOD_IPLA) {
        var properties = Configurator.setup({
            wrapper: {
                selector: '#player-wrapper'
            },
            button: {
                class: 'vod_ipla_downlaod_button'
            },
            grabber: {
                urlTemplates: ['https://getmedia.redefine.pl/vods/get_vod/?cpid=1&ua=www_iplatv_html5/12345&media_id=$idn'],
                idParser: function(){
                    try {
                        var match = $('script:not(:empty)').text().match(/(window\.CP\.embedSetup\()(.*)\);/);
                        var jsonObject = JSON.parse(match[2]);
                        return JSON.parse(jsonObject[0].media).result.mediaItem.id;
                    }
                    catch(e){
                        throw(NO_ID_ERROR_MESSAGE);
                    }
                },
                formatParser: function(data){
                    return IPLA.grabVideoFormats(data);
                }
            }
        });

        VOD_IPLA.waitOnWrapper = function(){
            WrapperDetector.run(properties);
        };

        return VOD_IPLA;
    }(VOD_IPLA || {}));

    var TVN = (function(TVN) {
        var properties = Configurator.setup({
            wrapper: {
                selector: '#player-container'
            },
            button: {
                class: 'btn btn-primary tvn_download_button'
            },
            grabber: {
                urlTemplates: ['/api/?platform=ConnectedTV&terminal=Panasonic&format=json&authKey=064fda5ab26dc1dd936f5c6e84b7d3c2&v=3.1&m=getItem&id=$idn'],
                idParser: function(){
                    try {
                        var url = $('#quarticon-event-image').attr('src');
                        var dataParam = Tool.getUrlParameter('data', url);
                        var data = JSON.parse(dataParam);
                        return data.articleId;
                    }
                    catch(e){
                        return grabVideoIdFromUrl();
                    }
                }
            }
        });

        properties.grabber.formatParser = function(data, w){
            var formats = [];
            var title;
            var video_content = (((data.item || {}).videos || {}).main || {}).video_content || {};
            if(video_content && video_content.length > 0){
                $.each(video_content, function( index, value ) {
                    var lastPartOfUrl = Tool.deleteParametersFromUrl(value.url).split("/").pop();
                    var bitrate = lastPartOfUrl.match(/\d{2,}/g);
                    formats.push({
                        quality: value.profile_name,
                        bitrate: bitrate,
                        url: value.url
                    });
                });
                title = data.item.episode != null ? 'E'+data.item.episode : '';
                title = data.item.season != null ? 'S'+data.item.season + title : title;
                if(data.item.serie_title != null){
                    title = data.item.serie_title + (title != '' ? ' - ' + title : '');
                }
            }
            return {
                title: title,
                formats: formats
            }
        };

        var grabVideoIdFromUrl = function(){
            var pageURL = window.location.href;
            var lastComma = pageURL.lastIndexOf(",");
            if (lastComma > - 1) {
                return pageURL.substring(lastComma+1);
            }

            throw NO_ID_ERROR_MESSAGE;
        };

        TVN.waitOnWrapper = function(){
            WrapperDetector.run(properties, TVN.waitOnWrapper);
        };

        return TVN;
    }(TVN || {}));

    var VOD_TVP = (function(VOD_TVP) {
        var properties = Configurator.setup({
            wrapper: {
                selector: '#JS-TVPlayer-Wrapper'
            },
            button: {
                class: 'video-block__btn tvp_vod_downlaod_button'
            },
            grabber: {
                urlTemplates: ['https://www.tvp.pl/shared/cdn/tokenizer_v2.php?object_id=$idn'],
                idParser: function(){
                    try {
                        var src = properties.wrapper.get().attr('data-src');
                        return src.split("/").pop();
                    }
                    catch(e){
                        throw NO_ID_ERROR_MESSAGE;
                    }
                },
                formatParser: function(data){
                    return VOD_TVP.grabVideoFormats(data);
                }
            }
        });

        VOD_TVP.grabVideoFormats = function(data){
            var formats = [];
            if(data.status == 'OK' && data.formats !== undefined){
                $.each(data.formats, function( index, value ) {
                    if(value.adaptive == false){
                        formats.push({
                            bitrate: value.totalBitrate,
                            url: value.url
                        });
                    }
                });
            }

            return {
                title: data.title,
                formats: formats
            };
        };

        VOD_TVP.waitOnWrapper = function(){
            WrapperDetector.run(properties);
        };

        return VOD_TVP;
    }(VOD_TVP || {}));

    var CYF_TVP = (function(CYF_TVP) {
        var properties = Configurator.setup({
            wrapper: {
                selector: 'div.playerContainerWrapper'
            },
            button: {
                class: 'video-block__btn tvp_cyf_downlaod_button'
            },
            grabber: {
                urlTemplates: ['https://www.tvp.pl/shared/cdn/tokenizer_v2.php?object_id=$idn'],
                idParser: function(){
                    try {
                        var src = $('iframe#JS-TVPlayer').attr('src');
                        return src.split("/").pop();
                    }
                    catch(e){
                        throw NO_ID_ERROR_MESSAGE;
                    }
                },
                formatParser: function(data){
                    return VOD_TVP.grabVideoFormats(data);
                }
            }
        });

        CYF_TVP.waitOnWrapper = function(){
            WrapperDetector.run(properties);
        };

        return CYF_TVP;
    }(CYF_TVP || {}));

    var IPLA = (function(IPLA) {
        var properties = Configurator.setup({
            wrapper: {
                selector: 'div.player-wrapper:visible:first-child, div.promo-box:visible:first-child'
            },
            button: {
                class: 'ipla_download_button'
            },
            grabber: {
                urlTemplates: ['https://getmedia.redefine.pl/vods/get_vod/?cpid=1&ua=www_iplatv_html5/12345&media_id=$idn'],
                idParser: function(){
                    if(location.href.match(/[\a-z\d]{32}/) !== null){
                        return window.location.href.match(/[\a-z\d]{32}/)[0];
                    }

                    return grabVideoIdFromHtmlElement();
                },
                formatParser: function(data){
                    return IPLA.grabVideoFormats(data);
                }
            }
        });

        IPLA.waitOnWrapper = function(){
            WrapperDetector.run(properties, IPLA.waitOnWrapper);
        };

        IPLA.grabVideoFormats = function(data){
            var formats = [];
            var vod = data.vod || {};
            if(vod.copies && vod.copies.length > 0){
                $.each(vod.copies, function( index, value ) {
                    formats.push({
                        bitrate: value.bitrate,
                        url: value.url,
                        quality: value.quality_p
                    });
                });
            }
            return {
                title: vod.title,
                formats: formats
            }
        };

        var grabVideoIdFromHtmlElement = function(){
            try{
                var frameSrc = $('app-commercial-wallpaper iframe:first-child').attr('src');
                return Tool.getUrlParameter('vid', frameSrc);
            }
            catch(e){
                return grabVideoIdFromUrl();
            }
        };

        var grabVideoIdFromUrl = function(){
            var pageURL = location.href.split("?")[0];
            var pageURLTemp = pageURL.substring(0, pageURL.length - 3);
            var lastSlash = pageURLTemp.lastIndexOf("/");
            if (lastSlash > - 1) {
                return pageURL.substring(lastSlash+1);
            }

            throw NO_ID_ERROR_MESSAGE;
        };

        return IPLA;
    }(IPLA || {}));

    var Starter = (function(Starter) {
        var matcher = [
            {action: VOD_TVP.waitOnWrapper, pattern: /^https:\/\/vod\.tvp\.pl\//},
            {action: CYF_TVP.waitOnWrapper, pattern: /^https:\/\/cyfrowa\.tvp\.pl\//},
            {action: TVN.waitOnWrapper, pattern: /^https:\/\/(?:w{3}\.)?(?:tvn)?player\.pl\//},
            {action: CDA.waitOnWrapper, pattern: /^https:\/\/www\.cda\.pl\//},
            {action: VOD.waitOnWrapper, pattern: /^https:\/\/vod\.pl\//},
            {action: VOD_IPLA.waitOnWrapper, pattern: /^https:\/\/.*\.redcdn.pl\/file\/o2\/redefine\/partner\//},
            {action: IPLA.waitOnWrapper, pattern: /^https:\/\/www\.ipla\.tv\//}
        ];

        Starter.start = function() {
            matcher.some(function(item){
                if(location.href.match(item.pattern)){
                    item.action();
                    return true;
                }
            });
        };

        return Starter;
    }(Starter || {}));

    $(document).ready(function(){
        console.info('jQuery: ' + $().jquery);
        Starter.start();
    });

})();