API iframe của YouTube: làm cách nào để kiểm soát trình phát iframe đã có trong HTML?


149

Tôi muốn có thể kiểm soát các trình phát YouTube dựa trên iframe. Trình phát này sẽ có trong HTML, nhưng tôi muốn kiểm soát chúng thông qua API JavaScript.

Tôi đã đọc tài liệu về API iframe giải thích cách thêm video mới vào trang bằng API và sau đó kiểm soát video đó bằng các chức năng của trình phát YouTube:

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

Mã đó tạo một đối tượng người chơi mới và gán nó cho 'người chơi', sau đó chèn nó vào div #container. Sau đó, tôi có thể hoạt động trên 'chơi' và cuộc gọi playVideo(), pauseVideo()vv trên đó.

Nhưng tôi muốn có thể hoạt động trên các trình phát iframe đã có trên trang.

Tôi có thể làm điều này rất dễ dàng với phương thức nhúng cũ, với một cái gì đó như:

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

Nhưng điều này không hoạt động với iframe mới. Làm cách nào tôi có thể chỉ định một đối tượng iframe đã có trên trang và sau đó sử dụng các hàm API trên đó?


Tôi đã viết một bản tóm tắt để làm việc với API IFrame của YouTube github.com/gajus/playTube
Gajus 22/2/2015

Câu trả lời:


316

Liên kết Fiddle: Mã nguồn - Xem trước -
Cập nhật phiên bản nhỏ : Hàm nhỏ này sẽ chỉ thực thi mã theo một hướng duy nhất. Nếu bạn muốn hỗ trợ đầy đủ (ví dụ người nghe sự kiện / thu khí), có một cái nhìn tại Nghe cho tổ chức sự kiện Youtube trong jQuery

Do phân tích mã sâu, tôi đã tạo một hàm: function callPlayeryêu cầu gọi hàm trên bất kỳ video YouTube đóng khung nào. Xem tài liệu tham khảo Api trên YouTube để có danh sách đầy đủ các cuộc gọi chức năng có thể. Đọc các bình luận tại mã nguồn để được giải thích.

Vào ngày 17 tháng 5 năm 2012, kích thước mã đã được nhân đôi để chăm sóc trạng thái sẵn sàng của người chơi. Nếu bạn cần một chức năng nhỏ gọn không liên quan đến trạng thái sẵn sàng của người chơi, hãy xem 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);
    }
}

Sử dụng:

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

Câu hỏi có thể (& câu trả lời):

Q : Nó không hoạt động!
A : "Không hoạt động" không phải là một mô tả rõ ràng. Bạn có nhận được bất kỳ thông báo lỗi? Vui lòng hiển thị mã có liên quan.

Q : playVideokhông phát video.
Trả lời : Phát lại yêu cầu tương tác của người dùng và sự hiện diện của allow="autoplay"iframe. Xem https://developers.google.com/web/updates/2017/09/autoplay-policy-changeshttps://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide

H : Tôi đã nhúng video YouTube bằng cách sử dụng <iframe src="http://www.youtube.com/embed/As2rZGPGKDY" />nhưng chức năng không thực hiện bất kỳ chức năng nào!
Trả lời : Bạn phải thêm ?enablejsapi=1vào cuối URL của mình : /embed/vid_id?enablejsapi=1.

H : Tôi nhận được thông báo lỗi "Chuỗi không hợp lệ hoặc không hợp lệ đã được chỉ định". Tại sao?
Trả lời : API không hoạt động chính xác tại máy chủ cục bộ ( file://). Lưu trữ trang (kiểm tra) của bạn trực tuyến hoặc sử dụng JSFiddle . Ví dụ: Xem các liên kết ở đầu câu trả lời này.

Q : Làm thế nào bạn biết điều này?
Trả lời : Tôi đã dành một chút thời gian để diễn giải thủ công nguồn của API. Tôi kết luận rằng tôi phải sử dụng postMessagephương pháp. Để biết đối số nào sẽ vượt qua, tôi đã tạo tiện ích mở rộng Chrome chặn tin nhắn. Mã nguồn cho phần mở rộng có thể được tải xuống ở đây .

Q : Những trình duyệt nào được hỗ trợ?
Trả lời : Mọi trình duyệt hỗ trợ JSONpostMessage.

  • IE 8+
  • Firefox 3.6+ (thực tế là 3.5, nhưng document.readyStateđã được triển khai trong 3.6)
  • Opera 10.50+
  • Safari 4+
  • Chrome 3+

Câu trả lời / triển khai có liên quan: Làm mờ video trong khung bằng cách sử dụng
hỗ trợ API đầy đủ của jQuery : Lắng nghe sự kiện Youtube trong
API chính thức của jQuery : https://developers.google.com/youtube/iframe_api_Vference

Lịch sử sửa đổi

  • 17 tháng 5 năm 2012
    Thực hiện onYouTubePlayerReady: callPlayer('frame_id', function() { ... }).
    Các chức năng được tự động xếp hàng khi người chơi chưa sẵn sàng.
  • 24 tháng 7 năm 2012
    Cập nhật và thử nghiệm thành công trong các trình duyệt được hỗ trợ (nhìn về phía trước).
  • 10 tháng 10 năm 2013 Khi một chức năng được thông qua như là một đối số, callPlayerbuộc phải kiểm tra sự sẵn sàng. Điều này là cần thiết, bởi vì khi callPlayerđược gọi ngay sau khi chèn iframe trong khi tài liệu đã sẵn sàng, không thể biết chắc rằng iframe đã hoàn toàn sẵn sàng. Trong Internet Explorer và Firefox, kịch bản này dẫn đến việc gọi quá sớm postMessage, đã bị bỏ qua.
  • Ngày 12 tháng 12 năm 2013, nên thêm &origin=*vào URL.
  • Ngày 2 tháng 3 năm 2014, rút ​​lại đề xuất để xóa &origin=*URL.
  • 9 tháng 4 năm 2019, sửa lỗi dẫn đến đệ quy vô hạn khi YouTube tải trước khi trang sẵn sàng. Thêm ghi chú về tự động phát.

@RobW Tôi đã thử nó thực sự. Có vẻ như JSON bị lỗi không phải là lỗi trong tập lệnh của bạn, mà bên trong iframe là một phần của API của youtube.
Fresheyeball

@RobW cảm ơn vì đoạn trích hay này. Bạn đã tìm thấy bất kỳ cách nào để sử dụng Sự kiện Tin nhắn thay vì sử dụng API API của youtube để thêm trình lắng nghe sự kiện chưa?
sáng

@ brillout.com PostMessagePhương thức này dựa trên API YT JS ( ?enablejsapi=1). Nếu không bật API API, postMessagephương thức sẽ không làm gì cả, Xem câu trả lời được liên kết để dễ dàng thực hiện các trình lắng nghe sự kiện. Tôi cũng đã tạo, nhưng không được công bố, mã có thể đọc được để giao tiếp với khung. Tôi đã quyết định không xuất bản nó, vì hiệu ứng của nó tương tự như API khung YouTube mặc định.
Rob W

1
@MatthewBaker Điều đó đòi hỏi phải nghe sự kiện tin nhắn và phân tích trạng thái của kết quả. Điều này không dễ dàng như các cuộc gọi đơn giản như playVideovậy, vì vậy tôi khuyên bạn nên sử dụng API chính thức cho điều đó. Xem nhà phát triển.google.com/youtube/iframe_api_Vference# Events .
Rob W

1
@ffyeahh Tôi không thấy bất kỳ lỗi rõ ràng. Vui lòng đặt câu hỏi mới với các bước tự tạo để thay thế, thay vì thêm câu hỏi trong nhận xét cho câu trả lời này.
Rob W

33

Có vẻ như YouTube đã cập nhật API JS của họ để nó có sẵn theo mặc định! Bạn có thể sử dụng ID iframe YouTube hiện có ...

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

... trong JS của bạn ...

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

function onPlayerStateChange() {
  //...
}

... và hàm tạo sẽ sử dụng iframe hiện tại của bạn thay vì thay thế nó bằng một khung mới. Điều này cũng có nghĩa là bạn không phải chỉ định videoId cho hàm tạo.

Xem Đang tải trình phát video


1
@raven bạn đang thiếu tham số autoplay = 1 trong url. Trong url ví dụ của bạn, nó sẽ là youtube.com/embed/M7lc1UVf-VE?enablejsapi=1& autoplay = 1 & origin = example.com
alengel

@alengel anh ấy không muốn sử dụng tham số autoplay-url. Thay vào đó, anh cố gắng bắt đầu video bằng cách sử dụng chức năng tự động phát js-API. Nhưng vì một số lý do, hàm onYouTubeIframeAPIReady () không được gọi.
Humppakäräjät

@raven tôi đã tìm ra rồi. 1) xóa & origin = example.com khỏi url iframe. 2) trong phần "Khung & Tiện ích mở rộng" trong jsfiddle của bạn, đặt menu thả xuống thứ hai thành "Không bao bọc - <head>" 3) thêm api iframe làm tài nguyên bên ngoài ( youtube.com/iframe_api ); Tôi đã chia rẽ câu đố của bạn và áp dụng những thay đổi này: jsfiddle.net/e97famd1/1
Humppakäräjät

Bất kỳ ý tưởng nào eventhoặc commandgửi đến iframe YT để dừng lại listeningtrạng thái?
mkhatib 16/2/2016

@CletusW: Tôi gặp lỗi này: Uncaught (trong lời hứa) DOMException: Yêu cầu play () bị gián đoạn bởi một cuộc gọi để tạm dừng (). Hứa hẹn ': Việc trình bày một tài liệu không an toàn [cast: 233637DE?
LauraNMS

20

Bạn có thể làm điều này với mã ít hơn nhiều:

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

Ví dụ hoạt động: http://jsfiddle.net/kmturley/g6P5H/296/


Tôi thực sự thích cách này, chỉ cần điều chỉnh nó để hoạt động với chỉ thị Angular nên không cần tất cả các vòng lặp và vượt qua func tùy thuộc vào chức năng chuyển đổi với phạm vi (về cơ bản nếu video được hiển thị -> tự động phát; khác -> tạm dừng video). Cảm ơn!
DD.

Bạn nên chia sẻ chỉ thị, có thể hữu ích!
Kim T

1
Đây là bản chuyển thể của mã cho Bút: codepen.io/anon/pen/qERdza Tôi hy vọng nó có ích! Nó cũng bật / tắt phím ESC khi bạn bật video
DD.

Điều này có vẻ quá tốt là đúng! Có bất kỳ giới hạn trình duyệt / bảo mật để sử dụng phương pháp này?
Dan

Có IE có một vài hạn chế, đặc biệt là IE10 hỗ trợ MessageChannel thay vì postMessage: caniuse.com/#search=postMessage cũng nhận thấy bất kỳ Chính sách bảo mật nội dung nào cũng sẽ hạn chế sử dụng tính năng này
Kim T

5

Phiên bản mã Kim T của riêng tôi ở trên kết hợp với một số jQuery và cho phép nhắm mục tiêu các iframe cụ thể.

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

Làm sao để biết youtube đang phát? bất kỳ cuộc gọi lại từ iframe youtube, vì vậy bên ngoài có thể đăng ký?
Búa

@ Hammer Kiểm tra phần Sự kiện API của YouTube cụ thể là OnStateChange: developers.google.com/youtube/iframe_api_Vference#Events
adamj

@admj, Có thể kiểm tra cái này không? Một số hành vi wierd ... stackoverflow.com/questions/38389802/ từ
Búa

0

Cảm ơn bạn Rob W cho câu trả lời của bạn.

Tôi đã sử dụng điều này trong một ứng dụng Cordova để tránh phải tải API và để tôi có thể dễ dàng kiểm soát các iframe được tải động.

Tôi luôn muốn khả năng trích xuất thông tin từ iframe, chẳng hạn như trạng thái (getPlayerState) và thời gian (getCienTime).

Rob W đã giúp làm nổi bật cách API hoạt động bằng postMessage, nhưng tất nhiên điều này chỉ gửi thông tin theo một hướng, từ trang web của chúng tôi vào iframe. Truy cập các getters yêu cầu chúng tôi lắng nghe các tin nhắn được gửi lại cho chúng tôi từ iframe.

Tôi phải mất một thời gian để tìm ra cách điều chỉnh câu trả lời của Rob W để kích hoạt và lắng nghe các tin nhắn được trả về bởi iframe. Về cơ bản, tôi đã tìm kiếm thông qua mã nguồn trong iframe YouTube cho đến khi tôi tìm thấy mã chịu trách nhiệm gửi và nhận tin nhắn.

Chìa khóa đã thay đổi 'sự kiện' thành 'lắng nghe', điều này về cơ bản đã cho phép truy cập vào tất cả các phương thức được thiết kế để trả về giá trị.

Dưới đây là giải pháp của tôi, xin lưu ý rằng tôi đã chuyển sang 'nghe' chỉ khi yêu cầu getters, bạn có thể điều chỉnh điều kiện để bao gồm các phương thức bổ sung.

Lưu ý thêm rằng bạn có thể xem tất cả các tin nhắn được gửi từ iframe bằng cách thêm console.log (e) vào window.onmessage. Bạn sẽ nhận thấy rằng một khi nghe được kích hoạt, bạn sẽ nhận được các cập nhật liên tục bao gồm thời gian hiện tại của video. Gọi các getters như getPlayerState sẽ kích hoạt các cập nhật liên tục này nhưng sẽ chỉ gửi một tin nhắn liên quan đến trạng thái video khi trạng thái đã thay đổi.

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);
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.