Skip to content

Commit a6ea9db

Browse files
authored
🐛 [Story Preview] Support the max-video-preview meta tag (ampproject#38554)
* Iitial logic to parse max-video-preview meta tag * Create getPreviewDurationSeconds_() helper method * Finish maxVideoPreview logic, with unfinished comments * Lint * Simplify setupAutoAdvanceForPreview_() logic * Lint * Lint * Finish auto-advance-after tests
1 parent 2c147a9 commit a6ea9db

2 files changed

Lines changed: 141 additions & 23 deletions

File tree

extensions/amp-story/1.0/amp-story-page.js

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -367,31 +367,63 @@ export class AmpStoryPage extends AMP.BaseElement {
367367
* @private
368368
*/
369369
setupAutoAdvanceForPreview_() {
370-
let previewSecondsPerPage = parseInt(
371-
this.viewer_.getParam('previewSecondsPerPage'),
372-
10
373-
);
374-
if (isNaN(previewSecondsPerPage) || previewSecondsPerPage <= 0) {
375-
previewSecondsPerPage = DEFAULT_PREVIEW_AUTO_ADVANCE_DURATION_S;
376-
}
377-
this.element.setAttribute(
378-
'auto-advance-after',
379-
previewSecondsPerPage + 's'
380-
);
370+
let autoAdvanceAfter = this.getAutoAdvanceAfterSeconds_();
381371

382372
const firstVideo = this.getFirstAmpVideo_();
383-
if (firstVideo) {
384-
whenUpgradedToCustomElement(firstVideo)
385-
.then(() => firstVideo.getImpl())
386-
.then((videoImpl) => {
387-
this.loadPromise(firstVideo).then(() => {
388-
const duration = videoImpl.getDuration();
389-
const tooShort = duration < previewSecondsPerPage;
390-
const videoEl = firstVideo.querySelector('video');
391-
videoEl.loop ||= tooShort;
392-
});
393-
});
373+
if (!firstVideo) {
374+
this.element.setAttribute('auto-advance-after', autoAdvanceAfter + 's');
375+
return;
394376
}
377+
378+
const maxPrev = this.getMaxVideoPreview_();
379+
if (maxPrev > 0) {
380+
// Comply with max-video-preview, but never to lengthen the page preview
381+
autoAdvanceAfter = Math.min(maxPrev, autoAdvanceAfter);
382+
} else if (maxPrev === 0) {
383+
// TODO(masanto): Prevent video from playing when maxVideoPreview is 0
384+
}
385+
this.element.setAttribute('auto-advance-after', autoAdvanceAfter + 's');
386+
387+
whenUpgradedToCustomElement(firstVideo)
388+
.then(() => firstVideo.getImpl())
389+
.then((videoImpl) => {
390+
this.loadPromise(firstVideo).then(() => {
391+
const duration = videoImpl.getDuration();
392+
const tooShort = duration < autoAdvanceAfter;
393+
const videoEl = firstVideo.querySelector('video');
394+
videoEl.loop ||= tooShort;
395+
});
396+
});
397+
}
398+
399+
/**
400+
* Calculates the duration of this page's preview based upon the
401+
* 'previewSecondsPerPage' query parameter.
402+
* @return {number} The number of seconds for which this page should be
403+
* previewed before advancing to the next page.
404+
* @private
405+
*/
406+
getAutoAdvanceAfterSeconds_() {
407+
const previewSecondsStr = this.viewer_.getParam('previewSecondsPerPage');
408+
const previewSecondsPerPage = parseInt(previewSecondsStr, 10);
409+
return isNaN(previewSecondsPerPage) || previewSecondsPerPage <= 0
410+
? DEFAULT_PREVIEW_AUTO_ADVANCE_DURATION_S
411+
: previewSecondsPerPage;
412+
}
413+
414+
/**
415+
* @return {number} The max-video-preview value, if it exists on the doc. A
416+
* positive value means that a maximum of <value> seconds may be used as
417+
* a video snippet for videos on this page in search results. A value of
418+
* 0 means that a static image may be used. And a value of -1 means that
419+
* there is no limit to the video's preview length.
420+
* @private
421+
*/
422+
getMaxVideoPreview_() {
423+
const robotsContent = this.getAmpDoc().getMetaByName('robots');
424+
const maxVideoPreviewRegex = /max-video-preview[^,]*/;
425+
const maxVideoPreviewStr = robotsContent?.match(maxVideoPreviewRegex)[0];
426+
return parseInt(maxVideoPreviewStr?.split(':')[1], 10);
395427
}
396428

397429
/**

extensions/amp-story/1.0/test/test-amp-story-page.js

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const extensions = ['amp-story:1.0', 'amp-audio'];
2626

2727
describes.realWin('amp-story-page', {amp: {extensions}}, (env) => {
2828
let win;
29+
let ampDoc;
2930
let element;
3031
let html;
3132
let gridLayerEl;
@@ -76,7 +77,8 @@ describes.realWin('amp-story-page', {amp: {extensions}}, (env) => {
7677

7778
element = win.document.createElement('amp-story-page');
7879
gridLayerEl = win.document.createElement('amp-story-grid-layer');
79-
element.getAmpDoc = () => new AmpDocSingle(win);
80+
ampDoc = new AmpDocSingle(win);
81+
element.getAmpDoc = () => ampDoc;
8082
const signals = new Signals();
8183
element.signals = () => signals;
8284
element.appendChild(gridLayerEl);
@@ -805,4 +807,88 @@ describes.realWin('amp-story-page', {amp: {extensions}}, (env) => {
805807
});
806808
});
807809
});
810+
811+
describe('auto-advance-after', async () => {
812+
beforeEach(() => {
813+
env.sandbox.stub(ampDoc, 'isPreview').returns(true);
814+
expect(element.getAttribute('auto-advance-after')).to.be.equal(null);
815+
});
816+
817+
it(
818+
'should use the default advancement value when auto-advance-after is ' +
819+
'unspecified',
820+
() => {
821+
page.buildCallback();
822+
823+
expect(element.getAttribute('auto-advance-after')).to.be.equal('5s');
824+
}
825+
);
826+
827+
it('should use the specified previewSecondsPerPage value', () => {
828+
const viewer = Services.viewerForDoc(element);
829+
stubWithArg(viewer, 'getParam', 'previewSecondsPerPage', '3');
830+
831+
page.buildCallback();
832+
833+
expect(element.getAttribute('auto-advance-after')).to.be.equal('3s');
834+
});
835+
836+
it('should ignore max-video-preview when the page has no video', () => {
837+
const viewer = Services.viewerForDoc(element);
838+
stubWithArg(viewer, 'getParam', 'previewSecondsPerPage', '3');
839+
stubWithArg(ampDoc, 'getMetaByName', 'robots', 'max-video-preview: 2');
840+
841+
page.buildCallback();
842+
843+
expect(element.getAttribute('auto-advance-after')).to.be.equal('3s');
844+
});
845+
846+
it(
847+
'should override the previewSecondsPerPage value with the ' +
848+
'max-video-preview value',
849+
() => {
850+
const viewer = Services.viewerForDoc(element);
851+
stubWithArg(viewer, 'getParam', 'previewSecondsPerPage', '3');
852+
stubWithArg(ampDoc, 'getMetaByName', 'robots', 'max-video-preview: 2');
853+
appendAmpVideo();
854+
855+
page.buildCallback();
856+
857+
expect(element.getAttribute('auto-advance-after')).to.be.equal('2s');
858+
}
859+
);
860+
861+
it('should be unaffected by a max-video-preview value of -1', () => {
862+
const viewer = Services.viewerForDoc(element);
863+
stubWithArg(viewer, 'getParam', 'previewSecondsPerPage', '3');
864+
stubWithArg(ampDoc, 'getMetaByName', 'robots', 'max-video-preview: -1');
865+
appendAmpVideo();
866+
867+
page.buildCallback();
868+
869+
expect(element.getAttribute('auto-advance-after')).to.be.equal('3s');
870+
});
871+
872+
it('should be unaffected by a max-video-preview value of 0', () => {
873+
const viewer = Services.viewerForDoc(element);
874+
stubWithArg(viewer, 'getParam', 'previewSecondsPerPage', '3');
875+
stubWithArg(ampDoc, 'getMetaByName', 'robots', 'max-video-preview: 0');
876+
appendAmpVideo();
877+
878+
page.buildCallback();
879+
880+
expect(element.getAttribute('auto-advance-after')).to.be.equal('3s');
881+
});
882+
883+
function stubWithArg(object, functionName, arg, returnValue) {
884+
env.sandbox.stub(object, functionName).withArgs(arg).returns(returnValue);
885+
}
886+
887+
/**
888+
* Appends an AMP video to the document
889+
*/
890+
function appendAmpVideo() {
891+
gridLayerEl.appendChild(win.document.createElement('amp-video'));
892+
}
893+
});
808894
});

0 commit comments

Comments
 (0)