使用YouTube API加载多个视频播放器

21

我需要使用YouTube的API加载多个视频。这是我第一次使用它,所以我不确定我做错了什么,但这是我正在尝试的:

  var player;
  var player2;
  function onYouTubePlayerAPIReady() {
    player = new YT.Player('player', {
      videoId: 'hdy78ehsjdi'
    });
    player2 = new YT.Player('player', {
      videoId: '81hdjskilct'
    });
  }
9个回答

32

由于 onYouTubeIframeAPIReady 函数只需要被调用一次,可以采用以下方法:

  • 初始化并将视频播放器信息 (ControlId、width、height、VideoId) 保存在数组中

  • 调用 onYouTubeIframeAPIReady 函数以创建所有视频播放器

示例

var playerInfoList = [{id:'player',height:'390',width:'640',videoId:'M7lc1UVf-VE'},{id:'player1',height:'390',width:'640',videoId:'M7lc1UVf-VE'}];

      function onYouTubeIframeAPIReady() {
        if(typeof playerInfoList === 'undefined')
           return; 

        for(var i = 0; i < playerInfoList.length;i++) {
          var curplayer = createPlayer(playerInfoList[i]);
        }   
      }
      function createPlayer(playerInfo) {
          return new YT.Player(playerInfo.id, {
             height: playerInfo.height,
             width: playerInfo.width,
             videoId: playerInfo.videoId
          });
      }

7
你尝试过使用事件来实现吗?出于某些原因,我的事件无法正常工作。以下是需要翻译的代码内容: function createPlayer(playerInfo) { return new YT.Player(playerInfo.id, { videoId: playerInfo.videoId, events: { 'onReady': onPlayerReady, 'onStateChange': onPlayerStateChange } }); } function onPlayerReady(event) { console.log('ready'); } - Fawn

26
新的YT.Player方法的第一个参数需要是HTML元素(如DIV)的ID,该元素将被替换为视频的iframe。 由于使用“ player”来命名这两个对象,因此您将两者都加载到同一元素中。
<div id="ytplayer1"></div>
<div id="ytplayer2"></div>

<script>
  var player;
  var player2;
  function onYouTubePlayerAPIReady() {
    player = new YT.Player('ytplayer1', {
      height: '390',
      width: '640',
      videoId: 'hdy78ehsjdi'
    });
    player2 = new YT.Player('ytplayer2', {
      height: '390',
      width: '640',
      videoId: '81hdjskilct'
    });
  }
</script>

在Youtube API文档中描述了函数的参数:
https://developers.google.com/youtube/iframe_api_reference#Loading_a_Video_Player(编辑:更正链接)


不错。然而,看起来玩家变量是不需要的。 - Marcus
你说得对mbee,它们可以省略掉。我只是纠正了原帖的片段。 - Egari

5

The HTML

<div data-id="youtubevideoidhere" class="video"></div>
<div data-id="youtubevideoidhere" class="video"></div>
<div data-id="youtubevideoidhere" class="video"></div>

视频的JS脚本

// CREATE VIDEOS "CLASS" to handler videos
var Videos = (function() {
    // VARIABLES
    var $   = jQuery,   // The jquery
    players = [],       // players array (to coltrol players individually)
    queue   = [];       // videos queue (once api is ready, transform this into YT player)

    // Constructor
    function Videos() {}

    // METHODS
    // Add elements to queue
    Videos.prototype.add = function($video) {
        queue.push($video);
    };

    // Load YT API
    Videos.prototype.loadApi = function() {
        // jQuery get script
        $.getScript("//www.youtube.com/iframe_api", function() {
            // once loaded, create the onYouTubeIframeAPIReady function
            window.onYouTubeIframeAPIReady = function() {
                queue.forEach(function($video) {
                    // Create the YT player
                    var player = new YT.Player($video.get(0), {
                        'width': "100%",
                        'height': "100%",
                        'videoId': $video.data("id")
                    });
                    // add to players array
                    players.push(player);
                });
            };
        });
    };

    return Videos;

})();

然后,创建像这样的视频。
var videos = new Videos();
$('.video').each( function () {
    videos.add( $(this) );
})
videos.loadApi();

1
谢谢你的帮助。你解决了我思考了几天的问题。问候。 - josue
1
这正是我所需要的,但是如何在按钮点击时播放视频呢?'player.playVideo();'似乎不起作用。你有这个的codepen吗? - user7201898
我喜欢这个变体。在这种情况下,我如何控制播放器?我在视频下添加了自定义按钮,并希望控制视频。在谷歌网站上,我找到了函数player.playVideo(),但我不知道如何与上面的代码一起使用。当我运行此函数时,我得到了“未定义播放器”的错误提示。 - Eugene Chefranov

2
我有一个更广泛的问题,归结为这个同样的问题。我需要编写一个JS类来管理一个或多个(数量可以从1到无限变化)视频嵌入。后端系统是ExpressionEngine(但这在这里是不相关的)。主要目标是建立一个分析框架,将个体数据推送到我们的Adobe Analytics平台。这里仅显示了给出播放次数的部分,它可以从这里扩展很多。
CMS允许编辑在页面上创建模块来呈现视频。每个模块一个视频。每个模块基本上是通过Bootstrap 3排列的HTML部分(对于这个答案无关紧要)。
相关的HTML看起来像这样:

<div id="js_youTubeContainer_{innov_mod_ytplayer:id}" class="embed-responsive embed-responsive-16by9">
  <div id="js_youTubeFrame_{innov_mod_ytplayer:id}" class="embed-responsive-item"></div>
</div>

这里说的"{innov_mod_ytplayer:id}"是我们CMS中的YouTube视频ID,它允许为每个嵌入项分配一个唯一的ID。稍后这非常重要。

在此之下,我会进行渲染:

            var innovYouTube_{innov_mod_ytplayer:id} = new Ariba.Innovations.YouTube.Class({
                'innovYouTubeVideoId': '{innov_mod_ytplayer:id}',
                'innovYouTubeVideoTitle': '{innov_mod_ytplayer:title}',
                'innovYouTubeDivId' : 'js_youTubeFrame_{innov_mod_ytplayer:id}'
            });
            innovYouTube_{innov_mod_ytplayer:id}.Init(); // And... Go!

            var onYouTubeIframeAPIReady = (function() {
                try{ //wrap this in try/catch because it actually throws errors when it runs subsequent times - this is expected as it's related to YouTube "rerunning" the function on other videos.
                    innovYouTube_{innov_mod_ytplayer:id}.config.functionCache = onYouTubeIframeAPIReady; //cache the existing global function
                    return function() {
                        try{
                            innovYouTube_{innov_mod_ytplayer:id}.onYouTubeIframeAPIReady(); //execute this instance's function
                            var newOnYouTubeIframeAPIReady = innovYouTube_{innov_mod_ytplayer:id}.config.functionCache.apply(this, arguments); //add instances to global function
                            return newOnYouTubeIframeAPIReady; //update global function
                        }catch(err){}
                    };
                }catch(err){}
            })();

你会在这里看到一些ExpressionEngine模板标记 - 这些只是YouTube的视频ID和视频标题。要复制此操作,您需要相应更改它们。
这样做的目的是允许我动态更新单个全局回调以获取每个新嵌入视频的新代码。最终,此回调将包含对其自己的类实例的调用。您需要那些try/catch块,因为它为除了“当前正在执行”的嵌入之外的所有“其他”嵌入引发了虚假正面错误 - 请记住,此脚本针对页面上的每个嵌入运行一次。这些错误是预期的,实际上并不会造成问题,因此try/catch将它们抑制。
使用CMS模板标记,我基于YouTube视频ID创建每个实例。如果有人添加了同一个视频模块多次,我可能会遇到问题,但是这是一个易于处理的业务问题,因为不应该发生这种情况。这使我能够为每个视频重复实例化我的类的唯一实例。
该脚本的关键部分基于这个极其有用的SO答案:Adding code to a javascript function programmatically 这是实际的类。它主要是注释的...我们使用jQuery,因此您将在这里看到一个重要的用法,即$.extend()方法。我在类构造函数方法中使用它作为方便,但您也可以使用vanilla JS(JavaScript equivalent of jQuery's extend method)。我只是发现jQuery更容易阅读,并且由于它对我可用,我使用它。

if (typeof Ariba === "undefined") { var Ariba = {}; }
if (typeof Ariba.Innovations === "undefined") { Ariba.Innovations = {}; }
if (typeof Ariba.Innovations.YouTube === "undefined") { Ariba.Innovations.YouTube = {}; }

if (typeof Ariba.Innovations.YouTube.Class === "undefined") {//this script may be embedded more than once - do this to avoid re-processing it on subsequent loads
    Ariba.Innovations.YouTube.Class = function (config) {
        this.static = {
            'ytScriptId': 'js_youtubeFrameAPI',
            'ytScriptUrl': 'https://www.youtube.com/iframe_api'
        };//static configuration.  Will overwrite any other settings with the same name
        this.config = {//optional configuration variables. Will be overridden by instance or static settings with the same name.
            'adobeAnalyticsFired': false
        };
        this.config = $.extend(true, this.config, config);//inserts (destructively!) the instance settings.
        this.config = $.extend(true, this.config, this.static);//inserts (destructively!) the static settings.
        this.config.this = this;
    };

    Ariba.Innovations.YouTube.Class.prototype.Init = function () {
        //Note: have to allow it to write it over an over because calling the API script is what makes YouTube call onYouTubeIframeAPIReady.
        //if (document.getElementById('js_youtubeFrameAPI') === null) { // don't add the script again if it already exists!
        this.config.apiScript = document.createElement('script');
        this.config.apiScript.src = 'https://www.youtube.com/iframe_api';
        this.config.apiScript.id = 'js_youtubeFrameAPI' + this.config.innovYouTubeVideoId;
        this.config.firstScriptTag = document.getElementsByTagName('script')[0];
        this.config.firstScriptTag.parentNode.insertBefore(this.config.apiScript, this.config.firstScriptTag);
        //}
        //else { console.log("iframe script already embedded", this.config.innovYouTubeVideoId); }
    }

    Ariba.Innovations.YouTube.Class.prototype.onYouTubeIframeAPIReady = function (event) {
        //console.log("onYouTubeIframeAPIReady", this.config.innovYouTubeVideoId, arguments);
        var _this = this;
        //console.log(this);
        this.config.ytPlayer = new YT.Player(this.config.innovYouTubeDivId, {
            videoId: this.config.innovYouTubeVideoId,
            events: {
                'onReady': _this.onPlayerReady.bind(_this),
                'onStateChange': _this.onPlayerStateChange.bind(_this)
            }
        });
    }

    Ariba.Innovations.YouTube.Class.prototype.onPlayerReady = function (event) {
        //console.log("onPlayerReady", this.config.innovYouTubeVideoId, event);
    }

    Ariba.Innovations.YouTube.Class.prototype.onPlayerStateChange = function (event) {
        //console.log("onPlayerStateChange", this.config.innovYouTubeVideoId, event, this);
        if (event.data === YT.PlayerState.PLAYING && !this.config.adobeAnalyticsFired) {
            //console.log("YouTube Video is PLAYING!!", this.config.innovYouTubeVideoId);
            this.config.adobeAnalyticsFired = true;
            if (typeof _satellite !== "undefined") {
                window._satellite.data.customVars.adhoc_tracker_val = "Innovations Video: " + this.config.innovYouTubeVideoTitle + " (" + this.config.innovYouTubeVideoId + ")";
                _satellite.track('adhoctrack');
            }
        }
    }
}

另外需要注意的几点:

一旦解决了主要的全局回调问题,将作用域保留在类实例中就很容易。你只需要添加.bind()即可。例如:

'onReady': _this.onPlayerReady.bind(_this)

您可能还会看到以下内容:
var _this = this;

这样做是为了避免意外丢失实例的“this”作用域。也许不是必需的,但这是我多年来采用的惯例。

总之,我已经花了一个星期的时间在这个问题上,并且认为我应该与SO社区分享它,因为从我的寻找答案中清楚地表明,很多人都在寻找解决方案。


注意:我已经修复了一个错误......你需要过滤YouTube ID中的破折号,因为那会破坏脚本。例如:innovYouTube_{innov_mod_ytplayer:id},其中ID类似于xyz123-123,因为这将生成一个名为innovYouTube_xyz123-123的变量名称,显然是无效的。只是想提一下。 - Carnix

1

我在React中需要相同的东西。在Vadim的答案基础上,您可以执行以下操作,并将它们添加到对象中,然后在不知道播放器数组外观的情况下创建播放器。

const YoutubeAPILoader = {
  _queue: [],
  _isLoaded: false,

  load: function (component) {
    // if the API is loaded just create the player
    if (this._isLoaded) {
      component._createPlayer()
    } else {
      this._queue.push(component)

      // load the Youtube API if this was the first component added
      if (this._queue.length === 1) {
        this._loadAPI()
      }
    }
  },

  _loadAPI: function () {
    // load the api however you like
    loadAPI('//youtube.com/player_api')

    window.onYouTubeIframeAPIReady = () => {
      this._isLoaded = true
      for (let i = this._queue.length; i--;) {
        this._queue[i]._createPlayer()
      }
      this._queue = []
    }
  }
}

0
作为 Vadim 回答的补充,以下内容在处理事件时对我有用:

const iframes = [{id: 'hello'},...];
const inOnReadyScope = "I can be accessed by onPlayerReady"

function onYouTubeIframeAPIReady() {
  for (let i = 0; i < iframes.length; i++) {
    const player = new YT.Player(iframe.id, {
      events {
        onReady: onPlayerReady
      }
    }
    function onPlayerReady(event){
      event.target.setVolume(0);
      console.log(inOnReadyScope)
      // use anything on event
    }
  }
}

0
<iframe title="YouTube video player" src="https:YOUR CHANNEL Full Link" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe>

1
虽然这段代码片段可能解决了问题,但它并没有解释为什么或者如何回答这个问题。请在你的代码中包含一个解释,因为这真的有助于提高你的帖子质量。记住,你正在为未来的读者回答问题,而那些人可能不知道你的代码建议的原因。 - Luca Kiebel

0

我为了加载多个视频所做的是在视频之外单击时销毁iframe(您可以使用任何事件),然后再次创建div,以便您可以重用具有另一个视频ID的div。


-2
<script type="text/javascript">

    $(document).ready(function () {


        $(".youtube-player").each(function () {
            var playerid = $(this).attr("id");
            setTimeout(function () {
                onYouTubeIframeAPIReady2(playerid);
            }, 2000);
        });

    });

    function onYouTubeIframeAPIReady2(PlayerID) {

        var ctrlq = document.getElementById(PlayerID);
        console.log(ctrlq);
        var player = new YT.Player(PlayerID, {
            height: ctrlq.dataset.height,
            width: ctrlq.dataset.width,
            events: {
                'onReady': function (e) {
                    e.target.cueVideoById({
                        videoId: ctrlq.dataset.video,
                        startSeconds: ctrlq.dataset.startseconds,
                        endSeconds: ctrlq.dataset.endseconds
                    });
                }
            }
        });
    }
</script>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接