YouTube iframe API : HTML에 이미있는 iframe 플레이어를 어떻게 제어합니까?


149

iframe 기반 YouTube 플레이어를 제어하고 싶습니다. 이 플레이어는 이미 HTML을 사용하고 있지만 JavaScript API를 통해 제어하고 싶습니다.

API 를 사용하여 페이지에 새 비디오를 추가하고 YouTube 플레이어 기능으로 제어하는 ​​방법을 설명하는 iframe API 설명서를 읽었습니다 .

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

이 코드는 새로운 플레이어 객체를 만들어 'player'에 할당 한 다음 #container div 안에 삽입합니다. 그럼 내가 '플레이어'와 통화에서 작동 할 수 있습니다 playVideo(), pauseVideo()그 위에, 등.

그러나 이미 페이지에있는 iframe 플레이어에서 작업하고 싶습니다.

이전 임베드 방법으로 다음과 같이 매우 쉽게 수행 할 수 있습니다.

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

그러나 이것은 새로운 iframe에서는 작동하지 않습니다. 이미 페이지에 iframe 객체를 할당 한 다음 API 함수를 사용하려면 어떻게해야합니까?


YouTube IFrame API 작업에 대한 추상화를 작성했습니다. github.com/gajus/playtube
Gajus

답변:


316

바이올린 링크 : 소스 코드 - 미리보기 - 작은 버전
업데이트 :이 작은 기능은 한 방향으로 만 코드를 실행합니다. 전체 지원 (예 : 이벤트 리스너 / 게터)을 원하면 jQuery에서 Youtube 이벤트 수신을 살펴보십시오 .

심층 코드 분석의 결과, 나는 function callPlayer모든 프레임 YouTube 비디오에서 함수 호출을 요청하는 함수를 만들었습니다 . 가능한 함수 호출의 전체 목록을 얻으려면 YouTube Api 참조참조 하십시오 . 설명을 보려면 소스 코드의 주석을 읽으십시오.

2012 년 5 월 17 일에 플레이어의 준비 상태를 처리하기 위해 코드 크기가 두 배가되었습니다. 플레이어의 준비 상태를 처리하지 않는 컴팩트 기능이 필요한 경우 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");

가능한 질문 (& 답변) :

Q : 작동하지 않습니다!
A : "작동하지 않습니다"는 명확한 설명이 아닙니다. 오류 메시지가 나타 납니까? 관련 코드를 보여주세요.

Q : playVideo비디오를 재생하지 않습니다.
A : 재생에는 사용자 상호 작용과 allow="autoplay"iframe 의 존재가 필요합니다. 참조 https://developers.google.com/web/updates/2017/09/autoplay-policy-changeshttps://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide

Q : YouTube 동영상을 사용하여 삽입 <iframe src="http://www.youtube.com/embed/As2rZGPGKDY" />했지만 기능이 실행되지 않습니다!
A : ?enablejsapi=1URL 끝에 추가해야합니다 : /embed/vid_id?enablejsapi=1.

Q : "잘못된 또는 잘못된 문자열이 지정되었습니다"라는 오류 메시지가 나타납니다. 왜?
A : 로컬 호스트 ( file://) 에서 API가 제대로 작동하지 않습니다 . (테스트) 페이지를 온라인으로 호스팅 하거나 JSFiddle을 사용 하십시오 . 예 :이 답변 맨 위에있는 링크를 참조하십시오.

Q : 어떻게 알았습니까?
A : API 소스를 수동으로 해석하는 데 시간을 보냈습니다. 나는 그 postMessage방법 을 사용해야한다는 결론을 내렸다 . 전달할 인수를 알기 위해 메시지를 가로채는 Chrome 확장 프로그램을 만들었습니다. 확장 프로그램의 소스 코드는 여기에서 다운로드 할 수 있습니다 .

Q : 어떤 브라우저가 지원됩니까?
A : JSON 및 을 지원하는 모든 브라우저 postMessage.

  • IE 8 이상
  • Firefox 3.6 이상 (실제로 3.5이지만 document.readyState3.6에서 구현 됨)
  • 오페라 10.50+
  • 사파리 4+
  • 크롬 3 이상

관련 답변 / 구현 : jQuery
Full API 지원을 사용하여 프레임 비디오 페이드 인 : jQuery
공식 API 에서 유튜브 이벤트 수신 : https://developers.google.com/youtube/iframe_api_reference

개정 이력

  • 2012 년 5 월 17 일
    구현 onYouTubePlayerReady: callPlayer('frame_id', function() { ... }).
    플레이어가 아직 준비되지 않은 경우 기능이 자동으로 대기합니다.
  • 2012 년 7 월 24 일
    지원되는 브라우저에서 업데이트되고 성공적으로 테스트되었습니다 (앞으로).
  • 2013 년 10 월 10 일 함수가 인수로 전달되면 callPlayer준비 상태 점검을 강제 실행합니다. callPlayer문서가 준비된 상태에서 iframe을 삽입 한 직후에을 호출하면 iframe이 완전히 준비되었는지 확인할 수 없기 때문에이 작업이 필요합니다 . Internet Explorer 및 Firefox에서이 시나리오로 인해 너무 이른 호출이 발생하여 postMessage무시되었습니다.
  • 2013 년 12 월 12 일, &origin=*URL 에 추가 하는 것이 좋습니다 .
  • 2014 년 3 월 2 일 &origin=*, URL에 대한 제거 권고가 철회 되었습니다.
  • 2019 년 4 월 9 일, 페이지가 준비되기 전에 YouTube가로드 될 때 무한 재귀가 발생하는 버그를 수정했습니다. 자동 재생에 대한 메모를 추가하십시오.

@RobW 실제로 시도했습니다. JSON 오류는 스크립트의 것이 아니라 youtube API의 일부로 iframe 내부에있는 것 같습니다.
Fresheyeball

이 멋진 스 니펫에 대한 @RobW 감사합니다. 이벤트 리스너를 추가하기 위해 youtube JS API를 사용하는 대신 메시지 이벤트를 사용하는 방법을 찾았습니까?
brillout

@ brillout.com이 PostMessage방법은 YT JS API ( ?enablejsapi=1)를 기반으로합니다 . JS API를 사용하지 않으면 postMessage메소드가 아무 작업도 수행하지 않습니다 . 이벤트 리스너를 쉽게 구현 하려면 링크 된 답변 을 참조하십시오 . 또한 프레임과 통신하기 위해 읽을 수있는 코드를 만들었지 만 게시하지는 않았습니다. 그 효과는 기본 YouTube 프레임 API와 비슷하기 때문에 게시하지 않기로 결정했습니다.
Rob W

1
@MatthewBaker 메시지 이벤트를 듣고 결과 상태를 구문 분석해야합니다. 이처럼 간단한 호출만큼 쉽지는 않으므로 playVideo공식 API를 사용하는 것이 좋습니다. developers.google.com/youtube/iframe_api_reference#Events를 참조하십시오 .
Rob W

1
@ffyeahh 명백한 오류가 표시되지 않습니다. 이 답변에 의견에 질문을 추가하는 대신 자체 포함 된 재현 단계를 사용하여 새로운 질문을하십시오.
Rob W

33

YouTube에서 JS API를 업데이트하여 기본적으로 사용할 수있는 것 같습니다. 기존 YouTube iframe의 ID를 사용할 수 있습니다 ...

<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을 새 iframe으로 바꾸는 대신 기존 iframe을 사용합니다. 또한 생성자에 videoId를 지정할 필요가 없습니다.

비디오 플레이어로드를 참조하십시오.


1
@raven의 URL에 autoplay = 1 매개 변수가 없습니다. 귀하의 URL 예에서 youtube.com/embed/M7lc1UVf-VE?enablejsapi=1& autoplay = 1 & origin = example.com
alengel

@alengel 그는 autoplay-url 매개 변수를 사용하고 싶지 않습니다. 대신 js-API 자동 재생 기능을 사용하여 비디오를 시작하려고합니다. 그러나 어떤 이유로 onYouTubeIframeAPIReady () 함수가 호출되지 않습니다.
Humppakäräjät

@raven 나는 그것을 알아 냈습니다. 1) iframe URL에서 & origin = example.com을 제거합니다. 2) jsfiddle의 "Frameworks & Extensions"섹션에서 두 번째 드롭 다운 메뉴를 "No wrap in-<head>"로 설정하십시오. 3) youtube iframe api를 외부 리소스 ( youtube.com/iframe_api ) 로 추가하십시오 . 나는 당신의 바이올린을 포크하고 다음 변경 사항을 적용했습니다 : jsfiddle.net/e97famd1/1
Humppakäräjät

어떤 생각 event또는 command중지 YT은 iframe에 보낼 수있는 listening상태로?
mkhatib 2019

@CletusW :이 오류가 발생합니다 : 약속 없음) DOMException : play () 요청이 pause () 호출로 중단되었습니다. 약속 (비동기) (익명) @ scripts.js : 20 디스패치 @ jquery-1.12.4.js : 5226 elemData.handle @ jquery-1.12.4.js : 4878 cast_sender.js : 67 포착되지 않은 DOMException : 'PresentationRequest를 생성하지 못했습니다. ': 안전하지 않은 문서 [cast : 233637DE? capabilities = video_out % 2Caudio_out & clientId = 153262711713390989 & autoJoinPolicy = tab_and_origin_scoped & defaultActionPolicy = cast_this_tab & launchTimeout = 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/


나는이 방법을 정말로 좋아했고 Angular 지시문과 함께 작동하도록 조정 했으므로 모든 루프가 필요하지 않고 범위가있는 토글 기능에 따라 func를 전달했습니다 (기본적으로 비디오가 표시되는 경우-> 자동 재생; 그렇지 않으면-> 일시 중지 비디오). 감사!
DD.

지시 사항을 공유해야 유용 할 수 있습니다!
Kim T

1
다음은 펜 코드에 대한 적응입니다. codepen.io/anon/pen/qERdza 도움이 되길 바랍니다. 그것은 또한 당신의 비디오가있을 때 ESC 키를 눌러 전환
DD합니다.

사실이 너무 좋은 것 같습니다! 이 방법을 사용하는 데 브라우저 / 보안 제한이 있습니까?
Dan

예 IE에는 몇 가지 제한 사항, 특히 postMessage 대신 MessageChannel을 지원하는 IE10이 있습니다 . caniuse.com/#search=postMessage 는 또한 모든 콘텐츠 보안 정책이이 기능의 사용을 제한 할 것임을 알고 있습니다.
Kim T

5

위의 내 자신의 Kim T 코드는 일부 jQuery와 결합되어 특정 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 || []
        } ), '*');
    }
}

YouTube가 재생 중인지 확인하는 방법 YouTube iframe의 콜백이 있으니 외부에서 구독 할 수 있습니까?
Hammer

@Hammer YouTube API 이벤트 섹션에서 구체적으로 OnStateChange: developers.google.com/youtube/iframe_api_reference#Events
adamj

@admj, 이것을 확인할 수 있습니까? 일부 이상한 행동 ... stackoverflow.com/questions/38389802/…
Hammer

0

귀하의 답변에 대해 Rob W에게 감사합니다.

API를로드하지 않아도되고 동적으로로드되는 iframe을 쉽게 제어 할 수 있도록 Cordova 애플리케이션에서 이것을 사용했습니다.

나는 항상 상태 (getPlayerState) 및 시간 (getCurrentTime)과 같은 iframe에서 정보를 추출 할 수있는 기능을 원했습니다.

Rob W는 postMessage를 사용하여 API의 작동 방식을 강조하는 데 도움이되었지만 물론 웹 페이지에서 iframe으로 한 방향으로 만 정보를 보냅니다. 게터에 접근하려면 iframe에서 게시 한 메시지를 들어야합니다.

Rob W의 답변을 조정하여 iframe에서 반환 한 메시지를 활성화하고 듣는 방법을 알아내는 데 시간이 걸렸습니다. 메시지를주고받는 코드를 찾을 때까지 기본적으로 YouTube iframe에서 소스 코드를 검색했습니다.

핵심은 '이벤트'를 '듣기'로 변경하는 것이 었습니다. 이것은 기본적으로 값을 반환하도록 설계된 모든 메소드에 대한 액세스를 제공했습니다.

아래는 내 해결책입니다. 게터를 요청할 때만 '듣기'로 전환했으며 추가 방법을 포함하도록 조건을 조정할 수 있습니다.

console.log (e)를 window.onmessage에 추가하여 iframe에서 전송 된 모든 메시지를 볼 수 있습니다. 청취가 활성화되면 비디오의 현재 시간이 포함 된 지속적인 업데이트를 받게됩니다. getPlayerState와 같은 getter를 호출하면 이러한 지속적인 업데이트가 활성화되지만 상태가 변경되었을 때 비디오 상태와 관련된 메시지 만 보냅니다.

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);
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.