API iframe YouTube: як я можу контролювати програвач iframe, який вже є в HTML?


149

Я хочу мати можливість контролювати плеєри YouTube на основі iframe. Цей плеєр вже буде в HTML, але я хочу контролювати їх через JavaScript API.

Я читав документацію для API iframe, в якій пояснюється, як додати нове відео на сторінку за допомогою API, а потім контролювати його за допомогою функцій програвача YouTube:

var player;
function onYouTubePlayerAPIReady() {
    player = new YT.Player('container', {
        height: '390',
        width: '640',
        videoId: 'u1zgFlCw8Aw',
        events: {
            'onReady': onPlayerReady,
            'onStateChange': onPlayerStateChange
        }
    });
}

Цей код створює новий об’єкт програвача і призначає його "гравцеві", а потім вставляє його всередину #container div. Тоді я можу працювати на «гравець» і виклик playVideo(), pauseVideo()і т.д. на ньому.

Але я хочу мати можливість працювати з програвачами iframe, які вже є на сторінці.

Я міг би зробити це дуже легко за допомогою старого методу вставки, із чимось на зразок:

player = getElementById('whateverID');
player.playVideo();

Але це не працює з новими рамками кадрів. Як я можу призначити об'єкт iframe вже на сторінці, а потім використовувати функції API на ньому?


Я написав абстракцію для роботи з API IFrame YouTube github.com/gajus/playtube
Gajus

Відповіді:


316

Посилання Fiddle: Вихідний код - Попередній перегляд -
Оновлення невеликої версії : Ця невелика функція виконуватиме код лише в одному напрямку. Якщо ви хочете повну підтримку (наприкладслухачі подій / геттеров), мають вигляд на аудіювання для Youtube події в JQuery

В результаті глибокого аналізу коду я створив функцію: function callPlayerзапитує виклик функції на будь-яке обрамлене відео YouTube. Перегляньте посилання на YouTube Api, щоб отримати повний перелік можливих функціональних дзвінків. Прочитайте коментарі у вихідному коді для пояснення.

17 травня 2012 року розмір коду був подвоєний, щоб подбати про готовність гравця. Якщо вам потрібна компактна функція, яка не відповідає готовності гравця, див. Http://jsfiddle.net/8R5y6/ .

/**
 * @author       Rob W <gwnRob@gmail.com>
 * @website      https://stackoverflow.com/a/7513356/938089
 * @version      20190409
 * @description  Executes function on a framed YouTube video (see website link)
 *               For a full list of possible functions, see:
 *               https://developers.google.com/youtube/js_api_reference
 * @param String frame_id The id of (the div containing) the frame
 * @param String func     Desired function to call, eg. "playVideo"
 *        (Function)      Function to call when the player is ready.
 * @param Array  args     (optional) List of arguments to pass to function func*/
function callPlayer(frame_id, func, args) {
    if (window.jQuery && frame_id instanceof jQuery) frame_id = frame_id.get(0).id;
    var iframe = document.getElementById(frame_id);
    if (iframe && iframe.tagName.toUpperCase() != 'IFRAME') {
        iframe = iframe.getElementsByTagName('iframe')[0];
    }

    // When the player is not ready yet, add the event to a queue
    // Each frame_id is associated with an own queue.
    // Each queue has three possible states:
    //  undefined = uninitialised / array = queue / .ready=true = ready
    if (!callPlayer.queue) callPlayer.queue = {};
    var queue = callPlayer.queue[frame_id],
        domReady = document.readyState == 'complete';

    if (domReady && !iframe) {
        // DOM is ready and iframe does not exist. Log a message
        window.console && console.log('callPlayer: Frame not found; id=' + frame_id);
        if (queue) clearInterval(queue.poller);
    } else if (func === 'listening') {
        // Sending the "listener" message to the frame, to request status updates
        if (iframe && iframe.contentWindow) {
            func = '{"event":"listening","id":' + JSON.stringify(''+frame_id) + '}';
            iframe.contentWindow.postMessage(func, '*');
        }
    } else if ((!queue || !queue.ready) && (
               !domReady ||
               iframe && !iframe.contentWindow ||
               typeof func === 'function')) {
        if (!queue) queue = callPlayer.queue[frame_id] = [];
        queue.push([func, args]);
        if (!('poller' in queue)) {
            // keep polling until the document and frame is ready
            queue.poller = setInterval(function() {
                callPlayer(frame_id, 'listening');
            }, 250);
            // Add a global "message" event listener, to catch status updates:
            messageEvent(1, function runOnceReady(e) {
                if (!iframe) {
                    iframe = document.getElementById(frame_id);
                    if (!iframe) return;
                    if (iframe.tagName.toUpperCase() != 'IFRAME') {
                        iframe = iframe.getElementsByTagName('iframe')[0];
                        if (!iframe) return;
                    }
                }
                if (e.source === iframe.contentWindow) {
                    // Assume that the player is ready if we receive a
                    // message from the iframe
                    clearInterval(queue.poller);
                    queue.ready = true;
                    messageEvent(0, runOnceReady);
                    // .. and release the queue:
                    while (tmp = queue.shift()) {
                        callPlayer(frame_id, tmp[0], tmp[1]);
                    }
                }
            }, false);
        }
    } else if (iframe && iframe.contentWindow) {
        // When a function is supplied, just call it (like "onYouTubePlayerReady")
        if (func.call) return func();
        // Frame exists, send message
        iframe.contentWindow.postMessage(JSON.stringify({
            "event": "command",
            "func": func,
            "args": args || [],
            "id": frame_id
        }), "*");
    }
    /* IE8 does not support addEventListener... */
    function messageEvent(add, listener) {
        var w3 = add ? window.addEventListener : window.removeEventListener;
        w3 ?
            w3('message', listener, !1)
        :
            (add ? window.attachEvent : window.detachEvent)('onmessage', listener);
    }
}

Використання:

callPlayer("whateverID", function() {
    // This function runs once the player is ready ("onYouTubePlayerReady")
    callPlayer("whateverID", "playVideo");
});
// When the player is not ready yet, the function will be queued.
// When the iframe cannot be found, a message is logged in the console.
callPlayer("whateverID", "playVideo");

Можливі запитання (& відповіді):

З : Це не працює!
Відповідь : "Не працює" - це не чіткий опис. Чи отримуєте ви повідомлення про помилки? Будь ласка, покажіть відповідний код.

З : playVideoне відтворює відео.
A : Для відтворення потрібна взаємодія з користувачем та наявність allow="autoplay"у кадрі iframe. Дивіться https://developers.google.com/web/updates/2017/09/autoplay-policy-changes та https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide

З : Я вбудував відео YouTube за допомогою, <iframe src="http://www.youtube.com/embed/As2rZGPGKDY" />але ця функція не виконує жодної функції! : Ви повинні додати в кінці вашого URL: .
?enablejsapi=1/embed/vid_id?enablejsapi=1

Питання : Я отримую повідомлення про помилку "Неправильний або незаконний рядок вказано". Чому?
A : API не працює належним чином на локальному хості ( file://). Розмістіть свою (тестову) сторінку в Інтернеті або скористайтеся JSFiddle . Приклади: Дивіться посилання вгорі цієї відповіді.

З : Як ти це знав?
Відповідь : Я витратив деякий час, щоб вручну інтерпретувати джерело API. Я зробив висновок, що мені довелося скористатися postMessageметодом. Щоб знати, які аргументи передавати, я створив розширення Chrome, яке перехоплює повідомлення. Вихідний код розширення можна завантажити тут .

З : Які браузери підтримуються?
A : кожен браузер, який підтримує JSON і postMessage.

  • IE 8+
  • Firefox 3.6+ (фактично 3.5, але document.readyStateвпроваджено в 3.6)
  • Опера 10.50+
  • Сафарі 4+
  • Chrome 3+

Відповідна відповідь / реалізація: Fade-in framed video with using jQuery
Повна підтримка API: Прослуховування події Youtube в
офіційному API jQuery : https://developers.google.com/youtube/iframe_api_reference

Історія перегляду

  • 17 травня 2012
    Реалізовано onYouTubePlayerReady: callPlayer('frame_id', function() { ... }).
    Функції автоматично ставляться в чергу, коли гравець ще не готовий.
  • 24 липня 2012 р.
    Оновлено та успішно тестується у підтримуваних браузерах (дивіться вперед).
  • 10 жовтня 2013 р. Коли функція передається як аргумент, callPlayerпримушує перевірити готовність. Це потрібно, тому що, коли callPlayerвикликається відразу після вставки iframe, поки документ готовий, він не може точно знати, що iframe повністю готовий. У Internet Explorer та Firefox цей сценарій призвів до занадто раннього виклику postMessage, яке було проігноровано.
  • 12 грудня 2013 року, рекомендується додати &origin=*в URL.
  • 2 березня 2014 р. Відкликано рекомендацію щодо видалення &origin=*до URL-адреси.
  • 9 квітня 2019 року виправте помилку, яка спричинила нескінченну рекурсію, коли YouTube завантажиться до того, як сторінка буде готова. Додати примітку про автоматичне відтворення.

@RobW Насправді я спробував це. Схоже, помилка JSON - це не те, що у вашому сценарії, а всередині iframe як частина API youtube.
Fresheyeball

@RobW дякую за цей приємний фрагмент Чи знайшли ви будь-які способи використовувати повідомлення «Подія повідомлення» замість API JS YouTube для того, щоб додати слухача події?
бриль

@ brillout.com PostMessageМетод заснований на YT JS API ( ?enablejsapi=1). Без включення API JS postMessageметод нічого не зробить. Див. Пов’язану відповідь для легкої реалізації слухачів подій. Я також створив, але не опублікований, читабельний код для спілкування з фреймом. Я вирішив не публікувати його, оскільки його ефект подібний до типового API Frame Frame.
Роб Ш

1
@MatthewBaker Для цього потрібно прослухати подію повідомлення та проаналізувати статус результату. Це не так просто, як прості дзвінки playVideo, тому я рекомендую використовувати офіційний API для цього. Див. Розробники.google.com/ youtube/iframe_api_reference# Events .
Роб Ш

1
@ffyeahh Я не бачу явної помилки. Будь ласка, задайте нове запитання із самостійним кроком для відтворення замість додавання запитань у коментарях до цієї відповіді.
Rob W

33

Схоже, YouTube оновив свій JS API, тому він доступний за замовчуванням! Ви можете використовувати наявний ідентифікатор iframe YouTube ...

<iframe id="player" src="http://www.youtube.com/embed/M7lc1UVf-VE?enablejsapi=1&origin=http://example.com" frameborder="0"></iframe>

... у вашому JS ...

var player;
function onYouTubeIframeAPIReady() {
  player = new YT.Player('player', {
    events: {
      'onStateChange': onPlayerStateChange
    }
  });
}

function onPlayerStateChange() {
  //...
}

... і конструктор використовуватиме існуючий iframe замість того, щоб замінити його новим. Це також означає, що вам не доведеться вказувати конструктору videoId.

Див. Розділ Завантаження відеоплеєра


1
@raven у URL-адресі вам не вистачає параметра autoplay = 1. У вашому URL-
адресі

@alengel він не хоче використовувати параметр autoplay-url. Натомість він намагається запустити відео за допомогою функції автовідтворення js-API. Але чомусь функція onYouTubeIframeAPIReady () не викликається.
Humppakäräjät

@raven я зрозумів це. 1) видаліть & origin = example.com з URL-адреси iframe. 2) у розділі "Рамки та розширення" вашого jsfiddle встановіть друге випадаюче меню на "Без обгортання - <head>" 3) додайте ютубе iframe api як зовнішній ресурс ( youtube.com/iframe_api ); Я розпрощав вашу скрипку і застосував ці зміни: jsfiddle.net/e97famd1/1
Humppakäräjät

Будь-яка ідея, що eventабо commandнадіслати в рамку YT iframe, щоб зупинитись listeningна статусі?
mkhatib

@CletusW: Я отримую цю помилку: Uncaught (з обіцянкою) DOMException: запит play () був перерваний викликом до паузи (). Обіцяння (async) (анонімний) @ scriptpts.js: 20 відправка @ jquery-1.12.4.js: 5226 elemData.handle @ jquery-1.12.4.js: 4878 cast_sender.js: 67 Невизначений DOMException: Не вдалося побудувати 'PresentationRequest ': Презентація небезпечного документа [cast: 233637DE? Можливості = video_out% 2Caudio_out & clientId = 153262711713390989 & autoJoinPolicy = tab_and_origin_scoped & defaultActionPolicy = cast_this_tab & startTimeout = 30000] забороняється із безпечного контексту.
LauraNMS

20

Це можна зробити за допомогою набагато меншого коду:

function callPlayer(func, args) {
    var i = 0,
        iframes = document.getElementsByTagName('iframe'),
        src = '';
    for (i = 0; i < iframes.length; i += 1) {
        src = iframes[i].getAttribute('src');
        if (src && src.indexOf('youtube.com/embed') !== -1) {
            iframes[i].contentWindow.postMessage(JSON.stringify({
                'event': 'command',
                'func': func,
                'args': args || []
            }), '*');
        }
    }
}

Приклад роботи: http://jsfiddle.net/kmturley/g6P5H/296/


Мені дуже сподобався цей спосіб, просто адаптував його для роботи з кутовою директивою, тому не знадобився весь цикл і передав функцію залежно від функції перемикання з областю (в основному, якщо відео відображається -> автовідтворення; інше -> пауза відео). Дякую!
ДД.

Ви повинні поділитися директивою, може бути корисною!
Кім Т

1
Ось адаптація коду для ручки: codepen.io/anon/pen/qERdza Я сподіваюся, що це допоможе! Він також перемикає клавішу ESC, коли у вас відео ввімкнено
DD.

Це здається занадто гарним, щоб бути правдою! Чи існують обмеження щодо використання браузера / безпеки щодо використання цього методу?
Dan

Так IE має кілька обмежень, особливо IE10 , який підтримує MessageChannel замість PostMessage: caniuse.com/#search=postMessage також бути в курсі яка - або контент Політика безпеки також обмежити використання цієї функції
Кім T

5

Моя власна версія коду Кім T, яка поєднується з деякими jQuery і дозволяє орієнтуватися на конкретні рамки кадрів.

$(function() {
    callPlayer($('#iframe')[0], 'unMute');
});

function callPlayer(iframe, func, args) {
    if ( iframe.src.indexOf('youtube.com/embed') !== -1) {
        iframe.contentWindow.postMessage( JSON.stringify({
            'event': 'command',
            'func': func,
            'args': args || []
        } ), '*');
    }
}

Як знати, що на YouTube грає? будь-який зворотний дзвінок з iframe youtube, тому зовні можна підписатися?
Молот

@Hammer Перегляньте розділ подій API YouTube, зокрема OnStateChange: developers.google.com/youtube/iframe_api_reference#Events
adamj

@admj, Можеш це перевірити? Деякі поведінки ... stackoverflow.com/questions/38389802/…
Hammer

0

Дякую Роб W за вашу відповідь.

Я використовую це в додатку Cordova, щоб уникнути необхідності завантажувати API, і щоб я міг легко керувати iframe, які завантажуються динамічно.

Мені завжди хотілося можливість мати можливість отримувати інформацію з iframe, наприклад стан (getPlayerState) та час (getCurrentTime).

Rob W допоміг підкреслити, як працює API, використовуючи postMessage, але, звичайно, це надсилає інформацію лише в одному напрямку, з нашої веб-сторінки в iframe. Для отримання доступу до користувачів вимагаємо, щоб ми слухали повідомлення, опубліковані нам із iframe.

Мені знадобилося певний час, щоб зрозуміти, як налаштувати відповідь Роб W, щоб активувати та прослуховувати повідомлення, повернені iframe. Я в основному шукав вихідний код у iframe YouTube, поки не знайшов код, відповідальний за надсилання та отримання повідомлень.

Ключовим моментом було зміна «події» на «прослуховування», що в основному дало доступ до всіх методів, які були розроблені для повернення значень.

Нижче моє рішення, будь ласка, зверніть увагу, що я перейшов на «прослуховування» лише тоді, коли запитуються користувачі, ви можете змінити умову, щоб включити додаткові методи

Далі зауважте, що ви можете переглянути всі повідомлення, надіслані з iframe, додавши console.log (e) до window.onmessage. Ви помітите, що після активації прослуховування ви отримуватимете постійні оновлення, які включають поточний час відео. Виклик ораторів, таких як getPlayerState, активує ці постійні оновлення, але надсилатиме повідомлення про стан відео лише тоді, коли стан змінився.

function callPlayer(iframe, func, args) {
    iframe=document.getElementById(iframe);
    var event = "command";
    if(func.indexOf('get')>-1){
        event = "listening";
    }

    if ( iframe&&iframe.src.indexOf('youtube.com/embed') !== -1) {
      iframe.contentWindow.postMessage( JSON.stringify({
          'event': event,
          'func': func,
          'args': args || []
      }), '*');
    }
}
window.onmessage = function(e){
    var data = JSON.parse(e.data);
    data = data.info;
    if(data.currentTime){
        console.log("The current time is "+data.currentTime);
    }
    if(data.playerState){
        console.log("The player state is "+data.playerState);
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.