YouTube iframe API'sı: HTML’de bulunan bir iframe oynatıcıyı nasıl kontrol ederim?


149

İframe tabanlı YouTube oynatıcılarını kontrol etmek istiyorum. Bu oynatıcılar zaten HTML'de olacak, ancak onları JavaScript API'sı aracılığıyla kontrol etmek istiyorum.

API ile sayfaya nasıl yeni bir video ekleneceğini açıklayan iframe API'sının belgelerini okudum ve ardından YouTube oynatıcı işlevleriyle kontrol ettik :

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

Bu kod yeni bir oyuncu nesnesi oluşturur ve bunu 'player' öğesine atar, daha sonra #container div içine ekler. Sonra 'oyuncu' ve çağrı üzerine çalışabilir playVideo(), pauseVideo()bunun üzerine, vb.

Ancak zaten sayfada olan iframe oynatıcılarda çalışabilmek istiyorum.

Bunu eski gömme yöntemi ile çok kolay bir şeyle yapabilirim:

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

Ancak bu yeni iframe'lerle çalışmaz. Zaten sayfada bir iframe nesnesi atayabilir ve ardından API işlevlerini nasıl kullanabilirim?


YouTube IFrame API'sı ile çalışmak için bir soyutlama yazdım github.com/gajus/playtube
Gajus

Yanıtlar:


316

Fiddle Links: Kaynak kodu - Önizleme - Küçük sürüm
Güncelleme: Bu küçük işlev kodu yalnızca tek bir yönde yürütür. Tam destek istiyorsanız (örn. Olay dinleyicileri / alıcıları), jQuery'de Youtube Etkinliğini Dinleme'ye göz atın

Derin kod analizinin bir sonucu olarak, bir işlev oluşturdum: function callPlayerçerçeveli herhangi bir YouTube videosunda bir işlev çağrısı ister. Olası işlev çağrılarının tam listesini almak için YouTube Api referansına bakın . Açıklama için kaynak kodundaki yorumları okuyun.

17 Mayıs 2012 tarihinde, oyuncunun hazır durumuna bakmak için kod boyutu iki katına çıkarıldı. Oyuncunun hazır durumu ile ilgilenmeyen kompakt bir işleve ihtiyacınız varsa, bkz. 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);
    }
}

Kullanımı:

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

Olası sorular (ve cevaplar):

S : Çalışmıyor!
C : "Çalışmıyor" açık bir açıklama değil. Hata mesajı alıyor musunuz? Lütfen ilgili kodu gösterin.

S : playVideovideoyu oynatmıyor.
C : Oynatma için kullanıcı etkileşimi ve allow="autoplay"iframe üzerinde bulunması gerekir . Https://developers.google.com/web/updates/2017/09/autoplay-policy-changes ve https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide sayfasına bakın.

S : Kullanarak bir YouTube videosu yerleştirdim <iframe src="http://www.youtube.com/embed/As2rZGPGKDY" />ancak işlev herhangi bir işlev yürütmüyor!
A : Sen eklemek zorunda ?enablejsapi=1senin URL'nin sonunda: /embed/vid_id?enablejsapi=1.

S : "Geçersiz veya geçersiz bir dize belirtildi" hata iletisini alıyorum. Neden?
Y : API, yerel bir ana bilgisayarda ( file://) düzgün çalışmıyor . (Test) sayfanızı çevrimiçi olarak barındırın veya JSFiddle kullanın . Örnekler: Bu cevabın üst kısmındaki bağlantılara bakın.

S : Bunu nasıl bildin?
Y : API'nin kaynağını manuel olarak yorumlamak için biraz zaman harcadım. postMessageYöntemi kullanmak zorunda olduğum sonucuna vardım . Hangi bağımsız değişkenlerin iletileceğini bilmek için iletileri engelleyen bir Chrome uzantısı oluşturdum. Uzantının kaynak kodu buradan indirilebilir .

S : Hangi tarayıcılar desteklenir?
C : JSON ve postMessage.

  • IE 8+
  • Firefox 3.6+ (aslında 3.5, ancak document.readyState3.6'da uygulandı)
  • Opera 10.50+
  • Safari 4+
  • Chrome 3+

İlgili cevap / uygulama: jQuery
Tam API desteğini kullanarak çerçeveli bir videoda solma : jQuery
Resmi API'sinde Youtube Etkinliğini Dinleme : https://developers.google.com/youtube/iframe_api_reference

Revizyon Geçmişi

  • 17 2012 olabilir
    Uygulanan onYouTubePlayerReady: callPlayer('frame_id', function() { ... }).
    Oynatıcı henüz hazır olmadığında işlevler otomatik olarak sıraya alınır.
  • 24 Temmuz 2012
    Desteklenen tarayıcılarda güncellendi ve başarıyla test edildi (ileriye bakın).
  • 10 ekim 2013 Bir işlev bağımsız değişken olarak iletildiğinde callPlayer, hazır olup olmadığını denetlemeye zorlar. Bu gereklidir, çünkü callPlayerbelge hazırken iframe'in yerleştirilmesinden hemen sonra çağrıldığında, iframe'in tamamen hazır olduğundan emin olamaz. Internet Explorer ve Firefox'ta, bu senaryonun bir çok erken çağırma sonuçlandı postMessagegöz ardı edildi.
  • 12 Aralık 2013, &origin=*URL'ye eklenmesi önerilir .
  • 2 Mar 2014, &origin=*URL'ye kaldırma önerisini geri çekti .
  • 9 Nisan 2019, sayfa hazır olmadan YouTube yüklendiğinde sonsuz bir tekrarlamaya neden olan hatayı düzeltin. Otomatik oynatma hakkında not ekleyin.

@RobW Aslında denedim. Hatalı JSON komut dosyanızdaki gibi değil, iframe'in içinde youtube API'sının bir parçası olarak görünüyor.
Fresheyeball

@ Bu güzel pasaj için teşekkürler. Etkinlik dinleyicisi eklemek için youtube JS API'sını kullanmak yerine İleti Etkinliğini kullanmanın herhangi bir yolunu buldunuz mu?
brillout

@ brillout.com PostMessageYöntem YT JS API'sine ( ?enablejsapi=1) dayanmaktadır . JS API'sini etkinleştirmeden, postMessageyöntem hiçbir şey yapmaz , Olay dinleyicilerinin kolay uygulanması için bağlantılı cevaba bakın . Ayrıca çerçeve ile iletişim kurmak için okunabilir kod oluşturdu, ancak yayınlanmadı. Yayınlamamaya karar verdim, çünkü etkisi varsayılan YouTube Frame API'sine benzer.
Rob W

1
@MatthewBaker İleti olayını dinlemeyi ve sonucun durumunu ayrıştırmayı gerektirir. Bu kadar basit çağrılar kadar kolay değil playVideo, bu yüzden bunun için resmi API'yi kullanmanızı tavsiye ederim. Developers.google.com/youtube/iframe_api_reference#Events adresine bakın .
Rob W

1
@ffyeahh Açık bir hata görmüyorum. Lütfen bu yanıta yorumlarda soru eklemek yerine bağımsız bir yeniden oluşturma adımlarıyla yeni bir soru sorun.
Rob W

33

YouTube'un JS API'sini güncellediği anlaşılıyor, bu nedenle varsayılan olarak kullanılabilir! Mevcut bir YouTube iframe kimliğini kullanabilirsiniz ...

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

... JS'nizde ...

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

function onPlayerStateChange() {
  //...
}

... ve yapıcı mevcut iframe'inizi yenisiyle değiştirmek yerine kullanacaktır. Bu ayrıca videoId'yi yapıcıya belirtmeniz gerekmediği anlamına gelir.

Bkz. Video oynatıcı yükleme


1
@ raven url'de autoplay = 1 parametresini kaçırdınız. Örnek
url'nizde

@alengel autoplay-url parametresini kullanmak istemiyor. Bunun yerine js-API'lerin otomatik oynatma işlevini kullanarak videoyu başlatmaya çalışır. Ancak bazı nedenlerden dolayı onYouTubeIframeAPIReady () işlevi çağrılmaz.
Humppakäräjät

@ raven anladım. 1) & origin = example.com'u iframe url'sinden kaldırın. 2) jsfiddle öğenizin "Çerçeveler ve Uzantılar" bölümünde ikinci açılır menüyü "Sarma yok - <head>" olarak ayarlayın 3) youtube iframe api'sini harici kaynak olarak ekleyin ( youtube.com/iframe_api ); Kemanını çatalladım
Humppakäräjät

Durumu durdurmak için ne eventveya commandYT iframe'e gönderilecek bir fikrin var listeningmı?
mkhatib

@CletusW: Bu hatayı alıyorum: Uncaught (promise içinde) DOMException: play () isteği, bir pause () çağrısı tarafından kesildi. Promise (async) (anonim) @ scripts.js: 20 dispatch @ jquery-1.12.4.js: 5226 elemData.handle @ jquery-1.12.4.js: 4878 cast_sender.js: 67 Yakalanmayan DOMException: 'PresentationRequest oluşturulamadı ': Güvenli olmayan bir belgenin sunumu [cast: 233637DE? Yetenekleri = video_out% 2Caudio_out & clientId = 153262711713390989 & autoJoinPolicy = tab_and_origin_scoped & defaultActionPolicy = cast_this_tab & launchTimeout = 30000] güvenli bir bağlamda yasaklanmıştır.
LauraNMS

20

Bunu çok daha az kodla yapabilirsiniz:

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

Çalışma örneği: http://jsfiddle.net/kmturley/g6P5H/296/


Ben gerçekten bu şekilde sevdim, sadece bir Açısal yönerge ile çalışmak için adapte edildi, bu yüzden tüm döngüye ihtiyaç duymadım ve kapsamla bir geçiş işlevine bağlı olarak func'u geçtim (temelde video gösteriliyorsa -> otomatik oynat; başka -> duraklat video). Teşekkürler!
DD.

Direktifi paylaşmalısınız, faydalı olabilir!
Kim T

1
İşte bir Pen için kod uyarlaması: codepen.io/anon/pen/qERdza Umarım yardımcı olur! Ayrıca, video
DD'de

Bu gerçek olamayacak kadar iyi görünüyor! Bu yöntemi kullanmanın herhangi bir tarayıcı / güvenlik sınırlaması var mı?
Dan

Evet, IE'nin bazı sınırlamaları vardır, özellikle postMessage yerine MessageChannel'i destekleyen IE10 : caniuse.com/#search=postMessage ayrıca herhangi bir İçerik Güvenliği Politikasının da bu özelliğin kullanımını kısıtlayacağını unutmayın
Kim T

5

Kim T kodunun yukarıdaki bazı sürümü jQuery ile birleşiyor ve belirli iframe'lerin hedeflenmesine izin veriyor.

$(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'un nasıl oynandığını nasıl bilebilirim? youtube iframe herhangi bir geri arama, yani dışında abone olabilirsiniz?
Hammer

@Hammer YouTube API Etkinlikleri bölümüne özellikle göz atın OnStateChange: developers.google.com/youtube/iframe_api_reference#Events
adamj

@admj, Şuna bir bakabilir misiniz? Bazı garip davranışlar ... stackoverflow.com/questions/38389802/…
Hammer

0

Cevabınız için teşekkür ederim Rob W.

API yüklemek zorunda kalmamak için bir Cordova uygulaması içinde bu kullanarak ve kolayca dinamik olarak yüklenen iframe'leri kontrol edebilirsiniz.

Ben her zaman devlet (getPlayerState) ve zaman (getCurrentTime) gibi iframe bilgi elde edebilmek için yeteneği istedim.

Rob W, API'nin postMessage kullanarak nasıl çalıştığını vurgulamaya yardımcı oldu, ancak elbette bu, web sayfamızdan iframe'e yalnızca bir yönde bilgi gönderir. Alıcılara erişmek, iframe'den bize geri gönderilen mesajları dinlememizi gerektirir.

İframe tarafından döndürülen mesajları etkinleştirmek ve dinlemek için Rob W'nin cevabını nasıl ayarlayacağımı anlamak biraz zaman aldı. Temelde, mesaj gönderip almaktan sorumlu olan kodu bulana kadar YouTube iframe içindeki kaynak kodunu araştırdım.

Anahtar 'olay'ı' dinleme 'olarak değiştiriyordu, bu temelde değerleri döndürmek için tasarlanan tüm yöntemlere erişim sağlıyordu.

Aşağıda benim çözümüm, sadece alıcılar istendiğinde 'dinlemeye' geçtiğimi lütfen unutmayın, ekstra yöntemler eklemek için koşulu düzenleyebilirsiniz.

Ayrıca, window.onmessage'a bir console.log (e) ekleyerek iframe'den gönderilen tüm iletileri görüntüleyebileceğinizi unutmayın. Dinleme etkinleştirildikten sonra videonun geçerli saatini içeren sürekli güncellemeler alacağınızı göreceksiniz. GetPlayerState gibi alıcıları aramak bu sürekli güncellemeleri etkinleştirir, ancak yalnızca durum değiştiğinde video durumunu içeren bir mesaj gönderir.

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);
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.