YouTube iframe API: jak Mogę kontrolować odtwarzacz iframe, który jest już w HTML?

Chcę być w stanie kontrolować Odtwarzacze YouTube oparte na iframe. Te odtwarzacze będą już w HTML, ale chcę je kontrolować za pomocą JavaScript API.

Czytałem dokumentację dla iframe API , która wyjaśnia, jak dodać nowy film do strony za pomocą API, a następnie kontrolować go za pomocą funkcji odtwarzacza YouTube:

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

Ten kod tworzy nowy obiekt player i przypisuje go do 'player', a następnie wstawia go do # container div. Wtedy mogę operować na "odtwarzaczu" i dzwonić playVideo(), pauseVideo(), itd. robi się.

Ale chcę móc operować na odtwarzaczach iframe, które są już na stronie.

Mogę to zrobić bardzo łatwo za pomocą starej metody embed, z czymś takim jak:

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

Ale to nie działa z nowymi ramkami iFrame. Jak przypisać obiekt iframe już na stronie, a następnie korzystać z funkcji API na nim?

Author: Silvia, 2011-09-16

5 answers

linki: kod źródłowy - podgląd - Wersja mała
Aktualizacja: ta mała funkcja będzie wykonywać kod tylko w jednym kierunku. Jeśli chcesz uzyskać pełne wsparcie (np. event listeners / getters), zajrzyj na Listening for Youtube Event w jQuery

W wyniku głębokiej analizy kodu, stworzyłem funkcję: function callPlayer żąda wywołania funkcji na dowolnym oprawionym filmie YouTube. Zobacz YouTube Api reference to get a full list of possible function calls. Przeczytaj komentarze w kodzie źródłowym, aby uzyskać wyjaśnienie.

W dniu 17 maja 2012, Rozmiar kodu został podwojony w celu dbania o stan gotowości gracza. Jeśli potrzebujesz zwartej funkcji, która nie zajmuje się gotowym stanem gracza, Zobacz http://jsfiddle.net/8R5y6/.

/**
 * @author       Rob W <[email protected]>
 * @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);
    }
}

Użycie:

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");

Możliwe pytania (i odpowiedzi):

Q: to nie działa!
A : "Nie działa" nie jest jednoznacznym opisem. Czy dostajesz jakieś komunikaty o błędach? Proszę pokazać odpowiedni kod.

Q: playVideo nie odtwarza filmu.
A: odtwarzanie wymaga interakcji użytkownika i obecności allow="autoplay" na ramce iframe. Zobacz https://developers.google.com/web/updates/2017/09/autoplay-policy-changes i https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide

Q : umieściłem film na YouTube za pomocą <iframe src="http://www.youtube.com/embed/As2rZGPGKDY" />ale funkcja nie wykonuje żadnej funkcji!
A : musisz dodać ?enablejsapi=1 na końcu adresu URL: /embed/vid_id?enablejsapi=1.

Q: wyświetla się komunikat o błędzie "podano nieprawidłowy lub nielegalny ciąg znaków". Dlaczego?
A : API nie działa poprawnie na lokalnym hoście (file://). Hostuj swoją (testową) stronę online lub użyj JSFiddle. Przykłady: zobacz linki na górze tej odpowiedzi.

P: skąd to wiesz?
A : spędziłem trochę czasu na ręczną interpretację źródła API. Doszedłem do wniosku, że muszę użyć postMessage metoda. Aby wiedzieć, które argumenty przekazać, stworzyłem rozszerzenie Chrome, które przechwytuje wiadomości. Kod źródłowy rozszerzenia można pobrać tutaj .

Q: jakie przeglądarki są obsługiwane?
A : każda przeglądarka obsługująca JSON i postMessage.

  • IE 8 +
  • Firefox 3.6+ (właściwie 3.5, ale document.readyState został zaimplementowany w 3.6)
  • Opera 10.50 +
  • Safari 4 +
  • Chrome 3 +

Podobne odpowiedzi / implementacja: Fade-w ramce wideo przy użyciu jQuery
Pełna obsługa API: nasłuchiwanie zdarzenia Youtube w jQuery
Oficjalne API: https://developers.google.com/youtube/iframe_api_reference

Historia zmian

  • 17 maja 2012
    Zrealizowane onYouTubePlayerReady: callPlayer('frame_id', function() { ... }).
    Funkcje to automatycznie w kolejce, gdy gracz nie jest jeszcze gotowy.
  • 24 lipca 2012
    Zaktualizowane i pomyślnie przetestowane w obsługiwanych przeglądarkach (patrz w przyszłość).
  • 10 października 2013 Gdy funkcja jest przekazywana jako argument, callPlayer wymusza sprawdzenie gotowości. Jest to konieczne, ponieważ gdy callPlayer jest wywoływane zaraz po wstawieniu ramki iframe, gdy dokument jest gotowy, nie może być pewien, czy ramka iframe jest w pełni gotowa. W przeglądarkach Internet Explorer i Firefox scenariusz ten zaowocował zbyt wczesne wywołanie postMessage, które zostało zignorowane.
  • 12 gru 2013, zaleca się dodać &origin=* W URL.
  • 2 Mar 2014, wycofano zalecenie usunięcia &origin=* do adresu URL.
  • 9 kwietnia 2019, Naprawiono błąd, który powodował nieskończoną rekurencję, gdy YouTube ładuje się, zanim strona była gotowa. Dodaj notatkę o autoodtwarzaniu.
 319
Author: Rob W,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-09 13:55:41

Wygląda na to, że YouTube zaktualizował swoje API JS, więc jest to domyślnie dostępne! Możesz użyć istniejącego identyfikatora YouTube iframe...

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

...w Twoim JS...

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

function onPlayerStateChange() {
  //...
}

...konstruktor użyje istniejącego iframe zamiast zastąpić go nowym. Oznacza to również, że nie musisz określać konstruktora videoId.

Zobacz Ładowanie odtwarzacza wideo

 34
Author: CletusW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-29 21:38:59

Możesz to zrobić za pomocą znacznie mniej kodu:

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 || []
            }), '*');
        }
    }
}

Przykład pracy: http://jsfiddle.net/kmturley/g6P5H/296/

 20
Author: Kim T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-24 00:39:09

Moja własna wersja kodu Kima t powyżej, która łączy się z jQuery i pozwala na targetowanie konkretnych ramek iFrame.

$(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 || []
        } ), '*');
    }
}
 5
Author: adamj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-24 13:00:27

Dziękuję Rob W za odpowiedź.

Używam tego w aplikacji Cordova, aby uniknąć konieczności ładowania API i tak, że mogę łatwo kontrolować iframes, które są ładowane dynamicznie.

Zawsze chciałem mieć możliwość wydobywania informacji z ramki iframe, takich jak stan (getPlayerState) i czas (getCurrentTime).

Rob w pomógł podkreślić, jak działa API przy użyciu postMessage, ale oczywiście wysyła to tylko informacje w jednym kierunku, z naszej strony w iframe. Dostęp do getterów wymaga od nas nasłuchiwania wiadomości wysłanych do nas z iframe.

Zajęło mi trochę czasu, aby dowiedzieć się, jak dostosować odpowiedź Roba W, aby aktywować i słuchać wiadomości zwracanych przez iframe. W zasadzie przeszukałem kod źródłowy w YouTube iframe, aż znalazłem kod odpowiedzialny za wysyłanie i odbieranie wiadomości.

Kluczem była zmiana "event" na "listening", to w zasadzie dało dostęp do wszystkich metody, które zostały zaprojektowane do zwracania wartości.

Poniżej znajduje się moje rozwiązanie, proszę zauważyć, że przełączyłem się na "nasłuchiwanie" tylko wtedy, gdy wymagane są gettery, możesz dostosować warunek, aby zawierał dodatkowe metody.

Zauważ ponadto, że możesz wyświetlić wszystkie wiadomości wysłane z ramki iframe, dodając konsolę.log (e) do okna.onmessage. Zauważysz, że po aktywacji odsłuchu będziesz otrzymywać stałe aktualizacje, które obejmują bieżący czas nagrania. Wywoływanie getterów, takich jak getplayertate uaktywni te stałe aktualizacje, ale wyśle wiadomość dotyczącą stanu wideo tylko wtedy, gdy stan się zmienił.

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);
    }
}
 0
Author: Danbardo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-04 11:57:48