JavaScript Library – YouTube Embedded、YouTube Player API、YouTube Data API
YouTube Embed Video
它和 Google Maps Embed 类似,是通过 iframe 完成的。
<iframe width="800" style="aspect-ratio: 16 / 9" src="https://www.youtube.com/embed/vEZCoe9GJFk" title="粉色海洋" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen ></iframe>
地址栏上面 v=code 就是每个 video 的 unique key 了
效果
它可以做一些配置, 参考: Supported parameters
比如自动播放
src="https://www.youtube.com/embed/vEZCoe9GJFk?autoplay=1"
注意:autoplay 在手机是不 work 的哦,哪怕是用户触发点击事件后你才 append iframe 依然是不 work 的。只有通过 Player API 的 event onReady 才能实现手机的 autiplay(或者说那根本就不叫 autoplay 只是一种 manual call api start video 的方式而已)
还有一个常用的是 rel,默认是 1,作用是在 pause 和 finished 后出现 related 的 videos(包括其它 channel 的 videos 哦)
refer:YouTube – hide "more videos" within youtube iframe when stop video
设定成 0 以后,它依然会出现 video,但是只局限在同一个的 channel 内。
YouTube Player API
它是一个 JS 的封装,底层依然是用上面的 iframe,只是多了一个交互沟通。iframe 沟通用的是 postMessage。
主页面是无法监听 iframe 里面的事件的,所以 parent child 必须有沟通逻辑,这个也只能是 YouTube 封装才办得到了。
所以,如果有想监听用户的交互行为就必须使用 Player API 了。比如监听用户按 pause 之类的。
引入 @types
yarn add @types/youtube --dev
注:不是 @types/youtube-player 哦。别搞错了。
HTML 和 JS 和 Google Maps JS API 几乎是同一种手法。
HTML
<body> <div id="player" style="width: 800px; height: auto; aspect-ratio: 16 / 9"></div> </body>
JavaScript
定义全局 init 方法。这个方法名是规定的。不可自定义哦。
declare global { interface Window { onYouTubeIframeAPIReady: () => void; } }
然后 append script
var tag = document.createElement("script"); tag.src = "https://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName("script")[0]!; firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag);
上面这段是抄来的,最好是是加上 defer 插入到 header。
最后是调用实例化 player。
export function onYouTubeIframeAPIReady() { const player = new YT.Player("player", { videoId: "wwUN9NWXcnY", playerVars: { playsinline: 1, autoplay: 1, }, events: { onReady: () => { console.log("ready"); }, onStateChange: (e) => { console.log("e", e); }, }, }); console.log("player", player); } window.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
知识点:
-
’player‘ 是 id,我们也可以自己 querySelector 然后传 element 给它
-
<div id="player"> 这个 element 会被 rebuild 成 iframe,如果我们想给这个 element 添加事件,记得要等 onReady 后才 addEventListener 哦。
onReady 后可以透过 player instance 的 getIframe() 方法获取到这个 element。
Angular YouTube Player 封装
参考 : Angular YouTube Player component
想深入了解如何封装, 可以参考 Angular 源码
YouTube Data API
建议大家先这 2 篇,基础我就不再教了。
Google Maps Embed API & JavaScript API(了解一下,什么是 API Keys)
Google – Reviews(了解一下,什么是 OAuth)
YouTube Data API 可以让我们通过 API 管理我们的 channel videos,但我个人只是用它来拿 YouTube 上 public videos 的资料而已。(e.g. Title, Duration, Thumbnail 等等)
Setup
1. enable YouTube Data API
2. create API Keys
由于这一次我是用 C# 去 request 这个 YouTube Data API,所以项目发布后 Key restrictions 选的是 IP addresses(或者选 None,但需要把 Key 藏好,比如放 Azure Key Vault)
平时是 JS 发 request 就选 Websites,本地测试时可以选 None。
3. setup OAuth
完整的 scopes 看这里。
我的需求只是查看 video 资料,有 scope https://www.googleapis.com/auth/youtube.readonly 就够了。
API Keys 和 OAuth 都需要要吗?
其实不需要,两个用其中一个就可以了。
像我只是想要拿 public video information,只需要 API Keys 就足够了。
但如果你是要操作特定的 channel 比如 manage video 等等,那才需要 OAuth。还有,倘若用了 OAuth 那就不需要再放 API Keys 了。
OAuth 可以替代 API Keys,但 API Keys 则不可以替代 OAuth 哦。
Request API
完整 API 文档看这里,里面还有 playground 可以玩,非常方便。
ASP.NET Core code sample
先获取到 access token(具体怎么弄,以前教过这里不重复)
var apiKey = "api key"; var videoId = "youtube video code from url query param v={code}"; var accessToken = "access token";
var httpRequestMessage = new HttpRequestMessage { RequestUri = new Uri($"https://www.googleapis.com/youtube/v3/videos?id={videoId}&key={apiKey}&part=snippet,contentDetails"), Method = HttpMethod.Get, Headers = { { "Accept", "application/json; charset=utf-8" }, { "Authorization", $"Bearer {accessToken}" } } }; var httpClient = httpClientFactory.CreateClient(); var response = await httpClient.SendAsync(httpRequestMessage); if (response.IsSuccessStatusCode) { var json = await response.Content.ReadAsStringAsync(); }
Response JSON

{ "kind": "youtube#videoListResponse", "etag": "gzfPTptt6zY7LjJygAHxPK33BaM", "items": [ { "kind": "youtube#video", "etag": "y4iRPPo9g6w--B6GYr0Nck4h1hA", "id": "5XK2C9w6oVk", "snippet": { "publishedAt": "2012-10-17T06:58:21Z", "channelId": "UCKUlsqazP-4QmxdEtUPlxOA", "title": "周杰倫 Jay Chou【愛在西元前 Love before BC】Official MV", "description": "周杰倫 愛在西元前 \n曲: 周杰倫 / 詞: 方文山\niTunes: https://itunes.apple.com/tw/album/ai-zai-xi-yuan-qian/id535739206?i=535739349\n\nJay Chou \"Love before BC\" (Ai Zai Xi Yuan Qian)\nSong & Lyrics: Jay Chou\nRecorded in Jay Chou's Fantasy album, released in 2001\n\n【上海 一九四三 完整MV】http://youtu.be/CcfnZOJpbM4\n【忍者 完整MV】http://youtu.be/55yJh4SHUBY\n【爸 我回來了 完整MV】http://youtu.be/nhyT8HDT4lg\n【威廉古堡 完整MV】http://youtu.be/lCzWCxVAkfc\n【開不了口 完整MV】http://youtu.be/H7hpK6cm-6k\n【對不起 完整MV】http://youtu.be/N2DkKFxijv0\n【雙截棍 完整MV】http://youtu.be/OR-0wptI_u0\n【簡單愛 完整MV】http://youtu.be/Y4xCVlyCvX4\n【安靜 完整MV】http://youtu.be/1hI-7vj2FhE\n【鍋牛 完整MV】http://youtu.be/H7pOrQEnc3c\n【你比從前快樂 完整MV】http://youtu.be/X_XqvQJi_6E\n【世界末日 完整MV】http://youtu.be/NDFULbHgL6E\n\n-----------------------------------------------------------------------------------------\n【Connection with Jay Chou 周杰倫相關網頁】\n◎ Jay Chou FACEBOOK(臉書): http://www.facebook.com/jay\n◎ Jay Chou YOUTUBE(視頻): http://www.youtube.com/JVRmuzic\n◎ JVR Music Official Site(杰威爾音樂): http://www.jvrmusic.com\n◎ iTunes(數位): https://itunes.apple.com/tw/album/shi-er-xin-zuo/id587743633?l=zh\n-----------------------------------------------------------------------------------------", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/5XK2C9w6oVk/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/5XK2C9w6oVk/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/5XK2C9w6oVk/hqdefault.jpg", "width": 480, "height": 360 }, "standard": { "url": "https://i.ytimg.com/vi/5XK2C9w6oVk/sddefault.jpg", "width": 640, "height": 480 } }, "channelTitle": "周杰倫 Jay Chou", "tags": [ "yt:crop=16:9", "周杰倫", "愛在西元前", "開不了口", "威廉古堡", "星晴", "mv", "周杰倫同名專輯", "徐若瑄", "Vivian", "官方", "完整版", "高清", "亞洲天王", "杰威爾", "流行音樂", "台灣", "jay", "chou", "Istanbul", "Yi", "Si", "Tan", "Bao", "HD", "official", "pop", "jvr", "music", "周杰伦", "周傑倫", "琴伤", "惊叹号", "亚洲天王", "杰威尔", "流行音乐", "台湾", "周杰伦同名专辑", "伊斯坦堡", "娘子", "完美主义", "简单爱", "开不了口", "爱在公元前" ], "categoryId": "10", "liveBroadcastContent": "none", "localized": { "title": "周杰倫 Jay Chou【愛在西元前 Love before BC】Official MV", "description": "周杰倫 愛在西元前 \n曲: 周杰倫 / 詞: 方文山\niTunes: https://itunes.apple.com/tw/album/ai-zai-xi-yuan-qian/id535739206?i=535739349\n\nJay Chou \"Love before BC\" (Ai Zai Xi Yuan Qian)\nSong & Lyrics: Jay Chou\nRecorded in Jay Chou's Fantasy album, released in 2001\n\n【上海 一九四三 完整MV】http://youtu.be/CcfnZOJpbM4\n【忍者 完整MV】http://youtu.be/55yJh4SHUBY\n【爸 我回來了 完整MV】http://youtu.be/nhyT8HDT4lg\n【威廉古堡 完整MV】http://youtu.be/lCzWCxVAkfc\n【開不了口 完整MV】http://youtu.be/H7hpK6cm-6k\n【對不起 完整MV】http://youtu.be/N2DkKFxijv0\n【雙截棍 完整MV】http://youtu.be/OR-0wptI_u0\n【簡單愛 完整MV】http://youtu.be/Y4xCVlyCvX4\n【安靜 完整MV】http://youtu.be/1hI-7vj2FhE\n【鍋牛 完整MV】http://youtu.be/H7pOrQEnc3c\n【你比從前快樂 完整MV】http://youtu.be/X_XqvQJi_6E\n【世界末日 完整MV】http://youtu.be/NDFULbHgL6E\n\n-----------------------------------------------------------------------------------------\n【Connection with Jay Chou 周杰倫相關網頁】\n◎ Jay Chou FACEBOOK(臉書): http://www.facebook.com/jay\n◎ Jay Chou YOUTUBE(視頻): http://www.youtube.com/JVRmuzic\n◎ JVR Music Official Site(杰威爾音樂): http://www.jvrmusic.com\n◎ iTunes(數位): https://itunes.apple.com/tw/album/shi-er-xin-zuo/id587743633?l=zh\n-----------------------------------------------------------------------------------------" } }, "contentDetails": { "duration": "PT3M59S", "dimension": "2d", "definition": "sd", "caption": "false", "licensedContent": true, "contentRating": {}, "projection": "rectangular" } } ], "pageInfo": { "totalResults": 1, "resultsPerPage": 1 } }
我需要的资料在这些位置
var title = items[0].snippet.title; var thumbnail = items[0].snippet.thumbnails.standard.url; var thumbnailHD = items[0].snippet.thumbnails.maxres.url; var duration = items[0].contentDetails.duration; // format is PT3M59S
其实 thumbnail 的位置是死的,即便不使用 Data API 也能知道。
https://i.ytimg.com/vi/{{ video_id }}/{{ image_quality }}.jpg https://img.youtube.com/vi/{{ video_id }}/{{ image-quality }}.jpg
比如:https://i.ytimg.com/vi/5XK2C9w6oVk/sddefault.jpg 或者 https://img.youtube.com/vi/De6rGdnwZvQ/maxresdefault.jpg
video_id = 5XK2C9w6oVk 或者 De6rGdnwZvQ
image-quality = sddefault (standard level) 或者 maxresdefault (高清,大约是 1280px)
Playlist API
补上几个拿 playlist 的 API。
get channel id
首先 playlist under channel,所以我们要先知道 channel id。
访问 channel 可以拿到 channel name,但这不是 id 哦。
要拿 id 可以透过这个 API
https://www.googleapis.com/youtube/v3/search?part=snippet&type=channel&q={ channel name }&key={ api key }
上面这个例子,channel name 是 “@JL-autogate”,把它放到 q=%40JL-autogate,q stand for query。
response
它可能会 query 到多个 items,我们透过 channelTitle 识别,然后就拿到 channelId 了。
get playlists
有了 channel id 就可以拿到 under 这个 channel 所有的 playlists 了。
https://www.googleapis.com/youtube/v3/playlists?part=snippet,contentDetails&maxResults=50&channelId={ channel id }&key={ api key }
maxResults 最多是 50,超过 50 就会有翻页,我们需要一个一个 page 拿。
response
一个 item 代表一个 playlist。常用的属性是 id, title, description, publishedAt 还有 contentDetails 的 itemCount,它的意思是这个 playlist 里有多少个 vides。
如果是有翻页,它会多一个 nextPageToken 属性
添加这个 nextPageToken 到 query params 就可以了
https://www.googleapis.com/youtube/v3/playlists?part=snippet,contentDetails&maxResults=50&channelId={ channel id }&pageToken={ next page token }&key={ api key }
get playlist videos
有了 playlist id 就可以拿出里面的 videos 了。
https://www.googleapis.com/youtube/v3/playlistItems?part=snippet,contentDetails&maxResults=2&playlistId={ play list id }&key={ api key }
response
大部分属性都有,但缺少了 video duration,如果想要,我们需要用拿 video by videoId 的 API 才行。(这里比较麻烦)
注:它也有 pagination 概念哦,也是透过 nextPageToken 来管理。
Etag の 运用
每个 response data 都会附带一个 etag 属性
etag 的作用是缓存。
2 个 response data etag 相同的话,代表从上一次缓存到现在,response data 都没有更改过。
我们在 send API request 时,可以加上 header "If-None-Match" 然后附上缓存的 etag value。
requestMessage.Headers.TryAddWithoutValidation("If-None-Match", "ZZFxiyE1q5MADlbZ0tiTrSoq-VU"); var response = await httpClient.SendAsync(requestMessage); if (response.StatusCode == System.Net.HttpStatusCode.NotModified) { // use cache }
如果 etag 没有变更,那 API 会 response status code 304 Not Modified,不会 response data。
这样的好处是减少了带宽,速度就快了。
但是,据说即便如此,API 仍然是算 quota 的,依然需要付费😔。
小心坑1
requestMessage.Headers.Add("If-None-Match", "ZZFxiyE1q5MADlbZ0tiTrSoq-VU")
这样会报错 -- The format of value 'ZZFxiyE1q5MADlbZ0tiTrSoq-VU' is invalid。
原因是 If-None-Match header 的 value 理应加上 double quote,像这样
requestMessage.Headers.Add("If-None-Match", "\"ZZFxiyE1q5MADlbZ0tiTrSoq-VU\"")
加了虽然不会再报错,但是你永远拿不到 304 response,我猜是因为 YouTube Data API 识别时是不加 double quote 的。
所以最终的做法是
requestMessage.Headers.TryAddWithoutValidation("If-None-Match", "ZZFxiyE1q5MADlbZ0tiTrSoq-VU");
顾名思义,TryAddWithoutValidation 就是 by pass validation,这样就不会报错。
小心坑2
另外,etag 是依据 response data generate 出来的,有点像前端打包,依据文件内容生成 hash file name。
比如说,我们拿 playlistitems
playlistItems?maxResults=2 playlistItems?maxResults=3
response 的数量不同,etag 就不同了,倒不是说里面的 3 个 videos 有变更。
要查看 video 是否变更,应该要依据 playlistitem 的 etag
还有哦,playlistItem 的 etag,和 video 的 etag 是不同的。
playlistItem 虽然有大部分 video 的属性 (title, description 等),但是没有 duration。
也就是说,如果 duration 变更了,我们靠 playlistItem etag 是无法感知到的,只有 video etag 才能感知到。
总之,etag 是依据 response data generate 出来的,response data 变了它就变,response data 以外的资料变,跟它无关。
比如说下面这个 playlist
如果 playlist 里面的某个 video 变更了,playlist etag 是不会变更的,会导致它变更的只有上面这个 response data item 的 data 改变,而 videos 不包含在里面。