From 4362a4ecfe51989e8dbdd2961a6f0237fcbdda26 Mon Sep 17 00:00:00 2001 From: Mustafa Senoglu Date: Fri, 10 Jul 2026 13:21:54 +0300 Subject: [PATCH 1/6] feat(amp-youtube): add data-channelid to embed a channel's uploads Allows embedding a YouTube channel's uploads playlist via a new data-channelid attribute, mirroring the existing data-videoid and data-live-channelid datasources. The uploads playlist id is the channel id prefixed with "UU". - 0.1 (extensions/amp-youtube) and 1.0 (bento) now support data-channelid - validator: data-channelid added to the mandatory_oneof group - tests: 0.1 render test + updated datasource assertion + validator fixture - docs: documented data-channelid in amp-youtube.md Refs #26304 --- extensions/amp-youtube/0.1/amp-youtube.js | 26 ++++++++++++++++--- .../amp-youtube/0.1/test/test-amp-youtube.js | 14 ++++++++-- .../0.1/test/validator-amp-youtube.out | 4 +-- extensions/amp-youtube/amp-youtube.md | 4 +++ .../validator-amp-youtube.protoascii | 9 +++++-- .../bento-youtube/1.0/base-element.js | 1 + .../components/bento-youtube/1.0/component.js | 21 +++++++++++---- test/fixtures/errors.html | 2 +- 8 files changed, 65 insertions(+), 16 deletions(-) diff --git a/extensions/amp-youtube/0.1/amp-youtube.js b/extensions/amp-youtube/0.1/amp-youtube.js index 44bdb651d438..577742af1442 100644 --- a/extensions/amp-youtube/0.1/amp-youtube.js +++ b/extensions/amp-youtube/0.1/amp-youtube.js @@ -69,6 +69,9 @@ class AmpYoutube extends AMP.BaseElement { /** @private {?string} */ this.liveChannelid_ = null; + /** @private {?string} */ + this.channelid_ = null; + /** @private {?boolean} */ this.muted_ = false; @@ -137,6 +140,7 @@ class AmpYoutube extends AMP.BaseElement { buildCallback() { this.videoid_ = this.getVideoId_(); this.liveChannelid_ = this.getLiveChannelId_(); + this.channelid_ = this.getChannelId_(); this.assertDatasourceExists_(); const deferred = new Deferred(); @@ -156,7 +160,11 @@ class AmpYoutube extends AMP.BaseElement { const baseUrl = `https://www.youtube${urlSuffix}.com/embed/`; const descriptor = this.videoid_ ? `${encodeURIComponent(this.videoid_ || '')}?` - : `live_stream?channel=${encodeURIComponent(this.liveChannelid_ || '')}&`; + : this.liveChannelid_ + ? `live_stream?channel=${encodeURIComponent(this.liveChannelid_ || '')}&` + : // Channel embeds use the channel's uploads playlist. The uploads + // playlist id is the channel id prefixed with "UU". + `?listType=playlist&list=UU${encodeURIComponent(this.channelid_ || '')}&`; return `${baseUrl}${descriptor}enablejsapi=1&=1`; } @@ -331,6 +339,14 @@ class AmpYoutube extends AMP.BaseElement { return this.element.getAttribute('data-live-channelid'); } + /** + * @return {?string} + * @private + */ + getChannelId_() { + return this.element.getAttribute('data-channelid'); + } + /** * @return {?string} * @private @@ -353,11 +369,13 @@ class AmpYoutube extends AMP.BaseElement { assertDatasourceExists_() { const datasourceExists = !(this.videoid_ && this.liveChannelid_) && - (this.videoid_ || this.liveChannelid_); + !(this.videoid_ && this.channelid_) && + !(this.liveChannelid_ && this.channelid_) && + (this.videoid_ || this.liveChannelid_ || this.channelid_); userAssert( datasourceExists, - 'Exactly one of data-videoid or ' + - 'data-live-channelid should be present for %s', + 'Exactly one of data-videoid, data-live-channelid or ' + + 'data-channelid should be present for %s', this.element ); } diff --git a/extensions/amp-youtube/0.1/test/test-amp-youtube.js b/extensions/amp-youtube/0.1/test/test-amp-youtube.js index 8cb1570af503..3ba16bff7e9e 100644 --- a/extensions/amp-youtube/0.1/test/test-amp-youtube.js +++ b/extensions/amp-youtube/0.1/test/test-amp-youtube.js @@ -9,8 +9,10 @@ import {VideoEvents_Enum} from '../../../../src/video-interface'; const EXAMPLE_VIDEOID = 'mGENRKrdoGY'; const EXAMPLE_LIVE_CHANNELID = 'UCB8Kb4pxYzsDsHxzBfnid4Q'; +const EXAMPLE_CHANNELID = 'UCB8Kb4pxYzsDsHxzBfnid4Q'; const EXAMPLE_VIDEOID_URL = `https://www.youtube.com/embed/${EXAMPLE_VIDEOID}?enablejsapi=1&=1&playsinline=1`; const EXAMPLE_LIVE_CHANNELID_URL = `https://www.youtube.com/embed/live_stream?channel=${EXAMPLE_LIVE_CHANNELID}&enablejsapi=1&=1&playsinline=1`; +const EXAMPLE_CHANNELID_URL = `https://www.youtube.com/embed?listType=playlist&list=UU${EXAMPLE_CHANNELID}&enablejsapi=1&=1&playsinline=1`; const EXAMPLE_NO_COOKIE_VIDEOID_URL = `https://www.youtube-nocookie.com/embed/${EXAMPLE_VIDEOID}?enablejsapi=1&=1&playsinline=1`; describes.realWin( @@ -256,6 +258,14 @@ describes.realWin( expect(iframe.src).to.equal(EXAMPLE_LIVE_CHANNELID_URL); }); + it('renders for channel ids', async () => { + const yt = await getYt({'data-channelid': EXAMPLE_CHANNELID}); + const iframe = yt.querySelector('iframe'); + expect(iframe).to.not.be.null; + expect(iframe.tagName).to.equal('IFRAME'); + expect(iframe.src).to.equal(EXAMPLE_CHANNELID_URL); + }); + it('uses privacy-enhanced mode', async () => { const yt = await getYt({ 'data-videoid': EXAMPLE_VIDEOID, @@ -267,10 +277,10 @@ describes.realWin( expect(iframe.src).to.equal(EXAMPLE_NO_COOKIE_VIDEOID_URL); }); - it('requires data-videoid or data-live-channelid', () => { + it('requires data-videoid or data-live-channelid or data-channelid', () => { return allowConsoleError(() => { return getYt({}).should.eventually.be.rejectedWith( - /Exactly one of data-videoid or data-live-channelid should/ + /Exactly one of data-videoid, data-live-channelid or data-channelid should/ ); }); }); diff --git a/extensions/amp-youtube/0.1/test/validator-amp-youtube.out b/extensions/amp-youtube/0.1/test/validator-amp-youtube.out index 62d66446c09e..008e84477b7f 100644 --- a/extensions/amp-youtube/0.1/test/validator-amp-youtube.out +++ b/extensions/amp-youtube/0.1/test/validator-amp-youtube.out @@ -34,7 +34,7 @@ FAIL | | >> ^~~~~~~~~ -amp-youtube/0.1/test/validator-amp-youtube.html:34:2 The tag 'amp-youtube' is missing a mandatory attribute - pick one of ['data-live-channelid', 'data-videoid']. (see https://amp.dev/documentation/components/amp-youtube) +amp-youtube/0.1/test/validator-amp-youtube.html:34:2 The tag 'amp-youtube' is missing a mandatory attribute - pick one of ['data-live-channelid', 'data-videoid', 'data-channelid']. (see https://amp.dev/documentation/components/amp-youtube) | | >> ^~~~~~~~~ @@ -49,7 +49,7 @@ amp-youtube/0.1/test/validator-amp-youtube.html:39:2 The attribute 'data-videoid | | > ^~~~~~~~~ -amp-youtube/0.1/test/validator-amp-youtube.html:43:2 Mutually exclusive attributes encountered in tag 'amp-youtube' - pick one of ['data-live-channelid', 'data-videoid']. (see https://amp.dev/documentation/components/amp-youtube) +amp-youtube/0.1/test/validator-amp-youtube.html:43:2 Mutually exclusive attributes encountered in tag 'amp-youtube' - pick one of ['data-live-channelid', 'data-videoid', 'data-channelid']. (see https://amp.dev/documentation/components/amp-youtube) | data-live-channelid="UCB8Kb4pxYzsDsHxzBfnid4Q" | data-videoid="dQw4w9WgXcQ"> | diff --git a/extensions/amp-youtube/amp-youtube.md b/extensions/amp-youtube/amp-youtube.md index 36dadaf3f4c6..f3c414ff5375 100644 --- a/extensions/amp-youtube/amp-youtube.md +++ b/extensions/amp-youtube/amp-youtube.md @@ -223,6 +223,10 @@ For example, in this URL: `https://www.youtube.com/watch?v=Z1q71gFeRqM`, `Z1q71g The Youtube channel id that provides a stable livestream url. For example, in this URL: `https://www.youtube.com/embed/live_stream?channel=UCB8Kb4pxYzsDsHxzBfnid4Q`, `UCB8Kb4pxYzsDsHxzBfnid4Q` is the channel id. You can provide a `data-live-channelid` instead of a `data-videoid` attribute to embed a stable url for a live stream instead of a video. Channels do not come with default placeholders. You can provide a placeholder for the video per example 2 above. +### data-channelid + +The YouTube channel id whose uploads playlist should be embedded. For example, in this URL: `https://www.youtube.com/embed?listType=playlist&list=UUUB8Kb4pxYzsDsHxzBfnid4Q`, `UB8Kb4pxYzsDsHxzBfnid4Q` is the channel id (the uploads playlist id is the channel id prefixed with `UU`). You can provide a `data-channelid` instead of a `data-videoid` attribute to embed a channel's uploads instead of a single video. Channels do not come with default placeholders. You can provide a placeholder for the video per example 2 above. + ### data-param-\* All `data-param-*` attributes (with the exception of `data-param-autoplay` and `data-param-loop`) will be added as query parameter to the YouTube iframe src. This may be used to pass custom values through to YouTube plugins, such as whether to show controls. diff --git a/extensions/amp-youtube/validator-amp-youtube.protoascii b/extensions/amp-youtube/validator-amp-youtube.protoascii index 411cb89b679a..0eb52cedac30 100644 --- a/extensions/amp-youtube/validator-amp-youtube.protoascii +++ b/extensions/amp-youtube/validator-amp-youtube.protoascii @@ -41,12 +41,17 @@ tags: { # } attrs: { name: "data-live-channelid" - mandatory_oneof: "['data-live-channelid', 'data-videoid']" + mandatory_oneof: "['data-live-channelid', 'data-videoid', 'data-channelid']" value_regex: "[^=/?:]+" } attrs: { name: "data-videoid" - mandatory_oneof: "['data-live-channelid', 'data-videoid']" + mandatory_oneof: "['data-live-channelid', 'data-videoid', 'data-channelid']" + value_regex: "[^=/?:]+" + } + attrs: { + name: "data-channelid" + mandatory_oneof: "['data-live-channelid', 'data-videoid', 'data-channelid']" value_regex: "[^=/?:]+" } attrs: { diff --git a/src/bento/components/bento-youtube/1.0/base-element.js b/src/bento/components/bento-youtube/1.0/base-element.js index e3b2af7bacd2..e4ff5bf16b81 100644 --- a/src/bento/components/bento-youtube/1.0/base-element.js +++ b/src/bento/components/bento-youtube/1.0/base-element.js @@ -19,6 +19,7 @@ BaseElement['props'] = { 'controls': {attr: 'controls', type: 'boolean'}, 'videoid': {attr: 'data-videoid'}, 'liveChannelid': {attr: 'data-live-channelid'}, + 'channelid': {attr: 'data-channelid'}, 'dock': {attr: 'dock', media: true}, 'credentials': {attr: 'credentials'}, // TODO(wg-components): Current behavior defaults to loading="auto". diff --git a/src/bento/components/bento-youtube/1.0/component.js b/src/bento/components/bento-youtube/1.0/component.js index 46d71467a392..88814eb3ed52 100644 --- a/src/bento/components/bento-youtube/1.0/component.js +++ b/src/bento/components/bento-youtube/1.0/component.js @@ -69,6 +69,7 @@ function createDefaultInfo() { function BentoYoutubeWithRef( { autoplay, + channelid, credentials, liveChannelid, loop, @@ -80,15 +81,19 @@ function BentoYoutubeWithRef( ref ) { const datasourceExists = - !(videoid && liveChannelid) && (videoid || liveChannelid); + !(videoid && liveChannelid) && + !(videoid && channelid) && + !(liveChannelid && channelid) && + (videoid || liveChannelid || channelid); if (!datasourceExists) { throw new Error( - 'Exactly one of data-videoid or data-live-channelid should be present for ' + 'Exactly one of data-videoid, data-live-channelid or ' + + 'data-channelid should be present for ' ); } - let src = getEmbedUrl(credentials, videoid, liveChannelid); + let src = getEmbedUrl(credentials, videoid, liveChannelid, channelid); if (!('playsinline' in params)) { params['playsinline'] = '1'; } @@ -205,7 +210,7 @@ function BentoYoutubeWithRef( * @return {string} * @private */ -function getEmbedUrl(credentials, videoid, liveChannelid) { +function getEmbedUrl(credentials, videoid, liveChannelid, channelid) { let urlSuffix = ''; if (credentials === 'omit') { urlSuffix = '-nocookie'; @@ -214,10 +219,16 @@ function getEmbedUrl(credentials, videoid, liveChannelid) { let descriptor = ''; if (videoid) { descriptor = `${encodeURIComponent(videoid)}?`; - } else { + } else if (liveChannelid) { descriptor = `live_stream?channel=${encodeURIComponent( liveChannelid || '' )}&`; + } else { + // Channel embeds use the channel's uploads playlist. The uploads + // playlist id is the channel id prefixed with "UU". + descriptor = `?listType=playlist&list=UU${encodeURIComponent( + channelid || '' + )}&`; } return `${baseUrl}${descriptor}enablejsapi=1&=1`; } diff --git a/test/fixtures/errors.html b/test/fixtures/errors.html index 63e29225ac45..8070924e039e 100644 --- a/test/fixtures/errors.html +++ b/test/fixtures/errors.html @@ -24,7 +24,7 @@ From 7369906f98d19cd4c051c77bad7564b2b1d8a4d7 Mon Sep 17 00:00:00 2001 From: Mustafa Senoglu Date: Fri, 10 Jul 2026 14:36:53 +0300 Subject: [PATCH 2/6] test(amp-youtube): fix channel URL slash and update 1.0 validator .out - EXAMPLE_CHANNELID_URL should use embed/? (baseUrl ends with slash) - update extensions/amp-youtube/1.0/test/validator-amp-youtube.out error messages to include data-channelid in the mandatory_oneof list --- extensions/amp-youtube/0.1/test/test-amp-youtube.js | 2 +- extensions/amp-youtube/1.0/test/validator-amp-youtube.out | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/amp-youtube/0.1/test/test-amp-youtube.js b/extensions/amp-youtube/0.1/test/test-amp-youtube.js index 3ba16bff7e9e..d1406000e028 100644 --- a/extensions/amp-youtube/0.1/test/test-amp-youtube.js +++ b/extensions/amp-youtube/0.1/test/test-amp-youtube.js @@ -12,7 +12,7 @@ const EXAMPLE_LIVE_CHANNELID = 'UCB8Kb4pxYzsDsHxzBfnid4Q'; const EXAMPLE_CHANNELID = 'UCB8Kb4pxYzsDsHxzBfnid4Q'; const EXAMPLE_VIDEOID_URL = `https://www.youtube.com/embed/${EXAMPLE_VIDEOID}?enablejsapi=1&=1&playsinline=1`; const EXAMPLE_LIVE_CHANNELID_URL = `https://www.youtube.com/embed/live_stream?channel=${EXAMPLE_LIVE_CHANNELID}&enablejsapi=1&=1&playsinline=1`; -const EXAMPLE_CHANNELID_URL = `https://www.youtube.com/embed?listType=playlist&list=UU${EXAMPLE_CHANNELID}&enablejsapi=1&=1&playsinline=1`; +const EXAMPLE_CHANNELID_URL = `https://www.youtube.com/embed/?listType=playlist&list=UU${EXAMPLE_CHANNELID}&enablejsapi=1&=1&playsinline=1`; const EXAMPLE_NO_COOKIE_VIDEOID_URL = `https://www.youtube-nocookie.com/embed/${EXAMPLE_VIDEOID}?enablejsapi=1&=1&playsinline=1`; describes.realWin( diff --git a/extensions/amp-youtube/1.0/test/validator-amp-youtube.out b/extensions/amp-youtube/1.0/test/validator-amp-youtube.out index 206af6806f00..6bdf112f854f 100644 --- a/extensions/amp-youtube/1.0/test/validator-amp-youtube.out +++ b/extensions/amp-youtube/1.0/test/validator-amp-youtube.out @@ -42,7 +42,7 @@ FAIL | | >> ^~~~~~~~~ -amp-youtube/1.0/test/validator-amp-youtube.html:42:4 The tag 'amp-youtube' is missing a mandatory attribute - pick one of ['data-live-channelid', 'data-videoid']. (see https://amp.dev/documentation/components/amp-youtube) +amp-youtube/1.0/test/validator-amp-youtube.html:42:4 The tag 'amp-youtube' is missing a mandatory attribute - pick one of ['data-live-channelid', 'data-videoid', 'data-channelid']. (see https://amp.dev/documentation/components/amp-youtube) | | | @@ -62,7 +62,7 @@ amp-youtube/1.0/test/validator-amp-youtube.html:48:4 The attribute 'data-videoid | | > ^~~~~~~~~ -amp-youtube/1.0/test/validator-amp-youtube.html:56:4 Mutually exclusive attributes encountered in tag 'amp-youtube' - pick one of ['data-live-channelid', 'data-videoid']. (see https://amp.dev/documentation/components/amp-youtube) +amp-youtube/1.0/test/validator-amp-youtube.html:56:4 Mutually exclusive attributes encountered in tag 'amp-youtube' - pick one of ['data-live-channelid', 'data-videoid', 'data-channelid']. (see https://amp.dev/documentation/components/amp-youtube) | width="480" | height="270" | data-live-channelid="UCB8Kb4pxYzsDsHxzBfnid4Q" From 5db8a0a9957a4553ae5eac88a577924bd8080913 Mon Sep 17 00:00:00 2001 From: mmustafasenoglu Date: Mon, 27 Jul 2026 21:53:55 +0300 Subject: [PATCH 3/6] test(amp-youtube): add channelid mutual exclusivity and no-cookie tests for codecov --- .../amp-youtube/0.1/test/test-amp-youtube.js | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/extensions/amp-youtube/0.1/test/test-amp-youtube.js b/extensions/amp-youtube/0.1/test/test-amp-youtube.js index d1406000e028..7a18758a32d2 100644 --- a/extensions/amp-youtube/0.1/test/test-amp-youtube.js +++ b/extensions/amp-youtube/0.1/test/test-amp-youtube.js @@ -277,7 +277,7 @@ describes.realWin( expect(iframe.src).to.equal(EXAMPLE_NO_COOKIE_VIDEOID_URL); }); - it('requires data-videoid or data-live-channelid or data-channelid', () => { + it('requires exactly one datasource (empty)', () => { return allowConsoleError(() => { return getYt({}).should.eventually.be.rejectedWith( /Exactly one of data-videoid, data-live-channelid or data-channelid should/ @@ -285,6 +285,52 @@ describes.realWin( }); }); + it('rejects data-videoid and data-channelid together', () => { + return allowConsoleError(() => { + return getYt({ + 'data-videoid': EXAMPLE_VIDEOID, + 'data-channelid': EXAMPLE_CHANNELID, + }).should.eventually.be.rejectedWith( + /Exactly one of data-videoid, data-live-channelid or data-channelid should/ + ); + }); + }); + + it('rejects data-live-channelid and data-channelid together', () => { + return allowConsoleError(() => { + return getYt({ + 'data-live-channelid': EXAMPLE_LIVE_CHANNELID, + 'data-channelid': EXAMPLE_CHANNELID, + }).should.eventually.be.rejectedWith( + /Exactly one of data-videoid, data-live-channelid or data-channelid should/ + ); + }); + }); + + it('rejects all three datasources together', () => { + return allowConsoleError(() => { + return getYt({ + 'data-videoid': EXAMPLE_VIDEOID, + 'data-live-channelid': EXAMPLE_LIVE_CHANNELID, + 'data-channelid': EXAMPLE_CHANNELID, + }).should.eventually.be.rejectedWith( + /Exactly one of data-videoid, data-live-channelid or data-channelid should/ + ); + }); + }); + + it('uses no-cookie mode for channel embeds', async () => { + const yt = await getYt({ + 'data-channelid': EXAMPLE_CHANNELID, + 'credentials': 'omit', + }); + const iframe = yt.querySelector('iframe'); + expect(iframe).to.not.be.null; + expect(iframe.src).to.contain('youtube-nocookie.com'); + expect(iframe.src).to.contain('listType=playlist'); + expect(iframe.src).to.contain('list=UU' + EXAMPLE_CHANNELID); + }); + it('adds an img placeholder in prerender mode if source is videoid', async () => { const yt = await getYt( {'data-videoid': EXAMPLE_VIDEOID}, From e3d8d91fbf168e4518e9da956e56d8716d836cac Mon Sep 17 00:00:00 2001 From: Mustafa Senoglu Date: Thu, 13 Aug 2026 22:31:16 +0300 Subject: [PATCH 4/6] fix(amp-youtube): derive uploads playlist id by replacing leading UC with UU The uploads playlist id for a channel is formed by replacing the leading "UC" with "UU" (UCB8... -> UUB8...), not by prefixing "UU" which produced an invalid id (UUUC...). --- extensions/amp-youtube/0.1/amp-youtube.js | 7 +++++-- extensions/amp-youtube/0.1/test/test-amp-youtube.js | 4 ++-- extensions/amp-youtube/amp-youtube.md | 2 +- src/bento/components/bento-youtube/1.0/component.js | 6 ++++-- src/bento/components/bento-youtube/1.0/component.type.js | 1 + 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/extensions/amp-youtube/0.1/amp-youtube.js b/extensions/amp-youtube/0.1/amp-youtube.js index 577742af1442..246719433029 100644 --- a/extensions/amp-youtube/0.1/amp-youtube.js +++ b/extensions/amp-youtube/0.1/amp-youtube.js @@ -163,8 +163,11 @@ class AmpYoutube extends AMP.BaseElement { : this.liveChannelid_ ? `live_stream?channel=${encodeURIComponent(this.liveChannelid_ || '')}&` : // Channel embeds use the channel's uploads playlist. The uploads - // playlist id is the channel id prefixed with "UU". - `?listType=playlist&list=UU${encodeURIComponent(this.channelid_ || '')}&`; + // playlist id is derived from the channel id by replacing the + // leading "UC" with "UU". + `?listType=playlist&list=UU${encodeURIComponent( + (this.channelid_ || '').replace(/^UC/, '') + )}&`; return `${baseUrl}${descriptor}enablejsapi=1&=1`; } diff --git a/extensions/amp-youtube/0.1/test/test-amp-youtube.js b/extensions/amp-youtube/0.1/test/test-amp-youtube.js index 7a18758a32d2..801ffcf0255f 100644 --- a/extensions/amp-youtube/0.1/test/test-amp-youtube.js +++ b/extensions/amp-youtube/0.1/test/test-amp-youtube.js @@ -12,7 +12,7 @@ const EXAMPLE_LIVE_CHANNELID = 'UCB8Kb4pxYzsDsHxzBfnid4Q'; const EXAMPLE_CHANNELID = 'UCB8Kb4pxYzsDsHxzBfnid4Q'; const EXAMPLE_VIDEOID_URL = `https://www.youtube.com/embed/${EXAMPLE_VIDEOID}?enablejsapi=1&=1&playsinline=1`; const EXAMPLE_LIVE_CHANNELID_URL = `https://www.youtube.com/embed/live_stream?channel=${EXAMPLE_LIVE_CHANNELID}&enablejsapi=1&=1&playsinline=1`; -const EXAMPLE_CHANNELID_URL = `https://www.youtube.com/embed/?listType=playlist&list=UU${EXAMPLE_CHANNELID}&enablejsapi=1&=1&playsinline=1`; +const EXAMPLE_CHANNELID_URL = `https://www.youtube.com/embed/?listType=playlist&list=UU${EXAMPLE_CHANNELID.slice(2)}&enablejsapi=1&=1&playsinline=1`; const EXAMPLE_NO_COOKIE_VIDEOID_URL = `https://www.youtube-nocookie.com/embed/${EXAMPLE_VIDEOID}?enablejsapi=1&=1&playsinline=1`; describes.realWin( @@ -328,7 +328,7 @@ describes.realWin( expect(iframe).to.not.be.null; expect(iframe.src).to.contain('youtube-nocookie.com'); expect(iframe.src).to.contain('listType=playlist'); - expect(iframe.src).to.contain('list=UU' + EXAMPLE_CHANNELID); + expect(iframe.src).to.contain('list=UU' + EXAMPLE_CHANNELID.slice(2)); }); it('adds an img placeholder in prerender mode if source is videoid', async () => { diff --git a/extensions/amp-youtube/amp-youtube.md b/extensions/amp-youtube/amp-youtube.md index f3c414ff5375..64b045d81aa8 100644 --- a/extensions/amp-youtube/amp-youtube.md +++ b/extensions/amp-youtube/amp-youtube.md @@ -225,7 +225,7 @@ The Youtube channel id that provides a stable livestream url. For example, in th ### data-channelid -The YouTube channel id whose uploads playlist should be embedded. For example, in this URL: `https://www.youtube.com/embed?listType=playlist&list=UUUB8Kb4pxYzsDsHxzBfnid4Q`, `UB8Kb4pxYzsDsHxzBfnid4Q` is the channel id (the uploads playlist id is the channel id prefixed with `UU`). You can provide a `data-channelid` instead of a `data-videoid` attribute to embed a channel's uploads instead of a single video. Channels do not come with default placeholders. You can provide a placeholder for the video per example 2 above. +The YouTube channel id whose uploads playlist should be embedded. For example, in this URL: `https://www.youtube.com/embed?listType=playlist&list=UUB8Kb4pxYzsDsHxzBfnid4Q`, `UCB8Kb4pxYzsDsHxzBfnid4Q` is the channel id (the uploads playlist id is derived from the channel id by replacing the leading `UC` with `UU`). You can provide a `data-channelid` instead of a `data-videoid` attribute to embed a channel's uploads instead of a single video. Channels do not come with default placeholders. You can provide a placeholder for the video per example 2 above. ### data-param-\* diff --git a/src/bento/components/bento-youtube/1.0/component.js b/src/bento/components/bento-youtube/1.0/component.js index 88814eb3ed52..71f1d4b73d8f 100644 --- a/src/bento/components/bento-youtube/1.0/component.js +++ b/src/bento/components/bento-youtube/1.0/component.js @@ -207,6 +207,7 @@ function BentoYoutubeWithRef( * @param {string} credentials * @param {string} videoid * @param {string} liveChannelid + * @param {string} channelid * @return {string} * @private */ @@ -225,9 +226,10 @@ function getEmbedUrl(credentials, videoid, liveChannelid, channelid) { )}&`; } else { // Channel embeds use the channel's uploads playlist. The uploads - // playlist id is the channel id prefixed with "UU". + // playlist id is derived from the channel id by replacing the + // leading "UC" with "UU". descriptor = `?listType=playlist&list=UU${encodeURIComponent( - channelid || '' + (channelid || '').replace(/^UC/, '') )}&`; } return `${baseUrl}${descriptor}enablejsapi=1&=1`; diff --git a/src/bento/components/bento-youtube/1.0/component.type.js b/src/bento/components/bento-youtube/1.0/component.type.js index 301388efe14c..412e1033c32b 100644 --- a/src/bento/components/bento-youtube/1.0/component.type.js +++ b/src/bento/components/bento-youtube/1.0/component.type.js @@ -7,6 +7,7 @@ * onLoad: (function():undefined|undefined), * videoid: (string|undefined), * liveChannelid: (string|undefined), + * channelid: (string|undefined), * params: Object, * credentials: (string|undefined), * }} From 3555e7bcd4343522a4cad047fe5959b79777a420 Mon Sep 17 00:00:00 2001 From: mmustafasenoglu Date: Wed, 19 Aug 2026 11:44:49 +0300 Subject: [PATCH 5/6] fix(amp-youtube): fix prettier formatting in getEmbedUrl_ --- extensions/amp-youtube/0.1/amp-youtube.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/extensions/amp-youtube/0.1/amp-youtube.js b/extensions/amp-youtube/0.1/amp-youtube.js index 246719433029..a232af4a46a9 100644 --- a/extensions/amp-youtube/0.1/amp-youtube.js +++ b/extensions/amp-youtube/0.1/amp-youtube.js @@ -159,15 +159,15 @@ class AmpYoutube extends AMP.BaseElement { const urlSuffix = this.getCredentials_() === 'omit' ? '-nocookie' : ''; const baseUrl = `https://www.youtube${urlSuffix}.com/embed/`; const descriptor = this.videoid_ - ? `${encodeURIComponent(this.videoid_ || '')}?` - : this.liveChannelid_ - ? `live_stream?channel=${encodeURIComponent(this.liveChannelid_ || '')}&` - : // Channel embeds use the channel's uploads playlist. The uploads - // playlist id is derived from the channel id by replacing the - // leading "UC" with "UU". - `?listType=playlist&list=UU${encodeURIComponent( - (this.channelid_ || '').replace(/^UC/, '') - )}&`; + ? `${encodeURIComponent(this.videoid_ || '')}?` + : this.liveChannelid_ + ? `live_stream?channel=${encodeURIComponent(this.liveChannelid_ || '')}&` + : // Channel embeds use the channel's uploads playlist. The uploads + // playlist id is derived from the channel id by replacing the + // leading "UC" with "UU". + `?listType=playlist&list=UU${encodeURIComponent( + (this.channelid_ || '').replace(/^UC/, '') + )}&`; return `${baseUrl}${descriptor}enablejsapi=1&=1`; } From 3fb23317637b5c8a54be7ddfaddd97ca3dddbbae Mon Sep 17 00:00:00 2001 From: mmustafasenoglu Date: Wed, 19 Aug 2026 11:49:37 +0300 Subject: [PATCH 6/6] fix(amp-youtube): fix prettier formatting in getEmbedUrl_ --- extensions/amp-youtube/0.1/amp-youtube.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/extensions/amp-youtube/0.1/amp-youtube.js b/extensions/amp-youtube/0.1/amp-youtube.js index a232af4a46a9..6e972519dd44 100644 --- a/extensions/amp-youtube/0.1/amp-youtube.js +++ b/extensions/amp-youtube/0.1/amp-youtube.js @@ -159,15 +159,15 @@ class AmpYoutube extends AMP.BaseElement { const urlSuffix = this.getCredentials_() === 'omit' ? '-nocookie' : ''; const baseUrl = `https://www.youtube${urlSuffix}.com/embed/`; const descriptor = this.videoid_ - ? `${encodeURIComponent(this.videoid_ || '')}?` - : this.liveChannelid_ - ? `live_stream?channel=${encodeURIComponent(this.liveChannelid_ || '')}&` - : // Channel embeds use the channel's uploads playlist. The uploads - // playlist id is derived from the channel id by replacing the - // leading "UC" with "UU". - `?listType=playlist&list=UU${encodeURIComponent( - (this.channelid_ || '').replace(/^UC/, '') - )}&`; + ? `${encodeURIComponent(this.videoid_ || '')}?` + : this.liveChannelid_ + ? `live_stream?channel=${encodeURIComponent(this.liveChannelid_ || '')}&` + : // Channel embeds use the channel's uploads playlist. The uploads + // playlist id is derived from the channel id by replacing the + // leading "UC" with "UU". + `?listType=playlist&list=UU${encodeURIComponent( + (this.channelid_ || '').replace(/^UC/, '') + )}&`; return `${baseUrl}${descriptor}enablejsapi=1&=1`; }