Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ [Story Video] Connect CacheUrl service to amp-video and load sources #33466

Merged
merged 55 commits into from
Apr 21, 2021
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
b077192
Added canonical to videos example
mszylkowski Mar 17, 2021
f657181
Merge branch 'master' of github.com:ampproject/amphtml into ampvideo_…
mszylkowski Mar 22, 2021
4687f9c
Started video cache
mszylkowski Mar 22, 2021
e6d122e
Use cdn url from url-impl
mszylkowski Mar 22, 2021
41ae575
Remove unnecessary | null
mszylkowski Mar 22, 2021
e0cd517
Undo videos.html
mszylkowski Mar 22, 2021
f5e3f8a
Merge branch 'master' of github.com:ampproject/amphtml into ampvideo_…
mszylkowski Mar 22, 2021
ff63db3
Remove console.log
mszylkowski Mar 22, 2021
d85dac3
Using ternary operator for reduce
mszylkowski Mar 22, 2021
a1aef44
Account for cached docs
mszylkowski Mar 22, 2021
4db83f6
Update logic with toolbox
mszylkowski Mar 22, 2021
59890a2
Merge branch 'master' of github.com:ampproject/amphtml into ampvideo_…
mszylkowski Mar 23, 2021
3f853a2
Added service
mszylkowski Mar 23, 2021
dc7d799
Undo amp-video
mszylkowski Mar 23, 2021
54b70b7
Added cache url service
mszylkowski Mar 23, 2021
164c99c
Added owners
mszylkowski Mar 23, 2021
19f5e3a
Cache url to video
mszylkowski Mar 23, 2021
095c0eb
Simplified cache example
mszylkowski Mar 23, 2021
46a9c5d
Updated title
mszylkowski Mar 23, 2021
f3fc5d5
Changed name to amp-cache-url of folder
mszylkowski Mar 23, 2021
ba110c1
Merge branch 'master' of github.com:ampproject/amphtml into ampvideo_…
mszylkowski Mar 23, 2021
9137b8a
Update readme
mszylkowski Mar 23, 2021
98c7d03
Update comments
mszylkowski Mar 23, 2021
cd45a21
Remove example
mszylkowski Mar 23, 2021
638499d
Updated readme
mszylkowski Mar 23, 2021
8fcbd8f
Merge branch 'master' of github.com:ampproject/amphtml into ampvideo_…
mszylkowski Mar 24, 2021
0d666a2
Merge branch 'master' of github.com:ampproject/amphtml into ampvideo_…
mszylkowski Mar 24, 2021
8f26de6
Added code
mszylkowski Mar 24, 2021
197d7a7
Fixed tests
mszylkowski Mar 24, 2021
cbd69bd
Removed comment
mszylkowski Mar 24, 2021
2e8eea9
Added some more tests
mszylkowski Mar 25, 2021
6d06b7d
Fixed cdnurl
mszylkowski Mar 25, 2021
e4cbe52
Update names of docSupports
mszylkowski Mar 25, 2021
8f2a336
Fix cacheUrl not replacing
mszylkowski Mar 25, 2021
62b866f
updated cacheDomain param name
mszylkowski Mar 25, 2021
bec5233
Check not cached doc to add cache sources
mszylkowski Mar 25, 2021
be109e5
improved implementation from comments
mszylkowski Mar 29, 2021
76c740f
added tests
mszylkowski Mar 29, 2021
327afc5
Added gregable file on examples
mszylkowski Mar 31, 2021
62447cd
Merge branch 'master' of github.com:ampproject/amphtml into ampvideo_…
mszylkowski Mar 31, 2021
2f010c0
Added warning
mszylkowski Mar 31, 2021
dafc2dd
Uppercase user error
mszylkowski Mar 31, 2021
7b1405a
Updated opt in
mszylkowski Apr 8, 2021
e3a7568
Fix response.json is promise
mszylkowski Apr 12, 2021
678d3b3
Updated test
mszylkowski Apr 12, 2021
53ff56f
Merge branch 'main' of github.com:ampproject/amphtml into ampvideo_ca…
mszylkowski Apr 14, 2021
2c44190
Fixed toArray import
mszylkowski Apr 14, 2021
1674173
Merge branch 'main' of github.com:ampproject/amphtml into ampvideo_ca…
mszylkowski Apr 19, 2021
a5ff5ed
Fixed visibilityState
mszylkowski Apr 19, 2021
157da69
Use helper functions
mszylkowski Apr 19, 2021
96e3548
Added tests to amp-cache-url
mszylkowski Apr 19, 2021
28f4faf
Update extensions/amp-cache-url/0.1/test/test-amp-cache-url.js
mszylkowski Apr 19, 2021
e990de2
Using extensionScriptInNode with win
mszylkowski Apr 20, 2021
2c125bd
Updated tests to match comments
mszylkowski Apr 20, 2021
c39e9ec
Not export applySources
mszylkowski Apr 21, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build-system/test-configs/dep-check-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ exports.rules = [
'src/service/extension-script.js',
'extensions/amp-video/0.1/amp-video.js->' +
'src/service/video-manager-impl.js',
'extensions/amp-video/0.1/video-cache.js->' +
'src/service/extension-script.js',
'extensions/amp-video-iframe/0.1/amp-video-iframe.js->' +
'src/service/video-manager-impl.js',
'extensions/amp-ooyala-player/0.1/amp-ooyala-player.js->' +
Expand Down
76 changes: 76 additions & 0 deletions examples/amp-story/videos-google-cache.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<!doctype html>
<html ⚡ lang="en">
<head>
<meta charset="utf-8">
<link rel="canonical" href="https://amp.dev/hello/ampconf.html">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<script async src="https://cdn.ampproject.org/v0.js"></script>
<title>Example story with Google cache videos</title>

<script async custom-element="amp-story" src="https://cdn.ampproject.org/v0/amp-story-1.0.js"></script>
<script async custom-element="amp-video" src="https://cdn.ampproject.org/v0/amp-video-0.1.js"></script>
<script async custom-element="amp-cache-url" src="https://cdn.ampproject.org/v0/amp-cache-url-0.1.js"></script>

<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
<style amp-custom>
.page-num {
display: block;
position: absolute;
left: 30px;
top: 30px;
width: 30px;
text-align: center;
font-family: sans-serif;
background-color: white;
border-radius: 50%;
}
</style>
</head>
<body>
<amp-story standalone id="cats"
title="Key Highlights of AMP Conf 2018" publisher="The AMP team">

<amp-story-page id="page-1">
<amp-story-grid-layer template="fill">
<amp-video autoplay loop cache="google"
id="video1"
width="400"
height="750"
poster="img-city1.jpeg#1"
layout="fill">
<source src="./video/stamp.mp4" type="video/mp4">
</amp-video>
</amp-story-grid-layer>
<amp-story-grid-layer><span class="page-num">1</span></amp-story-grid-layer>
</amp-story-page>

<amp-story-page id="page-2">
<amp-story-grid-layer template="fill">
<amp-video autoplay loop cache="google"
id="video2"
width="400"
height="750"
poster="img-city1.jpeg#2"
layout="fill">
<source src="./video/stamp-animation.mp4" type="video/mp4">
</amp-video>
</amp-story-grid-layer>
<amp-story-grid-layer><span class="page-num">2</span></amp-story-grid-layer>
</amp-story-page>

<amp-story-page id="page-3">
<amp-story-grid-layer template="fill">
<amp-video autoplay loop cache="google"
id="video2"
width="400"
height="750"
poster="img-city1.jpeg#2"
layout="fill">
<source src="https://gregable.com/re2ae3Ei/sample-mp4-file.mp4?amp_video_host_url=gregable.com" type="video/mp4">
</amp-video>
</amp-story-grid-layer>
<amp-story-grid-layer><span class="page-num">2</span></amp-story-grid-layer>
</amp-story-page>
</amp-story>
</body>
</html>
16 changes: 16 additions & 0 deletions extensions/amp-cache-url/0.1/amp-cache-url.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,27 @@
* limitations under the License.
*/

import * as ampToolboxCacheUrl from '@ampproject/toolbox-cache-url';
import {urls} from '../../../src/config';

export class AmpCacheUrlService {
/**
* Create cache url service
*/
constructor() {}

/**
*
* @param {string} url
* @param {string=} cacheDomain the cache domain name (eg: cdn.approject.org)
* @return {!Promise<string>}
*/
createCacheUrl(url, cacheDomain = urls.cdn) {
return ampToolboxCacheUrl.createCacheUrl(
cacheDomain.replace(/https?:\/\//, ''),
url
);
}
}

AMP.extension('amp-cache-url', '0.1', (AMP) => {
Expand Down
28 changes: 28 additions & 0 deletions extensions/amp-cache-url/0.1/test/test-amp-cache-url.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {AmpCacheUrlService} from '../amp-cache-url';

describes.fakeWin(
'amp-cache-url',
{amp: {extensions: ['amp-cache-url']}},
() => {
it('should return a cached url', async () => {
const cacheUrlService = new AmpCacheUrlService();
const result = await cacheUrlService.createCacheUrl(
'https://amp.dev/stories'
);
expect(result).to.equal(
'https://amp-dev.cdn.ampproject.org/c/s/amp.dev/stories'
);
});

it('should not throw with empty url', async () => {
const cacheUrlService = new AmpCacheUrlService();
expect(cacheUrlService.createCacheUrl('')).to.not.be.rejected;
});

it('should not throw with invalid url', async () => {
const cacheUrlService = new AmpCacheUrlService();
expect(cacheUrlService.createCacheUrl('invalid url')).to.not.be.rejected;
});
}
);
9 changes: 8 additions & 1 deletion extensions/amp-video/0.1/amp-video.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
} from '../../../src/dom';
import {descendsFromStory} from '../../../src/utils/story';
import {dev, devAssert, user} from '../../../src/log';
import {fetchCacheSources} from './video-cache';
import {getBitrateManager} from './flexible-bitrate';
import {getMode} from '../../../src/mode';
import {htmlFor} from '../../../src/static-template';
Expand Down Expand Up @@ -259,11 +260,17 @@ export class AmpVideo extends AMP.BaseElement {
// Cached so mediapool operations (eg: swapping sources) don't interfere with this bool.
this.hasBitrateSources_ =
!!this.element.querySelector('source[data-bitrate]') ||
this.hasAnyCachedSources_();
this.hasAnyCachedSources_() ||
this.element.hasAttribute('cache');

installVideoManagerForDoc(element);

Services.videoManagerForDoc(element).register(this);

// Fetch and add cached sources URLs if opted-in, and if the sources don't already contained cached URLs from the AMP Cache.
if (this.element.hasAttribute('cache') && !this.hasAnyCachedSources_()) {
return fetchCacheSources(this.element, this.win);
}
}

/**
Expand Down
159 changes: 159 additions & 0 deletions extensions/amp-video/0.1/test/test-video-cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/**
* Copyright 2021 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as videoCache from '../video-cache';
import {AmpCacheUrlService} from '../../../amp-cache-url/0.1/amp-cache-url';
import {AmpVideo} from '../amp-video';
import {Services} from '../../../../src/services';
import {createElementWithAttributes} from '../../../../src/dom';
import {createExtensionScript} from '../../../../src/service/extension-script';
import {xhrServiceForTesting} from '../../../../src/service/xhr-impl';

describes.realWin('amp-video cached-sources', {amp: true}, (env) => {
let xhrService;
let cacheUrlService;

beforeEach(() => {
xhrService = xhrServiceForTesting(env.win);
env.sandbox.stub(Services, 'xhrFor').returns(xhrService);

cacheUrlService = new AmpCacheUrlService();
env.sandbox
.stub(Services, 'cacheUrlServicePromiseForDoc')
.resolves(cacheUrlService);
env.win.document.head.appendChild(
createExtensionScript(env.win, 'amp-cache-url', '0.1')
);
env.sandbox
.stub(Services, 'documentInfoForDoc')
.returns({sourceUrl: 'https://example.com'});
});

describe('select sources', () => {
it('should select the source if there is only one source', () => {
const videoEl = createVideo([{src: 'video1.mp4'}]);
expect(videoCache.selectVideoSource(videoEl)).to.contain('video1.mp4');
});

it('should select the first source if there are similar sources', () => {
const videoEl = createVideo([{src: 'video1.mp4'}, {src: 'video2.mp4'}]);
expect(videoCache.selectVideoSource(videoEl)).to.contain('video1.mp4');
});

it('should select the mp4 source if there are many sources', () => {
const videoEl = createVideo([
{src: 'video1.mp4'},
{src: 'video2.mp4', type: 'video/mp4'},
]);
expect(videoCache.selectVideoSource(videoEl)).to.contain('video2.mp4');
});
});

describe('add sources', () => {
it('should set the correct attributes on the source added', () => {
const videoEl = createVideo([]);
videoCache.applySourcesToVideo(videoEl, [
{'url': 'video1.mp4', 'bitrate_kbps': 700, type: 'video/mp4'},
]);
const addedSource = videoEl.querySelector('source');
expect(addedSource.getAttribute('src')).to.equal('video1.mp4');
expect(addedSource.getAttribute('data-bitrate')).to.equal('700');
expect(addedSource.getAttribute('type')).to.equal('video/mp4');
});

it('should add the sources sorted by bitrate', () => {
const videoEl = createVideo([]);
videoCache.applySourcesToVideo(videoEl, [
{'url': 'video1.mp4', 'bitrate_kbps': 700, type: 'video/mp4'},
{'url': 'video2.mp4', 'bitrate_kbps': 2000, type: 'video/mp4'},
{'url': 'video3.mp4', 'bitrate_kbps': 1500, type: 'video/mp4'},
]);
const addedSources = videoEl.querySelectorAll('source');
expect(addedSources[0].getAttribute('data-bitrate')).to.equal('2000');
expect(addedSources[1].getAttribute('data-bitrate')).to.equal('1500');
expect(addedSources[2].getAttribute('data-bitrate')).to.equal('700');
});
});

describe('url forming', () => {
it('should send the request to the correct address if the video has an absolute url', async () => {
const videoEl = createVideo([{'src': 'https://website.com/video.html'}]);
const xhrSpy = env.sandbox.spy(xhrService, 'fetch');

const ampVideo = new AmpVideo(videoEl);
await ampVideo.buildCallback();

expect(xhrSpy).to.have.been.calledWith(
'https://website-com.cdn.ampproject.org/mbv/s/website.com/video.html'
);
});

it('should send the request to the correct address if the video has a relative url', async () => {
const videoEl = createVideo([{'src': 'video.html'}]);
const xhrSpy = env.sandbox.spy(xhrService, 'fetch');

const ampVideo = new AmpVideo(videoEl);
await ampVideo.buildCallback();

expect(xhrSpy).to.have.been.calledWith(
'https://example-com.cdn.ampproject.org/mbv/s/example.com/video.html'
);
});
});

describe('end to end', () => {
it('should create the sources from the request with the correct attributes', async () => {
env.sandbox.stub(xhrService, 'fetch').resolves({
json: () =>
Promise.resolve({
sources: [
{'url': 'video.mp4', 'bitrate_kbps': 700, 'type': 'video/mp4'},
],
}),
});

const videoEl = createVideo([{src: 'video.mp4'}]);
const ampVideo = new AmpVideo(videoEl);
await ampVideo.buildCallback();

expect(videoEl.querySelector('source[data-bitrate]')).to.not.be.null;
});
it('should not create the sources if there is amp-orig-src attribute', async () => {
const videoEl = createVideo([{'src': 'video.mp4', 'amp-orig-src': ''}]);
const ampVideo = new AmpVideo(videoEl);
await ampVideo.buildCallback();

expect(videoEl.querySelector('source[data-bitrate]')).to.be.null;
});
});

function createVideo(children) {
const videoEl = createElementWithAttributes(env.win.document, 'amp-video', {
'cache': 'google',
'layout': 'fill',
});
children.forEach((childJson) => {
const sourceEl = createElementWithAttributes(
env.win.document,
'source',
childJson
);
videoEl.appendChild(sourceEl);
});
env.win.document.body.appendChild(videoEl);
return videoEl;
}
});
Loading