UserJS для воспроизведения H.264 в «Опере»

Существует плагин «DivX web player», который отлично подменяет тег «VIDEO», в случае, если должен быть использован кодек H.264, но браузер его не поддерживает.

К сожалению, в «Опере» он почему-то не работает. Я сделал UserJS, который, пользуясь этим плагином, подменяет видеотег, если у него указан тип «video/mp4». Все атрибуты он не переносит (я просто пока не столкнулся с тем, что это нужно), буду добавлять, если столкнусь. Кроме того, я подменяю метод «canPlayType», чтобы показать, что браузер H.264 поддерживает.

Самая засада — это тег «VIDEO» без размеров. Мне для поддержки этого приходтся запускать проигрывание и когда становятся доступны медиаданные, останавливать воспроизведение и переписывать размеры видео.
// ==UserScript==
// @name        XVid patch
// @author      Evgeny Stepanischev aka Bolk
// @version     1.00
// @namespace   http://bolknote.ru/files/opera-xvid-patch.js
// @modified    2012-04-14
// @include     http://*
// ==/UserScript==

!function () {
    var htmlif = function (attr, attrname) {
        return attr ? attrname + '="' + attr + '" ' : '';
    }

    var patchtag = function (o, src) {
        var html = '<embed type="video/divx" '+
        'custommode="none" ';

        if (o.preload == 'auto') html += 'bufferingMode="full" '; else
        if (o.preload == 'none') html += 'bufferingMode="null" ';

        html += htmlif(o.poster, 'previewImage')+
                htmlif(o.id, 'id')+
                htmlif(o.name, 'name')+
                htmlif(src || o.src, 'src')+
                htmlif(o.controls ? 'full' : 'null', 'mode')+
                htmlif(o.width, 'width')+
                htmlif(o.height, 'height');

        if (!o.height || !o.width) {
            html += 'onloadedmetadata="this.width=this.videoWidth;this.height=this.videoHeight" ';
            html += htmlif(!o.autoplay && 'if (!this._sized) {this.pause(); this.currentTime=0; this._sized=1}', 'onplay');

            html += 'autoPlay=true ';
        } else {
            html += htmlif(o.autoplay ? 'true' : 'false', 'autoPlay');
        }

        html += 'pluginspage="http://go.divx.com/plugin/download/"></embed>';

        o.outerHTML = html;
    };

    var patch = function () {
        window.opera.addEventListener('BeforeScript', function() {

            with (window.HTMLVideoElement)
            if (prototype._canPlayType === undefined) {
                prototype._canPlayType = prototype.canPlayType;

                prototype.canPlayType = function (type) {
                    if (type.substr(0, 9) == 'video/mp4') {
                        return 'probably';
                    }

                    return this._canPlayType(type);
                }
            }

            !function() {
                var videos = document.getElementsByTagName('VIDEO');

                for (var i = 0, vl = videos.length; i<vl; i++) {
                    if (!videos[i]) continue;

                    if (videos[i].type && videos[i].type.substr(0, 9) == 'video/mp4') {

                    } else {
                        var sources = videos[i].getElementsByTagName('source');

                        var foundSupported = false, foundMP4 = false;

                        for (var j = 0, sl = sources.length; j < sl; j++) {
                            if (videos[i]._canPlayType(sources[j].type) !== '') {
                                foundSupported = true;
                                break;
                            }

                            if (sources[j].type.substr(0, 9) == 'video/mp4') {
                                foundMP4 = sources[j].src;
                            }
                        }

                        if (!foundSupported && foundMP4) {
                            patchtag(videos[i], foundMP4);
                        }
                    }
                }
            }();
        });
    };

    for (var i = 0, l = navigator.plugins.length; i < l; i++) {
        if (navigator.plugins[i].filename == 'DivXBrowserPlugin.plugin') {
            return patch();
        }
    }
}();
Добавлено: в комментариях справдливо указали на недостаток — перехватывать надо не только BeforeScript. Исправлять не буду до первого реального случая, когда где-то этого события не будет достаточно.
15 апреля 2012 13:18

Ivan (инкогнито)
15 апреля 2012, 14:28

если должен использован кодек H.264
"быть" пропущено

Ivan (инкогнито)
15 апреля 2012, 14:29

Имеется ввиду формал "был", конечно. :)

Морозов (morozov.livejournal.com)
15 апреля 2012, 15:15

А что означает конструкция !function(){}()? Та же ли это самовызывающаяся анонимная функция, что и (function(){})() и в чём смысл/особенность такой формы записи?

GreLI (инкогнито)
15 апреля 2012, 16:21, ответ предназначен Морозов (morozov.livejournal.com):

Да, это тоже самое. Фокус в восклицательном знаке: благодаря ему выходит функциональное выражение, а не определение функции. На один байт меньше скобок, и за ними не надо следить.

Fulcrum (fulc.ru)
15 апреля 2012, 21:05

"для воспроизведения"

bolk (bolknote.ru)
15 апреля 2012, 21:29

Ошибки поправил, спасибо всем!

greli (greli.livejournal.com)
16 апреля 2012, 14:38

Есть некоторые непонятки:
* почему используется BeforeScript? Если скрипты есть только в начале страницы, не факт, что вообще будут найдены теги `VIDEO`.
* почему prototype._canPlayType определяется после того, как использован в анонимной функции? Не приведёт ли это к ошибке, когда найден тег при первом проходе?
* http://bolknote.ru/files/opera-xvid-patch.js — ошибка 404.

bolk (bolknote.ru)
16 апреля 2012, 15:46, ответ предназначен greli (greli.livejournal.com):

Я нечасто пишу UserJS.
почему используется BeforeScript? Если скрипты есть только в начале страницы, не факт, что вообще будут найдены теги `VIDEO`
Глянул в доку, я думал это выполяется перед каждым выполнением скрипта (например, из событий). Надо переделать…
почему prototype._canPlayType определяется после того, как использован в анонимной функции? Не приведёт ли это к ошибке, когда найден тег при первом проходе?
Нет, конечно. Потому что событие не дёрнется, пока основной JS не выполнится до конца. По сути _canPlayType определяется не до, а после. Сначала выполняется привязка события.
http://bolknote.ru/files/opera-xvid-patch.js — ошибка 404.
Это же неймспейс, а не УРЛ.

bolk (bolknote.ru)
18 апреля 2012, 11:08, ответ предназначен greli (greli.livejournal.com):

Я пока решил ничего не трогать, до реальных проблем. Всё-таки это не решение, а костыль в какой-то мере. Надеюсь, что авторы DivX web player обратят внимание на «Оперу» и всё будет хорошо.

Treg (инкогнито)
26 апреля 2012, 10:56, ответ предназначен bolk (bolknote.ru):

Нет, конечно. Потому что событие не дёрнется, пока основной JS не выполнится до конца. По сути _canPlayType определяется не до, а после. Сначала выполняется привязка события.
Определение _canPlayType сделано внутри события и не произойдет до его вызова. Соответственно при первом вызове будет ошибка. Необходимо "with (window.HTMLVideoElement) ..." вынести за пределы определения события "window.opera.addEventListener('BeforeScript', function() { ...".

bolk (bolknote.ru)
26 апреля 2012, 12:20, ответ предназначен Treg

Да, точно, надо просто поднять перед функцией. Видимо, правил код потом.

bolk (bolknote.ru)
26 апреля 2012, 12:21, ответ предназначен Treg

Поправил, спасибо!

Ingvar Reverser (http://rreverser.com/) (инкогнито)
20 декабря 2012, 01:03, ответ предназначен bolk (bolknote.ru):

Двумя годами ранее тоже правил ихний скрипт для использования под Оперой и заодно добавил поддержку для Windows Media компонента - http://habrahabr.ru/post/110794/ :)

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)