根据您是否在中国在优酷和 YouTube 之间切换

Switching between Youku and YouTube based on whether you're in China

我正在托管一个会议网站,如果您在中国,我想在其中嵌入一个 Youku 视频,否则在其中嵌入一个 YouTube 视频。会议网站通过防火墙内的 CDN 提供服务。我得到了下面的代码,可以在优酷的内部版本和外部版本之间切换。

不幸的是,ipinfo.io 似乎无法在防火墙内访问,因此代码在 10 秒后超时。

我考虑重写页面以默认使用 Youku 视频,编写一个小的 non-blocking JavaScript 函数来尝试访问 YouTube。如果可以,把优酷换成优酷。如果不能,请无害地退出。这样一来,YouTube 的可达性就成为了关键测试,而不是你是否在中国。

或者,我考虑过在我的网站上托管视频,以便通过防火墙内的 CDN 复制它。但是,这意味着视频将始终以全分辨率下载,即使您的连接速度很慢。

有更好的优酷和Youtube切换的建议吗,或者更一般地说,有一个视频可以在中国境内外播放吗?

jQuery.get("https://ipinfo.io", function(response) {
    var country = response.country;

    if(country == 'CN') {
        youku.attr('src',chinaVideo)
    } else {
        youku.attr('src',generalVideo)
    }
}, "jsonp");

这是我们要使用的JavaScript:

$(document).ready(function (){
    var country = '',
    youku = $('#youku');

    $.ajax({
        url: "https://ipinfo.io",
        dataType: "jsonp",
        success: function(response){
            var country = response.country;

            if(country != 'CN') {
                youku.attr('src','https://www.youtube.com/embed/K3cEE5h7c1s')
            }
         },
         error: function(){
            console.log('sorry...')
         },
         timeout: 5000
    });     
});

我们将优酷 link 包含在 HTML 中,如果国家/地区不是中国,则切换到 YouTube。如果连接到 ipinfo.io 超时,这仍然有效,这在中国有时会发生。

编辑:修改为添加 5 秒超时。

Edit2:我们将其实现为开源 Wordpress 插件,以防其他人感兴趣。 https://github.com/cncf/china-video-block

您当前的解决方案并不理想,因为它取决于 GET 请求超时,这会减慢速度。由于问题主要围绕 ipinfo.io 在中国被屏蔽,有效的解决方案可能涉及基于中国的 IP 识别服务。

尝试使用 http://www.ip138.com/。 Google 翻译效果很好,可以用英语阅读。好像没有正式的JSONAPI,不过你可以按http://www.ip138.com/ips138.asp?ip=<IP_ADDRESS_HERE>获取位置信息

为此,请将 DOM 和 jQuery(因为您已经在使用它)导航到带有 本站数据: 的行,该行位于国家名称之前。我认为当前构建的页面的选择器只是 $( "li" ).first()。将冒号和 POST 后的字符串提取到 Google 翻译 API 以获取英文国家名称,瞧!您有一种更快、更可靠(如果相当复杂)的方法来查看访问者是否在中国。

编辑:您是否事先拥有用户的 IP 地址并不完全清楚。如果没有,您可以使用与 https://geoiptool.com/zh/ 类似的方法,它也位于中国境内,因此不会被阻止。我很乐意根据需要充实这部分内容。

最好具体检查 YouTube 是否在用户网络中被阻止,而不是根据 IP 过滤掉国家,这永远不会 100% 可靠。这样一来,它甚至可以在任何 school/office 环境中运行,在这种环境下,网络管理员可能出于任何原因阻止 YouTube。

This 是我在这方面能找到的最佳答案,它试图通过 JavaScript 加载 YouTube 的 favicon。如果成功下载该小图像,则可以访问 YouTube。该图片只有 0.2 KB 的下载量,很可能已经被任何访问过 YouTube 的用户缓存了。

这意味着对于具有 YouTube 访问权限的用户来说,结果几乎是即时的,对于防火墙被阻止的用户来说,只需几秒钟。代码很简单:

jQuery(function(){
    var youku = jQuery('#youku');
    var image = new Image();
    image.onload = function(){
        youku.attr('src','https://www.youtube.com/embed/K3cEE5h7c1s')
    };
    image.onerror = function(){
        youku.attr('src','https://www.youku.com/embed/K3cEE5h7c1s')
    };
    image.src = "https://youtube.com/favicon.ico";
});

这个解决方案比上面提到的更好,因为它不等待 jQuery 的 document.ready 事件,图像立即开始加载。

(function(){
    var image = new Image();
    image.onload = function(){
        jQuery(function(){
            jQuery('#youku').attr('src','https://www.youtube.com/embed/K3cEE5h7c1s')
        });
    };
    image.onerror = function(){
        jQuery(function(){
            jQuery('#youku').attr('src','https://www.youku.com/embed/K3cEE5h7c1s')
        });
    };
    image.src = "https://youtube.com/favicon.ico";
})();

根据评论更新:

如果图像之前有可能被缓存在用户的计算机上,将当前时间戳参数添加到图像 URL 将确保图像在之前缓存的情况下加载。

    image.src = "https://youtube.com/favicon.ico?_=" + Date.now();

Any better suggestions on how to switch between Youku and Youtube, or more generally have a video playable both inside and outside China?

我建议看看阿里云。他们有很多服务,但 AFAIK 没有免费计划。
由于我不熟悉中国的其他云提供商,让我描述一个阿里云的解决方案。

我看到两个可能的选择:

  1. 使用他们的 CDN 服务 并创建两个 JS 包 - 一个用于中国,第二个用于世界其他地区。第一个将嵌入优酷的视频,第二个嵌入 Youtube 的视频。将它们上传到 CDN 后,您可以将它们强制推送到 China/world 中的本地节点,以便始终为用户提供快速和最新的响应;
  2. 使用他们的媒体转码服务进行视频处理 + 对象存储服务 进行视频托管 + CDN 服务 用于分发。通过这种方式,您可以创建多个不同质量的视频并将它们分发到您的用户附近。 JS会直接从CDN嵌入视频。对于一个视频来说,这可能有点矫枉过正。

使用客户的时区怎么样?虽然不完美,但它可以让您避免完全依赖超时或任何 geoip 服务请求。如果可能的话,我会避免使用可能导致从同一地址块(例如来自会议参加者)多次重复调用同一阻塞资源的方法。我上次住在中国时,我记得从防火墙触发 TCP 重置会持续 30 秒或更长时间。

Intl.DateTimeFormat().resolvedOptions().timeZone 在现代浏览器中是合理的 well-supported。在中国,应该return中国的IANA时区:"Asia/Shanghai":

console.log(Intl.DateTimeFormat().resolvedOptions().timeZone)

还有一些库 (jsTimezoneDetect and moment-timezone) 将上述内容与 DategetTimezoneOffset 函数结合在一起,跨多个时间点,对时区进行有根据的猜测,这可能会在旧版浏览器上产生更好的结果:

console.log(jstz.determine().name());
console.log(moment.tz.guess());
<script src="//cdnjs.cloudflare.com/ajax/libs/jstimezonedetect/1.0.6/jstz.min.js"></script>

<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.5/moment-timezone-with-data-2010-2020.min.js"></script>

就我而言,我可能会尝试将时区检测为 "Asia/Shanghai" 并回退到 "UTC+8" 的时区偏移(仅时区偏移会比中国大陆捕获更多,但我会认为这是后备策略可以接受的过度范围),例如:

var ianaTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
var offset = new Date().getTimezoneOffset();

if (ianaTz === "Asia/Shanghai" || (ianaTz == null && offset/60 == -8)) {
  console.log("We'll assume China");
} else {
  console.log("not China");
}

您可以使用 ffmpeg 将视频转换为 HLS 版本,根据 RFC 8216 手动制作 HLS 播放列表(.m3u8)。ffmpeg 不会为您创建主播放列表。 HLS 有助于使分辨率适应互联网速度。如果将它与免费的 hls.js javascript 媒体播放器结合使用,您可以将其托管在您自己的站点上,以通过中国防火墙,并支持自适应分辨率。

这是一个 HLS 主播放列表示例,因此您不一定需要阅读 RFC 8216,该文件将被称为 Video.m3u8:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080
Video_1080p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=9000000,RESOLUTION=2560x1440
Video_1440p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=16000000,RESOLUTION=3840x2160
Video_4k.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
Video_720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480
Video_480p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
Video_360p.m3u8

带宽以每秒位数指定,质量顺序无关紧要,因为媒体播放器会选择要播放的分辨率。

您可以在命令提示符中用于 ffmpeg 的命令是:

ffmpeg -hwaccel dxva2 -hide_banner -y -i "C:\input\Video.mp4" -vf scale=1920:1080 -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -r 30 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 2 -hls_playlist_type vod -b:v 5000k -maxrate 5350k -bufsize 7500k -b:a 192k -hls_segment_filename "C:\output\Video_1080p_%03d.ts" "C:\output\Video_1080p.m3u8" -vf scale=2560:1440 -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -r 30 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 2 -hls_playlist_type vod -b:v 9000k -maxrate 10350k -bufsize 14500k -b:a 192k -hls_segment_filename "C:\output\Video_1440p_%03d.ts" "C:\output\Video_1440p.m3u8" -vf scale=3840:2160 -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -r 30 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 2 -hls_playlist_type vod -b:v 16000k -maxrate 20350k -bufsize 26000k -b:a 192k -hls_segment_filename "C:\output\Video_4k_%03d.ts" "C:\output\Video_4k.m3u8" -vf scale=1280:720 -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -r 30 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 2 -hls_playlist_type vod -b:v 2800k -maxrate 2996k -bufsize 4200k -b:a 128k -hls_segment_filename "C:\output\Video_720p_%03d.ts" "C:\output\Video_720p.m3u8" -vf scale=842:480 -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -r 30 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 2 -hls_playlist_type vod -b:v 1400k -maxrate 1498k -bufsize 2100k -b:a 128k -hls_segment_filename "C:\output\Video_480p_%03d.ts" "C:\output\Video_480p.m3u8" -vf scale=640:360 -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 -r 30 -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 2 -hls_playlist_type vod -b:v 800k -maxrate 865k -bufsize 1200k -b:a 96k -hls_segment_filename "C:\output\Video_360p_%03d.ts" "C:\output\Video_360p.m3u8"

您唯一需要做的就是将 C:\input\Video.mp4 替换为您的视频,并将所有 C:\output\Video 替换为视频的输出名称。 360p 代表输出视频文件名中指定的分辨率,它不影响实际分辨率,%03d 是一个命令,它被解析为视频片段提供一个数字,例如 001 到 999。下划线用于可读性。

评论说明: -hwaccel dxva2 意味着 ffmpeg 将使用 directx 使用硬件加速,这可以从 windows vista 及更高版本获得。 -hls_time 2 将片段时间设置为每个片段 2 秒。 aac 和 h264 是在 hls 视频片段中使用的编解码器。

此 ffmpeg 命令生成 6 个质量级别,但不创建主播放列表。主播放列表是每个带宽播放列表的播放列表(见上文),hls 播放器根据互联网速度选择要播放的视频片段播放列表。

使用 JAVASCRIPT

从浏览器获取区域信息

为了加快检测过程,您可以先尝试从浏览器包安装语言中提取语言环境信息:

navigator.language
navigator.languages

然后将其结合起来,或者作为最后一个资源,回退到地理位置或其他方式来检测来自 Accept-Language HTTP header.

的用户语言偏好

下面是外部服务抓取浏览器的例子headers:

https://ajaxhttpheaders1.appspot.com/?callback=getHeaders

另一种可能与上述解决方案相结合的方法是从用户环境或浏览上下文中获取一些字符串(window.location、document.cookie、document.localStorage、Date()或 Date().toLocaleString() 等)并使用正则表达式 Unicode 范围扫描这些字符串以查找中文 Unicode 代码点。

我相信这会让您得到一个非常好的近似值,而无需提出额外的要求。作为我的 Firefox 浏览器中的一个例子,调用 Date().toLocaleString() 也会产生:

"Mon Jul 30 2018 01:35:38 GMT+0200 (Ora legale dell’Europa centrale)"

可以很容易地解析为 "Italian" 语言环境。

在某处你会发现一个中文的Unicode点ideogram/symbol。