Movie player for BMX

Movie player on BMX

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey to install this script.

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name Movie player for BMX
// @namespace gdcytube
// @description Movie player on BMX
// @include http://psp-tv.com/r/BMX
// @include http://psp-tv.com/r/BMX
// @include http://bmx-tv.net/dick.html
// @include http://bmx-tv.net/*
// @grant unsafeWindow
// @grant GM_xmlhttpRequest
// @grant GM.xmlHttpRequest
// @connect docs.google.com
// @run-at document-end
// @version 1.7.0
// ==/UserScript==

try {
    function debug(message) {
        try {
            unsafeWindow.console.log('[Drive]', message);
        } catch (error) {
            unsafeWindow.console.error(error);
        }
    }

    function httpRequest(opts) {
        if (typeof GM_xmlhttpRequest === 'undefined') {
            // Assume GM4.0
            debug('Using GM4.0 GM.xmlHttpRequest');
            GM.xmlHttpRequest(opts);
        } else {
            debug('Using old-style GM_xmlhttpRequest');
            GM_xmlhttpRequest(opts);
        }
    }

    var ITAG_QMAP = {
        37: 1080,
        46: 1080,
        22: 720,
        45: 720,
        59: 480,
        44: 480,
        35: 480,
        18: 360,
        43: 360,
        34: 360
    };

    var ITAG_CMAP = {
        43: 'video/webm',
        44: 'video/webm',
        45: 'video/webm',
        46: 'video/webm',
        18: 'video/mp4',
        22: 'video/mp4',
        37: 'video/mp4',
        59: 'video/mp4',
        35: 'video/flv',
        34: 'video/flv'
    };

    function getVideoInfo(id, cb) {
        var url = 'https://docs.google.com/get_video_info?authuser='
                + '&docid=' + id
                + '&sle=true'
                + '&hl=en';
        debug('Fetching ' + url);

        httpRequest({
            method: 'GET',
            url: url,
            onload: function (res) {
                try {
                    debug('Got response ' + res.responseText);

                    if (res.status !== 200) {
                        debug('Response status not 200: ' + res.status);
                        return cb(
                            'Google Drive request failed: HTTP ' + res.status
                        );
                    }

                    var data = {};
                    var error;
                    // Google Santa sometimes eats login cookies and gets mad if there aren't any.
                    if(/accounts\.google\.com\/ServiceLogin/.test(res.responseText)){
                        error = 'Google Docs request failed: ' +
                                'This video requires you be logged into a Google account. ' +
                                'Open your Gmail in another tab and then refresh video.';
                        return cb(error);
                    }

                    res.responseText.split('&').forEach(function (kv) {
                        var pair = kv.split('=');
                        data[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
                    });

                    if (data.status === 'fail') {
                        error = 'Google Drive request failed: ' +
                                unescape(data.reason).replace(/\+/g, ' ');
                        return cb(error);
                    }

                    if (!data.fmt_stream_map) {
                        error = (
                            'Google has removed the video streams associated' +
                            ' with this item.  It can no longer be played.'
                        );

                        return cb(error);
                    }

                    data.links = {};
                    data.fmt_stream_map.split(',').forEach(function (item) {
                        var pair = item.split('|');
                        data.links[pair[0]] = pair[1];
                    });
                    data.videoMap = mapLinks(data.links);

                    cb(null, data);
                } catch (error) {
                    unsafeWindow.console.error(error);
                }
            },

            onerror: function () {
                var error = 'Google Drive request failed: ' +
                            'metadata lookup HTTP request failed';
                error.reason = 'HTTP_ONERROR';
                return cb(error);
            }
        });
    }

    function mapLinks(links) {
        var videos = {
            1080: [],
            720: [],
            480: [],
            360: []
        };

        Object.keys(links).forEach(function (itag) {
            itag = parseInt(itag, 10);
            if (!ITAG_QMAP.hasOwnProperty(itag)) {
                return;
            }

            videos[ITAG_QMAP[itag]].push({
                itag: itag,
                contentType: ITAG_CMAP[itag],
                link: links[itag]
            });
        });

        return videos;
    }

    /*
     * Greasemonkey 2.0 has this wonderful sandbox that attempts
     * to prevent script developers from shooting themselves in
     * the foot by removing the trigger from the gun, i.e. it's
     * impossible to cross the boundary between the browser JS VM
     * and the privileged sandbox that can run GM_xmlhttpRequest().
     *
     * So in this case, we have to resort to polling a special
     * variable to see if getGoogleDriveMetadata needs to be called
     * and deliver the result into another special variable that is
     * being polled on the browser side.
     */

    /*
     * Browser side function -- sets gdUserscript.pollID to the
     * ID of the Drive video to be queried and polls
     * gdUserscript.pollResult for the result.
     */
    function getGoogleDriveMetadata_GM(id, callback) {
        debug('Setting GD poll ID to ' + id);
        unsafeWindow.gdUserscript.pollID = id;
        var tries = 0;
        var i = setInterval(function () {
            if (unsafeWindow.gdUserscript.pollResult) {
                debug('Got result');
                clearInterval(i);
                var result = unsafeWindow.gdUserscript.pollResult;
                unsafeWindow.gdUserscript.pollResult = null;
                callback(result.error, result.result);
            } else if (++tries > 100) {
                // Took longer than 10 seconds, give up
                clearInterval(i);
            }
        }, 100);
    }

    /*
     * Sandbox side function -- polls gdUserscript.pollID for
     * the ID of a Drive video to be queried, looks up the
     * metadata, and stores it in gdUserscript.pollResult
     */
    function setupGDPoll() {
        unsafeWindow.gdUserscript = cloneInto({}, unsafeWindow);
        var pollInterval = setInterval(function () {
            if (unsafeWindow.gdUserscript.pollID) {
                var id = unsafeWindow.gdUserscript.pollID;
                unsafeWindow.gdUserscript.pollID = null;
                debug('Polled and got ' + id);
                getVideoInfo(id, function (error, data) {
                    unsafeWindow.gdUserscript.pollResult = cloneInto({
                        error: error,
                        result: data
                    }, unsafeWindow);
                });
            }
        }, 1000);
    }

    var TM_COMPATIBLES = [
        'Tampermonkey',
        'Violentmonkey' // https://github.com/calzoneman/sync/issues/713
    ];

    function isTampermonkeyCompatible() {
        try {
            return TM_COMPATIBLES.indexOf(GM_info.scriptHandler) >= 0;
        } catch (error) {
            return false;
        }
    }

    if (isTampermonkeyCompatible()) {
        unsafeWindow.getGoogleDriveMetadata = getVideoInfo;
    } else {
        debug('Using non-TM polling workaround');
        unsafeWindow.getGoogleDriveMetadata = exportFunction(
                getGoogleDriveMetadata_GM, unsafeWindow);
        setupGDPoll();
    }

    unsafeWindow.console.log('Initialized userscript Google Drive player');
    unsafeWindow.hasDriveUserscript = true;
    // Checked against GS_VERSION from data.js
    unsafeWindow.driveUserscriptVersion = '1.7';
} catch (error) {
    unsafeWindow.console.error(error);
}