在执行'postMessage'时出错:目标/来源不匹配 http vs https

4

很抱歉,我对我的问题感到非常困惑。这真的让我陷入了困境,因为它正在我的生产网站上引起问题。

我在我的网站上有一个JavaScript播放器,可以播放YouTube、SoundCloud或Vimeo上托管的歌曲列表。昨天我注意到了这个错误,通常情况下,每当您尝试通过播放器按钮“跳过”加载新歌曲时,就会出现这个错误。这个错误只是在最近一两天开始出现的。我没有在YouTube API发布说明中看到任何新内容,并且此错误在Chrome、Firefox和Safari中发生,因此很可能与浏览器的更改无关。不过,我正在使用的某些东西已经发生了变化,因为我已经18天没有推送新代码了。

这里有一个示例播放列表:http://www.muusical.com/playlists/programming-music

我认为我已经找到了重现错误的方法,以下是步骤:

  1. 播放YouTube托管的歌曲。
  2. 跳转到列表中的任何其他歌曲(无论是通过按跳过按钮还是直接按歌曲行项上的播放按钮)。

*请注意,如果播放列表中的第一首歌曲是YouTube歌曲,则即使不播放最初加载的YouTube歌曲,仅跳过到另一首歌曲也会产生错误。

基本上,一旦您加载和/或播放了YouTube歌曲并尝试跳转到另一首歌曲,该错误似乎就会发生。

如果您发现此行为的异常情况,请告诉我。

我在控制台中看到了这个错误:

Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://www.muusical.com') does not match the recipient window's origin ('http://www.muusical.com').

我使用Youtube的Javascript API来加载播放器:

new YT.Player('playlist-player', {
      playerVars: { 'autoplay': 1, 'fs': 0 },
      videoId: "gJ6APKIjFQY",
      events: {
        'onReady': @initPlayerControls,
        'onStateChange': @onPlayerStateChange
      }
    })

这将生成以下iframe:

<iframe id="playlist-player" frameborder="0" allowfullscreen="1" title="YouTube video player" width="640" height="360" src="https://www.youtube.com/embed/gJ6APKIjFQY?autoplay=1&amp;enablejsapi=1&amp;origin=http%3A%2F%2Fwww.muusical.com"></iframe>

点击上面的跳过按钮后,我在iframe中看到了以下加载的内容:
<iframe id="playlist-player" frameborder="0" allowfullscreen="1" title="YouTube video player" width="640" height="360" src=""></iframe>

我支持YouTube、SoundCloud和Vimeo的歌曲。似乎一旦加载了YouTube歌曲,"origin"就会从http更改为https。我认为不必包含其他主机的嵌入方法,因为即使整个播放列表仅由YouTube组成,此错误也会发生,并且在仅由SoundCloud和Vimeo的歌曲组成的播放列表中不会发生。
此外,这是我加载YouTube JavaScript的方式:
// Load the IFrame Player API code asynchronously.
  var tag = document.createElement('script');
  tag.src = "https://www.youtube.com/player_api";
  var firstScriptTag = document.getElementsByTagName('script')[0];
  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

如果您需要我澄清任何事情,请告诉我,感谢您的查看。

4个回答

3
@sodawillow的回答部分正确,但我想详细说明解决方案及导致我的代码停止调用.destroy()方法来删除youtube播放器的原因。
我的网站有一个播放器,可以从各种网站中切换歌曲,其中之一是Youtube。根据不同类型的播放器,可能会有不同的移除方法。我的代码检查是否存在youtube播放器,如果通过检查,则使用只有youtube播放器具有的.destroy()方法。问题在于YouTube更改了其播放器对象上某些静态变量的名称。例如,如果我通过以下方式创建播放器:
var player = new YT.Player('playlist-player', {
      playerVars: { 'autoplay': 1, 'fs': 0 },
      videoId: "gJ6APKIjFQY",
      events: {
      }
    })

那么就会有一个变量player.L,它保存着字符串"player"。为了检查当前播放器是否是YouTube播放器并将其删除,我这样做:

if (player.L == "player") {
  player.destroy();
} else {
//handle the removal of the Soundcloud or Vimeo player.
}

最近Youtube将字符串"player"的位置更改为现在位于player.M。我可以更改上面的代码以检查player.M而不是player.L,这样做可以解决问题,但为了避免将来出现类似问题,我实施了以下措施:

if (player.destroy) {
  player.destroy();
} else {
//handle the removal of the Soundcloud or Vimeo player.
}

只要Youtube没有未经通知地移除.destroy()方法,这将不会引起任何问题。
总之,问题就像@sodawillow所猜测的那样,我没有使用.destroy()方法来删除Youtube播放器。原因是因为Youtube对其API进行了一些未经通知的更改,改变了一些静态变量的位置。

我已经搜索并尝试了许多解决方案。这个很简单,但对我很有效。非常感谢。 - Hao XU

3
我已经对此进行了一些阅读,包括一些SO帖子和这个链接:https://code.google.com/p/gdata-issues/issues/detail?id=5788
我本来想在你的问题下添加评论,说我因为这个问题而疯狂......但当我开始描述我的设置时,我找到了一个避免这个问题的方法^^。
我从一个空的div元素开始,并使用Youtube Iframe API将其转换为一个具有所有必要选项的iframe
我有多个像这样的div,通常使用同一个JS变量来存储所有这些播放器,但一次只有一个(一个替换另一个,依此类推...... - 我知道可以更好)。
为了解决这个问题,我想到了用youtubePlayer.destroy();销毁播放器,然后从另一个元素构建新的播放器。我的Chrome控制台中不再出现JS错误:)。
希望它有所帮助,我能读到关于http和https的所有文献都不适用于我的情况,因为我没有自己设置iframe URL,并且我的网站恰好不是https......
我确实恢复了异步调用而不是静态的script标签在我的HTML中,但我认为这并不是必要的。
编辑:实际上,这个错误消息非常误导人,它只含义模糊地表示:你没有正确使用youtube API :)。

谢谢您的回复!当我想要摆脱玩家时,我确实会运行destroy()方法,但我实际上还没有阅读您发送的链接中的所有讨论。我尝试了各种不同的设置http/https在来源或不在来源以及动态加载的脚本中,但我将更加科学地通过这个讨论,并希望最终能够解决问题。 :) - wuliwong
你在哪个项目中遇到了这个问题?看起来你有很多与音乐相关的网站和播放器。 :) - wuliwong
你居然去了我的网站哈哈 :) 不知道在这里张贴是否是一个好事,但是......它已经在线上了 :) http://manec.fr: 每次你点击一个星球,就会打开一个带有不同 YouTube 播放器的部分(右下角)。 - sodawillow
我认为你在评论中发布链接相当自由。回答中也可以包含链接,但主要是希望你在回答中同时发布相关信息,以防链接后来失效。至少这是我目前了解的如何避免被stackoverflow警察抓住的方法。 - wuliwong
哇,我想通了。你说的有道理!Youtube曾经将字符串“player”存储在静态变量player.L下。所以,我用它来检查歌曲是否为Youtube,因为我支持几个不同的网站。如果是Youtube播放器,那么我就调用player.destroy(),否则我必须以不同的方式处理它。所以,Youtube悄悄地将静态变量名称更改为M,现在player.M == "player"。天啊!我必须找到一种更强大的方法来检查播放器是否确实是Youtube。 - wuliwong
显示剩余2条评论

2
这个错误属于Google Youtube API。
在"https://www.youtube.com/iframe_api"中:
if (!window['YT']) {
    var YT = {loading: 0, loaded: 0};
}
if (!window['YTConfig']) {
    var YTConfig = {'host': 'http://www.youtube.com'};
}

他们使用http而不是https。

我们需要覆盖“host”选项和“widget_referrer”,同样与“origin”相同。

player = new YT.Player('player', {
          host: 'https://www.youtube.com',
          height: '390',
          width: '640',
          videoId: 'M7lc1UVf-VE'
...
          origin: "https://www.enziin.com",
          widget_referrer: "https://www.enziin.com"
}

祝你好运。


0
我的解决方案是在一个尽可能空白的单独页面中编写所有Youtube播放器逻辑,并将该页面引用在一个IFRAME标签中。
<iframe src="/youtube_frame/?data=SOME_DATA -%>" height="400px" width="100%" frameborder="0" border="0" scrolling="no"></iframe>

然后,你的 youtube_frame.html 将会像这样:

<!DOCTYPE html>
<html>
  <body>
    <!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
    <div id="player"></div>

    <script>
      // 2. This code loads the IFrame Player API code asynchronously.
      var tag = document.createElement('script');

      tag.src = "https://www.youtube.com/iframe_api";
      var firstScriptTag = document.getElementsByTagName('script')[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

      // 3. This function creates an <iframe> (and YouTube player)
      //    after the API code downloads.
      var player;
      function onYouTubeIframeAPIReady() {
        player = new YT.Player('player', {
          height: '390',
          width: '640',
          videoId: 'M7lc1UVf-VE',
          events: {
            'onReady': onPlayerReady,
            'onStateChange': onPlayerStateChange
          }
        });
      }

      // 4. The API will call this function when the video player is ready.
      function onPlayerReady(event) {
        // event.target.playVideo();
      }

      // 5. The API calls this function when the player's state changes.
      //    The function indicates that when playing a video (state=1),
      //    the player should play for six seconds and then stop.
      var done = false;
      function onPlayerStateChange(event) {
        console.log("OnplayerStateChange");
        if (event.data == YT.PlayerState.PLAYING && !done) {
          console.log("OnplayerStateChange - If statement");
          setTimeout(stopVideo, 6000);
          done = true;
        }
      }
      function stopVideo() {
        player.stopVideo();
      }
    </script>
  </body>
</html>

(作为一个旁注:我的背景是Prototype.js干扰了事件'OnStateChange',我们无法移除这个依赖。 使用jsfiddle无法重现此问题,因为它有很少的依赖关系。 我的应用程序在Rails中构建,并使用此插件显示视频播放列表:https://github.com/Giorgio003/Youtube-TV/

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