From a7bb6ee086f463dbf2740219273d0991a5e92e57 Mon Sep 17 00:00:00 2001 From: Wassim Gharbi Date: Sat, 15 Jul 2017 09:33:37 -0700 Subject: [PATCH 1/7] Implemented metaData and media session for amp-video --- extensions/amp-vimeo/0.1/amp-vimeo.js | 7 ++ extensions/amp-youtube/0.1/amp-youtube.js | 7 ++ src/service/video-manager-impl.js | 80 +++++++++++++++++++++++ src/video-interface.js | 27 ++++++++ 4 files changed, 121 insertions(+) diff --git a/extensions/amp-vimeo/0.1/amp-vimeo.js b/extensions/amp-vimeo/0.1/amp-vimeo.js index 59e983d15289..3d9209291635 100644 --- a/extensions/amp-vimeo/0.1/amp-vimeo.js +++ b/extensions/amp-vimeo/0.1/amp-vimeo.js @@ -74,6 +74,13 @@ class AmpVimeo extends AMP.BaseElement { })), '*'); } } + + /** @override */ + optOutOfAutomaticMediaSessionAPI() { + // Vimeo already updates the Media Session so no need for the video + // manager to update it too + return true; + } }; AMP.registerElement('amp-vimeo', AmpVimeo); diff --git a/extensions/amp-youtube/0.1/amp-youtube.js b/extensions/amp-youtube/0.1/amp-youtube.js index 124906596d9e..3f02309081ae 100644 --- a/extensions/amp-youtube/0.1/amp-youtube.js +++ b/extensions/amp-youtube/0.1/amp-youtube.js @@ -246,6 +246,13 @@ class AmpYoutube extends AMP.BaseElement { } } + /** @override */ + optOutOfAutomaticMediaSessionAPI() { + // Youtube already updates the Media Session so no need for the video + // manager to update it too + return true; + } + /** @override */ mutatedAttributesCallback(mutations) { if (mutations['data-videoid'] !== undefined) { diff --git a/src/service/video-manager-impl.js b/src/service/video-manager-impl.js index 988f5410a9f0..28369b846cf6 100644 --- a/src/service/video-manager-impl.js +++ b/src/service/video-manager-impl.js @@ -571,6 +571,9 @@ class VideoEntry { */ videoPlayed_() { this.isPlaying_ = true; + if (!this.preimplementsMediaSession()) { + this.mediaSessionUpdate_(); + } this.actionSessionManager_.beginSession(); if (this.isVisible_) { this.visibilitySessionManager_.beginSession(); @@ -617,6 +620,11 @@ class VideoEntry { this.inlineVidRect_ = this.video.element./*OK*/getBoundingClientRect(); }); + if (!this.preimplementsMediaSession()) { + this.metaData_ = this.getMetaData_(); + this.mediaSessionUpdate_(); + } + this.updateVisibility(); if (this.isVisible_) { // Handles the case when the video becomes visible before loading @@ -624,6 +632,78 @@ class VideoEntry { } } + /** + * Gets the provided metadata and fills in missing fields + * @return {!../video-interface.VideoMetaDef} + * @private + */ + getMetaData_() { + let metaData = this.video.metaData; + if (!metaData) { + metaData = {}; + } + if (!metaData.artist) { + metaData.artist = 'No artist'; + } + if (!metaData.posterUrl) { + metaData.posterUrl = this.video.element.getAttribute('poster') + || this.internalElement_.getAttribute('poster') + || this.getDefaultPoster_(); + } + if (!metaData.title) { + metaData.title = this.video.element.getAttribute('title') + || this.video.element.getAttribute('aria-label') + || this.internalElement_.getAttribute('title') + || this.internalElement_.getAttribute('aria-label') + || this.ampdoc_.win.document.title; + } + if (!metaData.album) { + metaData.album = 'No album'; + } + return metaData; + } + + /** + * Gets the provided metadata and fills in missing fields + * @private + */ + mediaSessionUpdate_() { + const win = this.ampdoc_.win; + const navigator = win.navigator; + if ('mediaSession' in navigator && win.MediaMetadata) { + + navigator.mediaSession.metadata = new win.MediaMetadata({ + title: this.metaData_.title, + artist: this.metaData_.artist, + album: this.metaData_.album, + artwork: [ + { + src: this.metaData_.posterUrl, + sizes: '512x512', + type: 'image/png', + }, + ], + }); + + navigator.mediaSession.setActionHandler('play', function() { + this.video.play(); + }); + navigator.mediaSession.setActionHandler('pause', function() { + this.video.pause(); + }); + + // TODO(@wassgha) Implement seek & next/previous + } + + } + + getDefaultPoster_() { + /*eslint-disable */ + const ampLogo = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKs2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjarZZnUFPpHsb/55z0QksIRUroTZAiXSB0QhGkg42QUAIhxCQgInYWV3AtqIhgWVEpouBaAFkrFiwsgg37BllUlHWxYEPlfmAJe+fO/XBn7n/mzPnNM+/7vM97zpcHgNbDk0hEqBpAjlgujQ72YycmJbOJ/YAHLaACC4DHl0l8o6LCAQAm3/8YBODDXUAAAG7Z8iQSEfxvoy5Ik/EBkCgASBXI+DkAyHEApIUvkcoBMBEAmCyWS+QA2CYAYEoTk5IBsP0AwMyY4DYAYKZOcBcAMKWx0f4AmAKAROPxpBkA1GEAYOfzM+QANBoA2IsFQjEAbRYAePMzeQIAWhEATM/JyRUA0OoBwDL1Hz4Z/+aZqvTk8TKUPHEXAAAgBQhlEhFvCfy/J0eUN3mGMQDQMqUh0QCgAoDUZ+eGKVmcOjtykoUCgEnOzAuJm2S+zD95kgW8gLBJzsuO851knnRqr1DOjZ1kaW600l8smh2u9E/jKjlNFhgzyenCIO4kF2bGJkxyvjB+9iTLsmPCptb4K3VpXrQyc7o0SHnHHNlUNj5v6ix5ZmzIVIZEZR5BWkCgUhfHKddL5H5KT4koaiq/KFipy/JjlHvl0lilnsULjZryiVJ+HwiBKGBDMjiCK4gB5GkFcgAA/1zJEqkwI1PO9pVIRGlsrphvN53taO/gCpCYlMye+LXv7gECAAiLNKXl9gC4NgBA3ZTGSwFokwFomkxpZgcAVP8AOM3n50nzJzQcAAAeKKAKTNABAzABS7AFR3ABT+BAIIRCJMRCEiwAPmRCDkhhMRTBKiiBMtgE26AK9sA+qIfDcBRa4RSch8twHXrgDjwEBQzCKxiBDzCGIAgRoSMMRAcxRMwQG8QRcUO8kUAkHIlGkpAUJAMRI3lIEbIGKUPKkSpkL9KA/IKcRM4jV5Fe5D7Sjwwhb5EvKIbSUCaqj5qjM1A31BcNQ2PR+WgGuggtRIvRDWglWoMeQlvQ8+h19A6qQF+hoxhgVIyFGWG2mBvmj0ViyVg6JsWWY6VYBVaDNWHtWCd2C1Ngw9hnHAHHwLFxtjhPXAguDsfHLcItx63HVeHqcS24i7hbuH7cCO47no7Xw9vgPfBcfCI+A78YX4KvwNfiT+Av4e/gB/EfCAQCi2BBcCWEEJIIWYSlhPWEXYRmwjlCL2GAMEokEnWINkQvYiSRR5QTS4g7iIeIZ4k3iYPETyQqyZDkSAoiJZPEpNWkCtJB0hnSTdJz0hhZjWxG9iBHkgXkJeSN5P3kdvIN8iB5jKJOsaB4UWIpWZRVlEpKE+US5RHlHZVKNaa6U+dQhdSV1ErqEeoVaj/1M02DZk3zp82j5dE20Opo52j3ae/odLo5nUNPpsvpG+gN9Av0J/RPKgwVOxWuikBlhUq1SovKTZXXqmRVM1Vf1QWqhaoVqsdUb6gOq5HVzNX81Xhqy9Wq1U6q9amNqjPUHdQj1XPU16sfVL+q/kKDqGGuEagh0CjW2KdxQWOAgTFMGP4MPmMNYz/jEmOQSWBaMLnMLGYZ8zCzmzmiqaE5UzNes0CzWvO0poKFscxZXJaItZF1lHWX9UVLX8tXK01rnVaT1k2tj9rTtDnaadql2s3ad7S/6LB1AnWydTbrtOo81sXpWuvO0V2su1v3ku7wNOY0z2n8aaXTjk57oIfqWetF6y3V26fXpTeqb6AfrC/R36F/QX/YgGXAMcgy2GpwxmDIkGHobSg03Gp41vAlW5PtyxaxK9kX2SNGekYhRnlGe426jcaMLYzjjFcbNxs/NqGYuJmkm2w16TAZMTU0jTAtMm00fWBGNnMzyzTbbtZp9tHcwjzBfK15q/kLC20LrkWhRaPFI0u6pY/lIssay9tWBCs3q2yrXVY91qi1s3WmdbX1DRvUxsVGaLPLpnc6frr7dPH0mul9tjRbX9t820bbfjuWXbjdartWu9czTGckz9g8o3PGd3tne5H9fvuHDhoOoQ6rHdod3jpaO/Idqx1vO9GdgpxWOLU5vZlpMzNt5u6Z95wZzhHOa507nL+5uLpIXZpchlxNXVNcd7r2uTHdotzWu11xx7v7ua9wP+X+2cPFQ+5x1OMvT1vPbM+Dni9mWcxKm7V/1oCXsRfPa6+XwpvtneL9s7fCx8iH51Pj85RjwhFwajnPfa18s3wP+b72s/eT+p3w++jv4b/M/1wAFhAcUBrQHagRGBdYFfgkyDgoI6gxaCTYOXhp8LkQfEhYyOaQPq4+l89t4I6EuoYuC70YRguLCasKexpuHS4Nb49AI0IjtkQ8mm02Wzy7NRIiuZFbIh9HWUQtivp1DmFO1JzqOc+iHaKLojtjGDELYw7GfIj1i90Y+zDOMi4vriNeNX5efEP8x4SAhPIEReKMxGWJ15N0k4RJbcnE5Pjk2uTRuYFzt80dnOc8r2Te3fkW8wvmX12gu0C04PRC1YW8hcdS8CkJKQdTvvIieTW80VRu6s7UEb4/fzv/lYAj2CoYSvNKK097nu6VXp7+IsMrY0vGUKZPZkXmsNBfWCV8kxWStSfrY3Zkdl32uChB1JxDyknJOSnWEGeLL+Ya5Bbk9kpsJCUSxSKPRdsWjUjDpLUyRDZf1iZnyiXyrjzLvB/y+vO986vzPy2OX3ysQL1AXNC1xHrJuiXPC4MKDyzFLeUv7SgyKlpV1L/Md9ne5cjy1OUdK0xWFK8YXBm8sn4VZVX2qt9W268uX/1+TcKa9mL94pXFAz8E/9BYolIiLelb67l2z4+4H4U/dq9zWrdj3fdSQem1MvuyirKv6/nrr/3k8FPlT+Mb0jd0b3TZuHsTYZN4093NPpvry9XLC8sHtkRsadnK3lq69f22hduuVsys2LOdsj1vu6IyvLJth+mOTTu+VmVW3an2q27eqbdz3c6PuwS7bu7m7G7ao7+nbM+Xn4U/39sbvLelxrymYh9hX/6+Z/vj93cecDvQUKtbW1b7rU5cp6iPrr/Y4NrQcFDv4MZGtDGvcejQvEM9hwMOtzXZNu1tZjWXHYEjeUde/pLyy92jYUc7jrkdazpudnznCcaJ0hakZUnLSGtmq6Itqa33ZOjJjnbP9hO/2v1ad8roVPVpzdMbz1DOFJ8ZP1t4dvSc5Nzw+YzzAx0LOx5eSLxw++Kci92Xwi5duRx0+UKnb+fZK15XTl31uHrymtu11usu11u6nLtO/Ob824lul+6WG6432nrce9p7Z/Weuelz8/ytgFuXb3NvX78z+07v3bi79/rm9SnuCe69uC+6/+ZB/oOxhysf4R+VPlZ7XPFE70nN71a/NytcFKf7A/q7nsY8fTjAH3j1h+yPr4PFz+jPKp4bPm944fji1FDQUM/LuS8HX0lejQ2X/Kn+587Xlq+P/8X5q2skcWTwjfTN+Nv173Te1b2f+b5jNGr0yYecD2MfSz/pfKr/7Pa580vCl+dji78Sv1Z+s/rW/j3s+6PxnPFxCU/KAwAADADQ9HSAt3UA9CQARg8ARWWiG//d6ZGpdv/feKI/AwCAC0ADByAOACI4ALsAwJwDoMoBiOIAxHIAdXJSPn+PLN3JccKL2gqArxgff5cAQLQC+NY3Pj7WOj7+rRYAewBw7sNEJwcAMB4BcGQBANwu+P0/uvG/AOoFAaYCPpR4AAA8yGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMTEgNzkuMTU4MzI1LCAyMDE1LzA5LzEwLTAxOjEwOjIwICAgICAgICAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgICAgICAgICB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIgogICAgICAgICAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKTwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8eG1wOkNyZWF0ZURhdGU+MjAxNy0wNy0xNVQwMTozMTo0Ni0wNzowMDwveG1wOkNyZWF0ZURhdGU+CiAgICAgICAgIDx4bXA6TWV0YWRhdGFEYXRlPjIwMTctMDctMTVUMDE6MzE6NDYtMDc6MDA8L3htcDpNZXRhZGF0YURhdGU+CiAgICAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDE3LTA3LTE1VDAxOjMxOjQ2LTA3OjAwPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcE1NOkluc3RhbmNlSUQ+eG1wLmlpZDozN2EwZTYyZC04M2E2LTQ1MjYtOGQ2Zi1iOTRiOTI3MmY2MTA8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgICAgIDx4bXBNTTpEb2N1bWVudElEPmFkb2JlOmRvY2lkOnBob3Rvc2hvcDo2OTQ3ZjZiMC1hNzU5LTExN2EtYTQ2ZC04MDc0MjMwNTY3MDY8L3htcE1NOkRvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+eG1wLmRpZDo1ZjJiMmQzZC1kNGJmLTRlNDgtYjlhOC05NjFhODFlOGVlODc8L3htcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOkhpc3Rvcnk+CiAgICAgICAgICAgIDxyZGY6U2VxPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jcmVhdGVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6NWYyYjJkM2QtZDRiZi00ZTQ4LWI5YTgtOTYxYTgxZThlZTg3PC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE3LTA3LTE1VDAxOjMxOjQ2LTA3OjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6MzdhMGU2MmQtODNhNi00NTI2LThkNmYtYjk0YjkyNzJmNjEwPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE3LTA3LTE1VDAxOjMxOjQ2LTA3OjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8cGhvdG9zaG9wOlRleHRMYXllcnM+CiAgICAgICAgICAgIDxyZGY6QmFnPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHBob3Rvc2hvcDpMYXllck5hbWU+TGF5ZXIgMzwvcGhvdG9zaG9wOkxheWVyTmFtZT4KICAgICAgICAgICAgICAgICAgPHBob3Rvc2hvcDpMYXllclRleHQvPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxwaG90b3Nob3A6TGF5ZXJOYW1lPkxheWVyIDQ8L3Bob3Rvc2hvcDpMYXllck5hbWU+CiAgICAgICAgICAgICAgICAgIDxwaG90b3Nob3A6TGF5ZXJUZXh0Lz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8cGhvdG9zaG9wOkxheWVyTmFtZT5MYXllciA1PC9waG90b3Nob3A6TGF5ZXJOYW1lPgogICAgICAgICAgICAgICAgICA8cGhvdG9zaG9wOkxheWVyVGV4dC8+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICA8L3JkZjpCYWc+CiAgICAgICAgIDwvcGhvdG9zaG9wOlRleHRMYXllcnM+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5EaXNwbGF5PC9waG90b3Nob3A6SUNDUHJvZmlsZT4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9wbmc8L2RjOmZvcm1hdD4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+NzIwMDAwLzEwMDAwPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43MjAwMDAvMTAwMDA8L3RpZmY6WVJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+NjU1MzU8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjUxMjwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj41MTI8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/Pn46GdsAAAAgY0hSTQAAbicAAHOvAAEAtAAAgpsAAHHJAADpTwAAMVsAABOcy5g2HwAAT+tJREFUeNrs3WmTXNd9Jvjn7pl5c8/adwCFwk4CpEiCqzZSXCTLkmXLbTtmul909ER09xfwu/kOY0/MdMxEuDumbbctWzal5iZuIsVVJEhsBFFAAbXvuee9mXefF7cAURIXAFWZlZn1/CIYAFigqnTq1r3P/Z9z/kfAX74TgIiIiPYUkUNARETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABARETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABARETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABARETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABARERE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P1zg5wdwvAB+wIc/EQMA0Q68dYaNhn7zUMkjXEi4ULZxdsXEh0sGhpIKBhMqkpqEbExGNiojHZWQiylIRyQkNE4b3Iqq5aHU8JA3HZTqHgp1FwXTRdX2sFi2MVeyMJMPOz+yQQ/RnRPwl+8wKhPtEE0WkYvJGE9r2J/VMJEJFxaOpcI/9+oKOxF+AcP2sWE4uFawMF8OF+zNFi1cK1iYK1nImy7b8BKxAkDUnizXx/LW1rK5koXMVgUgtVUViKsisjEZqUhYIchEJWSi4e+TEQk9se4NCIbtY9N0UGmEb/XFuovi1ht+uRG+5ddsHwXTRdkKKwDFuodi3UXd4YOfiAGAqAPUHR91x8ZyBQDqv/UxSRTQp8sYTqoYSKgYSCgYSaro0RWMplQMJVX06jIisghJECAIgCgAohA2JhIEQMBnfw8IgrD162//GbjzVgY3ptEDAEEQbP36+3/2AyBAcPP3fhCE/y4AvCBAw/WxYbhYroRTJpuGg8WKjdWqg9VquBVz3XDhcdEeEQMAUTfz/ABrNQdVy8dcyUZMCbccRmQRuiqGf1YkRBURCU1ESpOR0CSko+E6gqQWfkxXRKhy+PcjsoiIHPYo0ORwkaK4zWYFN4KD7wcwHR+WG4R77d3woW46PmzXh+H4qDs+KpYXzt/Xw1/LlouqFX7MdDyYjg9ja0ueaYf//Y2P8dlPxABAtCf4AVCzPdRs73M/LgpARBaRjso3txv2xGSkIzKyMQm6GgaBiCwiroWBQVdFxNUwHMS3AoUqCRC3WhvLEn6rzbG09YD3frddrge4friS3vbCh33NDh/WNduDYYcP7ZoVPswrlgfD9lAwPZQaLjZN9+b2vFLdRcP1+YAnYgAgolsNCKbjw3RsrnQnoqbgcmQiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIqKO8P8PAJkqi3WN70gIAAAAAElFTkSuQmCC'; + return ampLogo; + /*eslint-enable */ + } + /** * Called when visibility of a video changes. * @private diff --git a/src/video-interface.js b/src/video-interface.js index b5952805bafa..35e37fc61dc7 100644 --- a/src/video-interface.js +++ b/src/video-interface.js @@ -104,6 +104,23 @@ export class VideoInterface { */ hideControls() {} + /** + * Returns video's meta data (poster, artist, album, etc.) + * @return {!VideoMetaDef} metadata + */ + get metaData() {} + + /** + * If this returns true then it will be assumed that the player implements + * the MediaSession API internally so that the video manager does not override + * it. If not, the video manager will use the metaData variable as well as + * inferred meta-data to update the video's Media Session notification. + * + * @return {boolean} + */ + optOutOfAutomaticMediaSessionAPI() {} + + /** * Automatically comes from {@link ./base-element.BaseElement} * @@ -414,3 +431,13 @@ export const VideoAnalyticsEvents = { * }} */ export let VideoAnalyticsDetailsDef; + +/** + * @typedef {{ + * posterUrl: string, + * title: string, + * album: string, + * artist: string, + * }} + */ +export let VideoMetaDef; From 429dee4d60c83c0054fbd0878704ed4bc365361e Mon Sep 17 00:00:00 2001 From: Wassim Gharbi Date: Tue, 18 Jul 2017 15:02:15 -0700 Subject: [PATCH 2/7] Started working on requested changes --- extensions/amp-vimeo/0.1/amp-vimeo.js | 2 +- extensions/amp-youtube/0.1/amp-youtube.js | 2 +- src/service/video-manager-impl.js | 103 ++++++++++++---------- src/video-interface.js | 31 ++++--- 4 files changed, 76 insertions(+), 62 deletions(-) diff --git a/extensions/amp-vimeo/0.1/amp-vimeo.js b/extensions/amp-vimeo/0.1/amp-vimeo.js index 3d9209291635..20e33f5367fe 100644 --- a/extensions/amp-vimeo/0.1/amp-vimeo.js +++ b/extensions/amp-vimeo/0.1/amp-vimeo.js @@ -76,7 +76,7 @@ class AmpVimeo extends AMP.BaseElement { } /** @override */ - optOutOfAutomaticMediaSessionAPI() { + preimplementsMediaSessionAPI() { // Vimeo already updates the Media Session so no need for the video // manager to update it too return true; diff --git a/extensions/amp-youtube/0.1/amp-youtube.js b/extensions/amp-youtube/0.1/amp-youtube.js index 3f02309081ae..3d8719d2beed 100644 --- a/extensions/amp-youtube/0.1/amp-youtube.js +++ b/extensions/amp-youtube/0.1/amp-youtube.js @@ -247,7 +247,7 @@ class AmpYoutube extends AMP.BaseElement { } /** @override */ - optOutOfAutomaticMediaSessionAPI() { + preimplementsMediaSessionAPI() { // Youtube already updates the Media Session so no need for the video // manager to update it too return true; diff --git a/src/service/video-manager-impl.js b/src/service/video-manager-impl.js index 28369b846cf6..80b4fd2048c1 100644 --- a/src/service/video-manager-impl.js +++ b/src/service/video-manager-impl.js @@ -38,6 +38,7 @@ import { PositionObserverFidelity, PositionInViewportEntryDef, } from './position-observer-impl'; +import {map} from '../utils/object'; import {layoutRectLtwh, RelativePositions} from '../layout-rect'; import {Animation} from '../animation'; import * as st from '../style'; @@ -456,6 +457,8 @@ class VideoEntry { const element = dev().assert(video.element); + // Autoplay Variables + /** @private {boolean} */ this.userInteractedWithAutoPlay_ = false; @@ -541,7 +544,18 @@ class VideoEntry { listenOncePromise(element, VideoEvents.LOAD) .then(() => this.videoLoaded()); + // Media Session API Variables + + /** @private {!../video-interface.VideoMetaDef} */ + this.metaData_ = { + 'artist': '', + 'album': '', + 'artwork': '', + 'title': '', + }; + listenOncePromise(element, VideoEvents.LOAD) + .then(() => this.videoLoaded()); listen(element, VideoEvents.PAUSE, () => this.videoPaused_()); listen(element, VideoEvents.PLAYING, () => this.videoPlayed_()); listen(element, VideoEvents.MUTED, () => this.muted_ = true); @@ -571,9 +585,8 @@ class VideoEntry { */ videoPlayed_() { this.isPlaying_ = true; - if (!this.preimplementsMediaSession()) { - this.mediaSessionUpdate_(); - } + this.updateMediaSession_(); + this.actionSessionManager_.beginSession(); if (this.isVisible_) { this.visibilitySessionManager_.beginSession(); @@ -620,10 +633,7 @@ class VideoEntry { this.inlineVidRect_ = this.video.element./*OK*/getBoundingClientRect(); }); - if (!this.preimplementsMediaSession()) { - this.metaData_ = this.getMetaData_(); - this.mediaSessionUpdate_(); - } + this.getMetaData_(); this.updateVisibility(); if (this.isVisible_) { @@ -634,59 +644,65 @@ class VideoEntry { /** * Gets the provided metadata and fills in missing fields - * @return {!../video-interface.VideoMetaDef} * @private */ getMetaData_() { - let metaData = this.video.metaData; - if (!metaData) { - metaData = {}; + if (this.video.preimplementsMediaSessionAPI()) { + return; } - if (!metaData.artist) { - metaData.artist = 'No artist'; + + if (this.video.getMetaData()) { + this.metaData_ = map(this.video.getMetaData()); + } + + if (!this.metaData_.artist) { + const artist = 'No artist'; + if (artist) { + this.metaData_.artist = artist; + } } - if (!metaData.posterUrl) { - metaData.posterUrl = this.video.element.getAttribute('poster') - || this.internalElement_.getAttribute('poster') - || this.getDefaultPoster_(); + + if (!this.metaData_.artwork) { + const posterUrl = this.video.element.getAttribute('poster') + || this.internalElement_.getAttribute('poster'); + if (posterUrl) { + this.metaData_.artwork = posterUrl; + } } - if (!metaData.title) { - metaData.title = this.video.element.getAttribute('title') - || this.video.element.getAttribute('aria-label') - || this.internalElement_.getAttribute('title') - || this.internalElement_.getAttribute('aria-label') - || this.ampdoc_.win.document.title; + + if (!this.metaData_.title) { + const title = this.video.element.getAttribute('title') + || this.video.element.getAttribute('aria-label') + || this.internalElement_.getAttribute('title') + || this.internalElement_.getAttribute('aria-label') + || this.ampdoc_.win.document.title; + if (title) { + this.metaData_.title = title; + } } - if (!metaData.album) { - metaData.album = 'No album'; + + if (!this.metaData_.album) { + this.metaData_.album = 'No album'; } - return metaData; } /** * Gets the provided metadata and fills in missing fields * @private */ - mediaSessionUpdate_() { + updateMediaSession_() { + if (this.video.preimplementsMediaSessionAPI()) { + return; + } + const win = this.ampdoc_.win; const navigator = win.navigator; if ('mediaSession' in navigator && win.MediaMetadata) { - navigator.mediaSession.metadata = new win.MediaMetadata({ - title: this.metaData_.title, - artist: this.metaData_.artist, - album: this.metaData_.album, - artwork: [ - { - src: this.metaData_.posterUrl, - sizes: '512x512', - type: 'image/png', - }, - ], - }); + navigator.mediaSession.metadata = new win.MediaMetadata(this.metaData_); navigator.mediaSession.setActionHandler('play', function() { - this.video.play(); + this.video.play(/*isAutoplay*/ false); }); navigator.mediaSession.setActionHandler('pause', function() { this.video.pause(); @@ -697,13 +713,6 @@ class VideoEntry { } - getDefaultPoster_() { - /*eslint-disable */ - const ampLogo = 'data:image/png;base64,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6ZGpdv/feKI/AwCAC0ADByAOACI4ALsAwJwDoMoBiOIAxHIAdXJSPn+PLN3JccKL2gqArxgff5cAQLQC+NY3Pj7WOj7+rRYAewBw7sNEJwcAMB4BcGQBANwu+P0/uvG/AOoFAaYCPpR4AAA8yGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMTEgNzkuMTU4MzI1LCAyMDE1LzA5LzEwLTAxOjEwOjIwICAgICAgICAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgICAgICAgICB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIgogICAgICAgICAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKTwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8eG1wOkNyZWF0ZURhdGU+MjAxNy0wNy0xNVQwMTozMTo0Ni0wNzowMDwveG1wOkNyZWF0ZURhdGU+CiAgICAgICAgIDx4bXA6TWV0YWRhdGFEYXRlPjIwMTctMDctMTVUMDE6MzE6NDYtMDc6MDA8L3htcDpNZXRhZGF0YURhdGU+CiAgICAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDE3LTA3LTE1VDAxOjMxOjQ2LTA3OjAwPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcE1NOkluc3RhbmNlSUQ+eG1wLmlpZDozN2EwZTYyZC04M2E2LTQ1MjYtOGQ2Zi1iOTRiOTI3MmY2MTA8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgICAgIDx4bXBNTTpEb2N1bWVudElEPmFkb2JlOmRvY2lkOnBob3Rvc2hvcDo2OTQ3ZjZiMC1hNzU5LTExN2EtYTQ2ZC04MDc0MjMwNTY3MDY8L3htcE1NOkRvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+eG1wLmRpZDo1ZjJiMmQzZC1kNGJmLTRlNDgtYjlhOC05NjFhODFlOGVlODc8L3htcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOkhpc3Rvcnk+CiAgICAgICAgICAgIDxyZGY6U2VxPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jcmVhdGVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6NWYyYjJkM2QtZDRiZi00ZTQ4LWI5YTgtOTYxYTgxZThlZTg3PC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE3LTA3LTE1VDAxOjMxOjQ2LTA3OjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6MzdhMGU2MmQtODNhNi00NTI2LThkNmYtYjk0YjkyNzJmNjEwPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE3LTA3LTE1VDAxOjMxOjQ2LTA3OjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8cGhvdG9zaG9wOlRleHRMYXllcnM+CiAgICAgICAgICAgIDxyZGY6QmFnPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHBob3Rvc2hvcDpMYXllck5hbWU+TGF5ZXIgMzwvcGhvdG9zaG9wOkxheWVyTmFtZT4KICAgICAgICAgICAgICAgICAgPHBob3Rvc2hvcDpMYXllclRleHQvPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxwaG90b3Nob3A6TGF5ZXJOYW1lPkxheWVyIDQ8L3Bob3Rvc2hvcDpMYXllck5hbWU+CiAgICAgICAgICAgICAgICAgIDxwaG90b3Nob3A6TGF5ZXJUZXh0Lz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8cGhvdG9zaG9wOkxheWVyTmFtZT5MYXllciA1PC9waG90b3Nob3A6TGF5ZXJOYW1lPgogICAgICAgICAgICAgICAgICA8cGhvdG9zaG9wOkxheWVyVGV4dC8+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICA8L3JkZjpCYWc+CiAgICAgICAgIDwvcGhvdG9zaG9wOlRleHRMYXllcnM+CiAgICAgICAgIDxwaG90b3Nob3A6Q29sb3JNb2RlPjM8L3Bob3Rvc2hvcDpDb2xvck1vZGU+CiAgICAgICAgIDxwaG90b3Nob3A6SUNDUHJvZmlsZT5EaXNwbGF5PC9waG90b3Nob3A6SUNDUHJvZmlsZT4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9wbmc8L2RjOmZvcm1hdD4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+NzIwMDAwLzEwMDAwPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43MjAwMDAvMTAwMDA8L3RpZmY6WVJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+NjU1MzU8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjUxMjwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj41MTI8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/Pn46GdsAAAAgY0hSTQAAbicAAHOvAAEAtAAAgpsAAHHJAADpTwAAMVsAABOcy5g2HwAAT+tJREFUeNrs3WmTXNd9Jvjn7pl5c8/adwCFwk4CpEiCqzZSXCTLkmXLbTtmul909ER09xfwu/kOY0/MdMxEuDumbbctWzal5iZuIsVVJEhsBFFAAbXvuee9mXefF7cAURIXAFWZlZn1/CIYAFigqnTq1r3P/Z9z/kfAX74TgIiIiPYUkUNARETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABARETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABARETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABARETEAEBEREQMAERERMQAQERERAwARERExABAREREDABERETEAEBEREQMAERERMQAQERERAwARERExABARERE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P1zg5wdwvAB+wIc/EQMA0Q68dYaNhn7zUMkjXEi4ULZxdsXEh0sGhpIKBhMqkpqEbExGNiojHZWQiylIRyQkNE4b3Iqq5aHU8JA3HZTqHgp1FwXTRdX2sFi2MVeyMJMPOz+yQQ/RnRPwl+8wKhPtEE0WkYvJGE9r2J/VMJEJFxaOpcI/9+oKOxF+AcP2sWE4uFawMF8OF+zNFi1cK1iYK1nImy7b8BKxAkDUnizXx/LW1rK5koXMVgUgtVUViKsisjEZqUhYIchEJWSi4e+TEQk9se4NCIbtY9N0UGmEb/XFuovi1ht+uRG+5ddsHwXTRdkKKwDFuodi3UXd4YOfiAGAqAPUHR91x8ZyBQDqv/UxSRTQp8sYTqoYSKgYSCgYSaro0RWMplQMJVX06jIisghJECAIgCgAohA2JhIEQMBnfw8IgrD162//GbjzVgY3ptEDAEEQbP36+3/2AyBAcPP3fhCE/y4AvCBAw/WxYbhYroRTJpuGg8WKjdWqg9VquBVz3XDhcdEeEQMAUTfz/ABrNQdVy8dcyUZMCbccRmQRuiqGf1YkRBURCU1ESpOR0CSko+E6gqQWfkxXRKhy+PcjsoiIHPYo0ORwkaK4zWYFN4KD7wcwHR+WG4R77d3woW46PmzXh+H4qDs+KpYXzt/Xw1/LlouqFX7MdDyYjg9ja0ueaYf//Y2P8dlPxABAtCf4AVCzPdRs73M/LgpARBaRjso3txv2xGSkIzKyMQm6GgaBiCwiroWBQVdFxNUwHMS3AoUqCRC3WhvLEn6rzbG09YD3frddrge4friS3vbCh33NDh/WNduDYYcP7ZoVPswrlgfD9lAwPZQaLjZN9+b2vFLdRcP1+YAnYgAgolsNCKbjw3RsrnQnoqbgcmQiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiIGACIiImIAICIiIgYAIiIiYgAgIiIiBgAiIiJiACAiIqKO8P8PAJkqi3WN70gIAAAAAElFTkSuQmCC'; - return ampLogo; - /*eslint-enable */ - } - /** * Called when visibility of a video changes. * @private diff --git a/src/video-interface.js b/src/video-interface.js index 35e37fc61dc7..1f5062a3c2a1 100644 --- a/src/video-interface.js +++ b/src/video-interface.js @@ -16,6 +16,16 @@ import {ActionTrust} from './action-trust'; /* eslint no-unused-vars: 0 */ +/** + * @typedef {{ + * artwork: string, + * title: string, + * album: string, + * artist: string, + * }} + */ +export let VideoMetaDef; + /** * VideoInterface defines a common video API which any AMP component that plays * videos is expected to implement. @@ -105,10 +115,15 @@ export class VideoInterface { hideControls() {} /** - * Returns video's meta data (poster, artist, album, etc.) + * Returns video's meta data (artwork, title, artist, album, etc.) for use + * with the Media Session API + * artwork (string): URL to the poster image (preferably a 512x512 PNG) + * title (string): Name of the video + * artist (string): Name of the video's author/artist + * album (string): Name of the video's album if it exists * @return {!VideoMetaDef} metadata */ - get metaData() {} + getMetaData() {} /** * If this returns true then it will be assumed that the player implements @@ -118,7 +133,7 @@ export class VideoInterface { * * @return {boolean} */ - optOutOfAutomaticMediaSessionAPI() {} + preimplementsMediaSessionAPI() {} /** @@ -431,13 +446,3 @@ export const VideoAnalyticsEvents = { * }} */ export let VideoAnalyticsDetailsDef; - -/** - * @typedef {{ - * posterUrl: string, - * title: string, - * album: string, - * artist: string, - * }} - */ -export let VideoMetaDef; From d25e0854c9147c890f4c51343098cc1e8815287c Mon Sep 17 00:00:00 2001 From: Wassim Gharbi Date: Mon, 24 Jul 2017 12:46:08 -0700 Subject: [PATCH 3/7] Implemented metadata and mediasession api for all players & added better default posters --- extensions/amp-3q-player/0.1/amp-3q-player.js | 15 ++++ .../amp-brid-player/0.1/amp-brid-player.js | 15 ++++ .../amp-dailymotion/0.1/amp-dailymotion.js | 15 ++++ extensions/amp-ima-video/0.1/amp-ima-video.js | 15 ++++ .../0.1/amp-nexxtv-player.js | 15 ++++ .../0.1/amp-ooyala-player.js | 15 ++++ extensions/amp-video/0.1/amp-video.js | 69 ++++++++++------- extensions/amp-vimeo/0.1/amp-vimeo.js | 7 -- extensions/amp-youtube/0.1/amp-youtube.js | 24 ++++-- src/service/video-manager-impl.js | 77 +++++++++++++++---- src/video-interface.js | 13 +++- 11 files changed, 223 insertions(+), 57 deletions(-) diff --git a/extensions/amp-3q-player/0.1/amp-3q-player.js b/extensions/amp-3q-player/0.1/amp-3q-player.js index c25cf71c8056..15ce8eed43c7 100644 --- a/extensions/amp-3q-player/0.1/amp-3q-player.js +++ b/extensions/amp-3q-player/0.1/amp-3q-player.js @@ -248,6 +248,21 @@ class Amp3QPlayer extends AMP.BaseElement { return isFullscreenElement(dev().assertElement(this.iframe_)); } + /** @override */ + getMetaData() { + return { + 'artwork': [], + 'title': '', + 'artist': '', + 'album': '', + }; + } + + /** @override */ + preimplementsMediaSessionAPI() { + return false; + } + /** @override */ getCurrentTime() { // Not supported. diff --git a/extensions/amp-brid-player/0.1/amp-brid-player.js b/extensions/amp-brid-player/0.1/amp-brid-player.js index e72d24faf76f..e946e37daaeb 100644 --- a/extensions/amp-brid-player/0.1/amp-brid-player.js +++ b/extensions/amp-brid-player/0.1/amp-brid-player.js @@ -319,6 +319,21 @@ class AmpBridPlayer extends AMP.BaseElement { return isFullscreenElement(dev().assertElement(this.iframe_)); } + /** @override */ + getMetaData() { + return { + 'artwork': [], + 'title': '', + 'artist': '', + 'album': '', + }; + } + + /** @override */ + preimplementsMediaSessionAPI() { + return false; + } + /** @override */ getCurrentTime() { // Not supported. diff --git a/extensions/amp-dailymotion/0.1/amp-dailymotion.js b/extensions/amp-dailymotion/0.1/amp-dailymotion.js index 8d95e77d8784..69cfd677b7d9 100644 --- a/extensions/amp-dailymotion/0.1/amp-dailymotion.js +++ b/extensions/amp-dailymotion/0.1/amp-dailymotion.js @@ -392,6 +392,21 @@ class AmpDailymotion extends AMP.BaseElement { } } + /** @override */ + getMetaData() { + return { + 'artwork': [], + 'title': '', + 'artist': '', + 'album': '', + }; + } + + /** @override */ + preimplementsMediaSessionAPI() { + return false; + } + /** @override */ getCurrentTime() { // Not supported. diff --git a/extensions/amp-ima-video/0.1/amp-ima-video.js b/extensions/amp-ima-video/0.1/amp-ima-video.js index ebb9efc8ac1e..f1c2bbb115e1 100644 --- a/extensions/amp-ima-video/0.1/amp-ima-video.js +++ b/extensions/amp-ima-video/0.1/amp-ima-video.js @@ -310,6 +310,21 @@ class AmpImaVideo extends AMP.BaseElement { return isFullscreenElement(dev().assertElement(this.iframe_)); } + /** @override */ + getMetaData() { + return { + 'artwork': [], + 'title': '', + 'artist': '', + 'album': '', + }; + } + + /** @override */ + preimplementsMediaSessionAPI() { + return false; + } + /** @override */ getCurrentTime() { // Not supported. diff --git a/extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js b/extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js index dfd6291eb8f2..edfbbf4a9824 100644 --- a/extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js +++ b/extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js @@ -274,6 +274,21 @@ class AmpNexxtvPlayer extends AMP.BaseElement { return isFullscreenElement(dev().assertElement(this.iframe_)); } + /** @override */ + getMetaData() { + return { + 'artwork': [], + 'title': '', + 'artist': '', + 'album': '', + }; + } + + /** @override */ + preimplementsMediaSessionAPI() { + return false; + } + /** @override */ getCurrentTime() { // Not supported. diff --git a/extensions/amp-ooyala-player/0.1/amp-ooyala-player.js b/extensions/amp-ooyala-player/0.1/amp-ooyala-player.js index 068793262806..a06810bb9c7c 100644 --- a/extensions/amp-ooyala-player/0.1/amp-ooyala-player.js +++ b/extensions/amp-ooyala-player/0.1/amp-ooyala-player.js @@ -259,6 +259,21 @@ class AmpOoyalaPlayer extends AMP.BaseElement { return isFullscreenElement(dev().assertElement(this.iframe_)); } + /** @override */ + getMetaData() { + return { + 'artwork': [], + 'title': '', + 'artist': '', + 'album': '', + }; + } + + /** @override */ + preimplementsMediaSessionAPI() { + return false; + } + /** @override */ getCurrentTime() { // Not supported. diff --git a/extensions/amp-video/0.1/amp-video.js b/extensions/amp-video/0.1/amp-video.js index 901d13177ce4..b8c1d3190189 100644 --- a/extensions/amp-video/0.1/amp-video.js +++ b/extensions/amp-video/0.1/amp-video.js @@ -59,23 +59,23 @@ const ATTRS_TO_PROPAGATE = */ class AmpVideo extends AMP.BaseElement { - /** - * @param {!AmpElement} element - */ + /** + * @param {!AmpElement} element + */ constructor(element) { super(element); - /** @private {?Element} */ + /** @private {?Element} */ this.video_ = null; - /** @private {?boolean} */ + /** @private {?boolean} */ this.muted_ = false; } - /** - * @param {boolean=} opt_onLayout - * @override - */ + /** + * @param {boolean=} opt_onLayout + * @override + */ preconnectCallback(opt_onLayout) { const videoSrc = this.getVideoSource_(); if (videoSrc) { @@ -84,10 +84,10 @@ class AmpVideo extends AMP.BaseElement { } } - /** - * @private - * @return {string} - */ + /** + * @private + * @return {string} + */ getVideoSource_() { let videoSrc = this.element.getAttribute('src'); if (!videoSrc) { @@ -99,12 +99,12 @@ class AmpVideo extends AMP.BaseElement { return videoSrc; } - /** @override */ + /** @override */ isLayoutSupported(layout) { return isLayoutSizeDefined(layout); } - /** @override */ + /** @override */ buildCallback() { this.video_ = this.element.ownerDocument.createElement('video'); @@ -114,10 +114,10 @@ class AmpVideo extends AMP.BaseElement { 'No "poster" attribute has been provided for amp-video.'); } - // Enable inline play for iOS. + // Enable inline play for iOS. this.video_.setAttribute('playsinline', ''); this.video_.setAttribute('webkit-playsinline', ''); - // Disable video preload in prerender mode. + // Disable video preload in prerender mode. this.video_.setAttribute('preload', 'none'); this.propagateAttributes(ATTRS_TO_PROPAGATE_ON_BUILD, this.video_, /* opt_removeMissingAttrs */ true); @@ -129,7 +129,7 @@ class AmpVideo extends AMP.BaseElement { Services.videoManagerForDoc(this.element).register(this); } - /** @override */ + /** @override */ mutatedAttributesCallback(mutations) { if (!this.video_) { return; @@ -148,12 +148,12 @@ class AmpVideo extends AMP.BaseElement { } } - /** @override */ + /** @override */ viewportCallback(visible) { this.element.dispatchCustomEvent(VideoEvents.VISIBILITY, {visible}); } - /** @override */ + /** @override */ layoutCallback() { this.video_ = dev().assertElement(this.video_); @@ -170,7 +170,7 @@ class AmpVideo extends AMP.BaseElement { /* opt_removeMissingAttrs */ true); this.getRealChildNodes().forEach(child => { - // Skip the video we already added to the element. + // Skip the video we already added to the element. if (this.video_ === child) { return; } @@ -181,7 +181,7 @@ class AmpVideo extends AMP.BaseElement { this.video_.appendChild(child); }); - // loadPromise for media elements listens to `loadstart` + // loadPromise for media elements listens to `loadstart` return this.loadPromise(this.video_).then(() => { this.element.dispatchCustomEvent(VideoEvents.LOAD); }); @@ -241,11 +241,11 @@ class AmpVideo extends AMP.BaseElement { if (ret && ret.catch) { ret.catch(() => { - // Empty catch to prevent useless unhandled promise rejection logging. - // Play can fail for many reasons such as video getting paused before - // play() is finished. - // We use events to know the state of the video and do not care about - // the success or failure of the play()'s returned promise. + // Empty catch to prevent useless unhandled promise rejection logging. + // Play can fail for many reasons such as video getting paused before + // play() is finished. + // We use events to know the state of the video and do not care about + // the success or failure of the play()'s returned promise. }); } } @@ -304,6 +304,21 @@ class AmpVideo extends AMP.BaseElement { return isFullscreenElement(dev().assertElement(this.video_)); } + /** @override */ + getMetaData() { + return { + 'artwork': [], + 'title': '', + 'artist': '', + 'album': '', + }; + } + + /** @override */ + preimplementsMediaSessionAPI() { + return false; + } + /** @override */ getCurrentTime() { return this.video_.currentTime; diff --git a/extensions/amp-vimeo/0.1/amp-vimeo.js b/extensions/amp-vimeo/0.1/amp-vimeo.js index 20e33f5367fe..59e983d15289 100644 --- a/extensions/amp-vimeo/0.1/amp-vimeo.js +++ b/extensions/amp-vimeo/0.1/amp-vimeo.js @@ -74,13 +74,6 @@ class AmpVimeo extends AMP.BaseElement { })), '*'); } } - - /** @override */ - preimplementsMediaSessionAPI() { - // Vimeo already updates the Media Session so no need for the video - // manager to update it too - return true; - } }; AMP.registerElement('amp-vimeo', AmpVimeo); diff --git a/extensions/amp-youtube/0.1/amp-youtube.js b/extensions/amp-youtube/0.1/amp-youtube.js index 3d8719d2beed..fe14f2859ef3 100644 --- a/extensions/amp-youtube/0.1/amp-youtube.js +++ b/extensions/amp-youtube/0.1/amp-youtube.js @@ -246,13 +246,6 @@ class AmpYoutube extends AMP.BaseElement { } } - /** @override */ - preimplementsMediaSessionAPI() { - // Youtube already updates the Media Session so no need for the video - // manager to update it too - return true; - } - /** @override */ mutatedAttributesCallback(mutations) { if (mutations['data-videoid'] !== undefined) { @@ -473,6 +466,23 @@ class AmpYoutube extends AMP.BaseElement { return isFullscreenElement(dev().assertElement(this.iframe_)); } + /** @override */ + getMetaData() { + return { + 'artwork': [], + 'title': '', + 'artist': '', + 'album': '', + }; + } + + /** @override */ + preimplementsMediaSessionAPI() { + // Youtube already updates the Media Session so no need for the video + // manager to update it too + return true; + } + /** @override */ getCurrentTime() { // Not supported. diff --git a/src/service/video-manager-impl.js b/src/service/video-manager-impl.js index 80b4fd2048c1..2f126e3c5c4a 100644 --- a/src/service/video-manager-impl.js +++ b/src/service/video-manager-impl.js @@ -42,7 +42,11 @@ import {map} from '../utils/object'; import {layoutRectLtwh, RelativePositions} from '../layout-rect'; import {Animation} from '../animation'; import * as st from '../style'; +<<<<<<< HEAD import * as tr from '../transition'; +======= +import {tryParseJson} from '../json'; +>>>>>>> Implemented metadata and mediasession api for all players & added better default posters /** * @const {number} Percentage of the video that should be in viewport before it @@ -550,7 +554,7 @@ class VideoEntry { this.metaData_ = { 'artist': '', 'album': '', - 'artwork': '', + 'artwork': [], 'title': '', }; @@ -655,18 +659,68 @@ class VideoEntry { this.metaData_ = map(this.video.getMetaData()); } - if (!this.metaData_.artist) { - const artist = 'No artist'; - if (artist) { - this.metaData_.artist = artist; + if (!this.metaData_.artwork) { + const doc = this.ampdoc_.win.document; + + // Parses the schema.org json-ld formatted meta-data + const parseSchemaImage = () => { + const schema = doc.querySelector('script[type="application/ld+json"]'); + if (!schema) { + // No schema element found + return undefined; + } + const schemaJson = tryParseJson(schema.textContent); + if (!schemaJson || !schemaJson.image) { + // No image found in the schema + return undefined; + } + + if (schemaJson.image['@list'] + && schemaJson.image['@list'][0] + && typeof schemaJson.image['@list'][0] === 'string') { + return schemaJson.image['@list'][0]; + } else if (schemaJson.image[0] + && typeof schemaJson.image[0] === 'string') { + // Return the first image + return schemaJson.image[0]; + } else if (typeof schemaJson.image === 'string') { + return schemaJson.image; + } else { + return undefined; + } + }; + + // Parses the og:image if it exists + const parseOgImage = () => { + const metaTag = doc.querySelector('meta[property="og:image"]'); + if (metaTag) { + return metaTage.getAttribute('content'); + } else { + return undefined; + } + }; + + // Parses the website's Favicon + const parseFavicon = () => { + const linkTag = doc.querySelector('link[rel="shortcut icon"]') + || doc.querySelector('link[rel="icon"]'); + if (linkTag) { + return linkTag.getAttribute('href'); + } else { + return undefined; + } } - } - if (!this.metaData_.artwork) { const posterUrl = this.video.element.getAttribute('poster') - || this.internalElement_.getAttribute('poster'); + || this.internalElement_.getAttribute('poster') + || parseSchemaImage() + || parseOgImage() + || parseFavicon(); + if (posterUrl) { - this.metaData_.artwork = posterUrl; + this.metaData_.artwork = [{ + 'src':posterUrl, + }]; } } @@ -680,10 +734,6 @@ class VideoEntry { this.metaData_.title = title; } } - - if (!this.metaData_.album) { - this.metaData_.album = 'No album'; - } } /** @@ -698,7 +748,6 @@ class VideoEntry { const win = this.ampdoc_.win; const navigator = win.navigator; if ('mediaSession' in navigator && win.MediaMetadata) { - navigator.mediaSession.metadata = new win.MediaMetadata(this.metaData_); navigator.mediaSession.setActionHandler('play', function() { diff --git a/src/video-interface.js b/src/video-interface.js index 1f5062a3c2a1..577b3ea210ec 100644 --- a/src/video-interface.js +++ b/src/video-interface.js @@ -123,7 +123,14 @@ export class VideoInterface { * album (string): Name of the video's album if it exists * @return {!VideoMetaDef} metadata */ - getMetaData() {} + getMetaData() { + return { + 'artwork': '', + 'title': '', + 'artist': '', + 'album': '', + }; + } /** * If this returns true then it will be assumed that the player implements @@ -133,7 +140,9 @@ export class VideoInterface { * * @return {boolean} */ - preimplementsMediaSessionAPI() {} + preimplementsMediaSessionAPI() { + return false; + } /** From 7e142ecb222f39977992182e01d6e1e5cbaf5fd6 Mon Sep 17 00:00:00 2001 From: Wassim Gharbi Date: Fri, 28 Jul 2017 11:01:38 -0700 Subject: [PATCH 4/7] More requested changes --- src/service/video-manager-impl.js | 4 ++-- test/functional/test-video-manager.js | 31 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/service/video-manager-impl.js b/src/service/video-manager-impl.js index 2f126e3c5c4a..fd288d17bb00 100644 --- a/src/service/video-manager-impl.js +++ b/src/service/video-manager-impl.js @@ -694,7 +694,7 @@ class VideoEntry { const parseOgImage = () => { const metaTag = doc.querySelector('meta[property="og:image"]'); if (metaTag) { - return metaTage.getAttribute('content'); + return metaTag.getAttribute('content'); } else { return undefined; } @@ -719,7 +719,7 @@ class VideoEntry { if (posterUrl) { this.metaData_.artwork = [{ - 'src':posterUrl, + 'src': posterUrl, }]; } } diff --git a/test/functional/test-video-manager.js b/test/functional/test-video-manager.js index f76caab15e37..4ec02d02114c 100644 --- a/test/functional/test-video-manager.js +++ b/test/functional/test-video-manager.js @@ -228,6 +228,22 @@ describes.fakeWin('VideoManager', { }); + it('should change media session when video starts playing', () => { + + videoManager.register(impl); + + const mediaSessionSpy = sandbox.spy( + videoManager.getEntryForVideo_(impl), + 'updateMediaSession_' + ); + + impl.play(); + + return listenOncePromise(video, VideoEvents.PLAYING).then(() => { + expect(mediaSessionSpy.called).to.be.true; + }); + }); + beforeEach(() => { sandbox = sinon.sandbox.create(); klass = createFakeVideoPlayerClass(env.win); @@ -526,6 +542,21 @@ function createFakeVideoPlayerClass(win) { return this.currentTime_; } + /** @override */ + getMetaData() { + return { + 'artwork': '', + 'title': '', + 'artist': '', + 'album': '', + }; + } + + /** @override */ + preimplementsMediaSessionAPI() { + return false; + } + /** @override */ getDuration() { return this.duration_; From acc0054b5e2ec9b0d791ad84bc1e1515a00faa0b Mon Sep 17 00:00:00 2001 From: Wassim Gharbi Date: Sat, 29 Jul 2017 15:48:43 -0700 Subject: [PATCH 5/7] Created media session helper and implemented tests --- src/mediasession-helper.js | 124 +++++++++++++++++++ src/service/video-manager-impl.js | 127 +++++--------------- test/functional/test-mediasession-helper.js | 126 +++++++++++++++++++ test/functional/test-video-manager.js | 17 --- 4 files changed, 283 insertions(+), 111 deletions(-) create mode 100644 src/mediasession-helper.js create mode 100644 test/functional/test-mediasession-helper.js diff --git a/src/mediasession-helper.js b/src/mediasession-helper.js new file mode 100644 index 000000000000..b030269610a8 --- /dev/null +++ b/src/mediasession-helper.js @@ -0,0 +1,124 @@ +/** + * Copyright 2017 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 {tryParseJson} from './json'; + +/** + * Updates the Media Session API's metadata + * @param {!./ampdoc-impl.AmpDoc} ampdoc + * @param {!./video-interface.VideoMetaDef} metaData + * @param {function} playHandler + * @param {function} pauseHandler + */ +export function setMediaSession(ampdoc, + metaData, + playHandler = null, + pauseHandler = null) { + const win = ampdoc.win; + const navigator = win.navigator; + if ('mediaSession' in navigator && win.MediaMetadata) { + // Clear mediaSession (required to fix a bug when switching between two + // videos) + navigator.mediaSession.metadata = new win.MediaMetadata({ + title: '', + artist: '', + album: '', + artwork: [ + { src: ''}, + ] + }); + // Add metaData + navigator.mediaSession.metadata = new win.MediaMetadata(metaData); + + navigator.mediaSession.setActionHandler('play', playHandler); + navigator.mediaSession.setActionHandler('pause', pauseHandler); + + // TODO(@wassgha) Implement seek & next/previous + } +} + + +/** + * Parses the schema.org json-ld formatted meta-data, looks for the page's + * featured image and returns it + * @param {!./ampdoc-impl.AmpDoc} ampdoc + * @return {string|undefined} + */ +export function parseSchemaImage(ampdoc) { + const doc = ampdoc.win.document; + const schema = doc.querySelector('script[type="application/ld+json"]'); + if (!schema) { + // No schema element found + return undefined; + } + const schemaJson = tryParseJson(schema.textContent); + if (!schemaJson || !schemaJson.image) { + // No image found in the schema + return undefined; + } + + // Image definition in schema could be one of : + if (schemaJson.image['@list'] + && schemaJson.image['@list'][0] + && typeof schemaJson.image['@list'][0] === 'string') { + // 1. "image": {.., "@list": ["http://.."], ..} + return schemaJson.image['@list'][0]; + } else if (schemaJson.image['url'] + && typeof schemaJson.image['url'] === 'string') { + // 2. "image": {.., "url": "http://..", ..} + return schemaJson.image['url']; + } else if (schemaJson.image[0] + && typeof schemaJson.image[0] === 'string') { + // 3. "image": ["http://.. "] + return schemaJson.image[0]; + } else if (typeof schemaJson.image === 'string') { + // 4. "image": "http://..", + return schemaJson.image; + } else { + return undefined; + } +}; + +/** + * Parses the og:image if it exists and returns it + * @param {!./ampdoc-impl.AmpDoc} ampdoc + * @return {string|undefined} + */ +export function parseOgImage(ampdoc) { + const doc = ampdoc.win.document; + const metaTag = doc.querySelector('meta[property="og:image"]'); + if (metaTag) { + return metaTag.getAttribute('content'); + } else { + return undefined; + } +}; + +/** + * Parses the website's Favicon and returns it + * @param {!./ampdoc-impl.AmpDoc} ampdoc + * @return {string|undefined} + */ +export function parseFavicon(ampdoc) { + const doc = ampdoc.win.document; + const linkTag = doc.querySelector('link[rel="shortcut icon"]') + || doc.querySelector('link[rel="icon"]'); + if (linkTag) { + return linkTag.getAttribute('href'); + } else { + return undefined; + } +} diff --git a/src/service/video-manager-impl.js b/src/service/video-manager-impl.js index fd288d17bb00..bbf319f2826a 100644 --- a/src/service/video-manager-impl.js +++ b/src/service/video-manager-impl.js @@ -40,14 +40,15 @@ import { } from './position-observer-impl'; import {map} from '../utils/object'; import {layoutRectLtwh, RelativePositions} from '../layout-rect'; +import { + parseSchemaImage, + parseOgImage, + parseFavicon, + setMediaSession, +} from '../mediasession-helper'; import {Animation} from '../animation'; import * as st from '../style'; -<<<<<<< HEAD import * as tr from '../transition'; -======= -import {tryParseJson} from '../json'; ->>>>>>> Implemented metadata and mediasession api for all players & added better default posters - /** * @const {number} Percentage of the video that should be in viewport before it * is considered visible. @@ -589,7 +590,22 @@ class VideoEntry { */ videoPlayed_() { this.isPlaying_ = true; - this.updateMediaSession_(); + + if (!this.video.preimplementsMediaSessionAPI()) { + const playHandler = () => { + this.video.play(/*isAutoplay*/ false); + }; + const pauseHandler = () => { + this.video.pause(/*isAutoplay*/ false); + } + // Update the media session + setMediaSession( + this.ampdoc_, + this.metaData_, + playHandler, + pauseHandler + ); + } this.actionSessionManager_.beginSession(); if (this.isVisible_) { @@ -637,7 +653,7 @@ class VideoEntry { this.inlineVidRect_ = this.video.element./*OK*/getBoundingClientRect(); }); - this.getMetaData_(); + this.fillMetaData_(); this.updateVisibility(); if (this.isVisible_) { @@ -650,7 +666,7 @@ class VideoEntry { * Gets the provided metadata and fills in missing fields * @private */ - getMetaData_() { + fillMetaData_() { if (this.video.preimplementsMediaSessionAPI()) { return; } @@ -659,63 +675,12 @@ class VideoEntry { this.metaData_ = map(this.video.getMetaData()); } - if (!this.metaData_.artwork) { - const doc = this.ampdoc_.win.document; - - // Parses the schema.org json-ld formatted meta-data - const parseSchemaImage = () => { - const schema = doc.querySelector('script[type="application/ld+json"]'); - if (!schema) { - // No schema element found - return undefined; - } - const schemaJson = tryParseJson(schema.textContent); - if (!schemaJson || !schemaJson.image) { - // No image found in the schema - return undefined; - } - - if (schemaJson.image['@list'] - && schemaJson.image['@list'][0] - && typeof schemaJson.image['@list'][0] === 'string') { - return schemaJson.image['@list'][0]; - } else if (schemaJson.image[0] - && typeof schemaJson.image[0] === 'string') { - // Return the first image - return schemaJson.image[0]; - } else if (typeof schemaJson.image === 'string') { - return schemaJson.image; - } else { - return undefined; - } - }; - - // Parses the og:image if it exists - const parseOgImage = () => { - const metaTag = doc.querySelector('meta[property="og:image"]'); - if (metaTag) { - return metaTag.getAttribute('content'); - } else { - return undefined; - } - }; - - // Parses the website's Favicon - const parseFavicon = () => { - const linkTag = doc.querySelector('link[rel="shortcut icon"]') - || doc.querySelector('link[rel="icon"]'); - if (linkTag) { - return linkTag.getAttribute('href'); - } else { - return undefined; - } - } - + if (!this.metaData_.artwork || this.metaData_.artwork.length == 0) { const posterUrl = this.video.element.getAttribute('poster') - || this.internalElement_.getAttribute('poster') - || parseSchemaImage() - || parseOgImage() - || parseFavicon(); + || this.internalElement_.getAttribute('poster') + || parseSchemaImage(this.ampdoc_) + || parseOgImage(this.ampdoc_) + || parseFavicon(this.ampdoc_); if (posterUrl) { this.metaData_.artwork = [{ @@ -726,42 +691,16 @@ class VideoEntry { if (!this.metaData_.title) { const title = this.video.element.getAttribute('title') - || this.video.element.getAttribute('aria-label') - || this.internalElement_.getAttribute('title') - || this.internalElement_.getAttribute('aria-label') - || this.ampdoc_.win.document.title; + || this.video.element.getAttribute('aria-label') + || this.internalElement_.getAttribute('title') + || this.internalElement_.getAttribute('aria-label') + || this.ampdoc_.win.document.title; if (title) { this.metaData_.title = title; } } } - /** - * Gets the provided metadata and fills in missing fields - * @private - */ - updateMediaSession_() { - if (this.video.preimplementsMediaSessionAPI()) { - return; - } - - const win = this.ampdoc_.win; - const navigator = win.navigator; - if ('mediaSession' in navigator && win.MediaMetadata) { - navigator.mediaSession.metadata = new win.MediaMetadata(this.metaData_); - - navigator.mediaSession.setActionHandler('play', function() { - this.video.play(/*isAutoplay*/ false); - }); - navigator.mediaSession.setActionHandler('pause', function() { - this.video.pause(); - }); - - // TODO(@wassgha) Implement seek & next/previous - } - - } - /** * Called when visibility of a video changes. * @private diff --git a/test/functional/test-mediasession-helper.js b/test/functional/test-mediasession-helper.js new file mode 100644 index 000000000000..10dce56cecc7 --- /dev/null +++ b/test/functional/test-mediasession-helper.js @@ -0,0 +1,126 @@ +/** + * Copyright 2017 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 { + setMediaSession, + parseSchemaImage, + parseOgImage, + parseFavicon, +} from '../../src/mediasession-helper'; + +const template = ` + + + + + + +

Yesterday Something Happened

+

Lorem ipsum dolor set amet..

+ +`; + + +describes.sandboxed('MediaSessionAPI Helper Functions', {}, () => { + let ampdoc; + + beforeEach(() => { + document.documentElement.innerHTML = template; + ampdoc = { + win: { + document: document, + navigator: { + mediaSession: { + metadata: { + 'artist': '', + 'album': '', + 'artwork': [], + 'title': '', + }, + setActionHandler: () => { + }, + } + }, + MediaMetadata: Object, + }, + }; + }); + + afterEach(() => { + document.documentElement.innerHTML = ''; + }); + + it('should parse the schema and find the image', () => { + expect(parseSchemaImage(ampdoc)).to.equal('http://example.com/image.png'); + }); + + it('should parse the og-image', () => { + expect(parseOgImage(ampdoc)).to.equal('http://example.com/og-image.png'); + }); + + it('should parse the favicon', () => { + expect(parseFavicon(ampdoc)).to.equal('http://example.com/favicon.ico'); + }); + + it('should set the media session', () => { + expect(ampdoc.win.navigator.mediaSession.metadata).to.deep.equal({ + 'artist': '', + 'album': '', + 'artwork': [], + 'title': '', + }); + const fakeMetaData = { + 'artist': 'Some artist', + 'album': 'Some album', + 'artwork': [ + 'http://example.com/image.png' + ], + 'title': 'Some title', + }; + setMediaSession(ampdoc, fakeMetaData); + expect(ampdoc.win.navigator.mediaSession.metadata).to.deep.equal(fakeMetaData); + }); +}); diff --git a/test/functional/test-video-manager.js b/test/functional/test-video-manager.js index 4ec02d02114c..340c7b42fb59 100644 --- a/test/functional/test-video-manager.js +++ b/test/functional/test-video-manager.js @@ -227,23 +227,6 @@ describes.fakeWin('VideoManager', { }); }); - - it('should change media session when video starts playing', () => { - - videoManager.register(impl); - - const mediaSessionSpy = sandbox.spy( - videoManager.getEntryForVideo_(impl), - 'updateMediaSession_' - ); - - impl.play(); - - return listenOncePromise(video, VideoEvents.PLAYING).then(() => { - expect(mediaSessionSpy.called).to.be.true; - }); - }); - beforeEach(() => { sandbox = sinon.sandbox.create(); klass = createFakeVideoPlayerClass(env.win); From ca3ff51a8985f7728346b67d18b9e1302c61480c Mon Sep 17 00:00:00 2001 From: Wassim Gharbi Date: Mon, 31 Jul 2017 13:59:29 -0700 Subject: [PATCH 6/7] Requested changes for MediaSessionAPI implementation --- extensions/amp-3q-player/0.1/amp-3q-player.js | 9 +- .../amp-brid-player/0.1/amp-brid-player.js | 9 +- .../amp-dailymotion/0.1/amp-dailymotion.js | 9 +- extensions/amp-ima-video/0.1/amp-ima-video.js | 9 +- .../0.1/amp-nexxtv-player.js | 9 +- .../0.1/amp-ooyala-player.js | 9 +- extensions/amp-video/0.1/amp-video.js | 19 ++-- extensions/amp-youtube/0.1/amp-youtube.js | 9 +- src/mediasession-helper.js | 87 ++++++++++--------- src/service/video-manager-impl.js | 38 ++++---- src/video-interface.js | 19 ++-- test/functional/test-mediasession-helper.js | 19 ++-- test/functional/test-video-manager.js | 9 +- 13 files changed, 103 insertions(+), 151 deletions(-) diff --git a/extensions/amp-3q-player/0.1/amp-3q-player.js b/extensions/amp-3q-player/0.1/amp-3q-player.js index 15ce8eed43c7..b795ac9a512c 100644 --- a/extensions/amp-3q-player/0.1/amp-3q-player.js +++ b/extensions/amp-3q-player/0.1/amp-3q-player.js @@ -249,13 +249,8 @@ class Amp3QPlayer extends AMP.BaseElement { } /** @override */ - getMetaData() { - return { - 'artwork': [], - 'title': '', - 'artist': '', - 'album': '', - }; + getMetadata() { + // Not implemented } /** @override */ diff --git a/extensions/amp-brid-player/0.1/amp-brid-player.js b/extensions/amp-brid-player/0.1/amp-brid-player.js index e946e37daaeb..5b48eeced6f6 100644 --- a/extensions/amp-brid-player/0.1/amp-brid-player.js +++ b/extensions/amp-brid-player/0.1/amp-brid-player.js @@ -320,13 +320,8 @@ class AmpBridPlayer extends AMP.BaseElement { } /** @override */ - getMetaData() { - return { - 'artwork': [], - 'title': '', - 'artist': '', - 'album': '', - }; + getMetadata() { + // Not implemented } /** @override */ diff --git a/extensions/amp-dailymotion/0.1/amp-dailymotion.js b/extensions/amp-dailymotion/0.1/amp-dailymotion.js index 69cfd677b7d9..5f7469b4f00d 100644 --- a/extensions/amp-dailymotion/0.1/amp-dailymotion.js +++ b/extensions/amp-dailymotion/0.1/amp-dailymotion.js @@ -393,13 +393,8 @@ class AmpDailymotion extends AMP.BaseElement { } /** @override */ - getMetaData() { - return { - 'artwork': [], - 'title': '', - 'artist': '', - 'album': '', - }; + getMetadata() { + // Not implemented } /** @override */ diff --git a/extensions/amp-ima-video/0.1/amp-ima-video.js b/extensions/amp-ima-video/0.1/amp-ima-video.js index f1c2bbb115e1..0012bab67f00 100644 --- a/extensions/amp-ima-video/0.1/amp-ima-video.js +++ b/extensions/amp-ima-video/0.1/amp-ima-video.js @@ -311,13 +311,8 @@ class AmpImaVideo extends AMP.BaseElement { } /** @override */ - getMetaData() { - return { - 'artwork': [], - 'title': '', - 'artist': '', - 'album': '', - }; + getMetadata() { + // Not implemented } /** @override */ diff --git a/extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js b/extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js index edfbbf4a9824..046e935f4ab5 100644 --- a/extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js +++ b/extensions/amp-nexxtv-player/0.1/amp-nexxtv-player.js @@ -275,13 +275,8 @@ class AmpNexxtvPlayer extends AMP.BaseElement { } /** @override */ - getMetaData() { - return { - 'artwork': [], - 'title': '', - 'artist': '', - 'album': '', - }; + getMetadata() { + // Not implemented } /** @override */ diff --git a/extensions/amp-ooyala-player/0.1/amp-ooyala-player.js b/extensions/amp-ooyala-player/0.1/amp-ooyala-player.js index a06810bb9c7c..9cc160dedb47 100644 --- a/extensions/amp-ooyala-player/0.1/amp-ooyala-player.js +++ b/extensions/amp-ooyala-player/0.1/amp-ooyala-player.js @@ -260,13 +260,8 @@ class AmpOoyalaPlayer extends AMP.BaseElement { } /** @override */ - getMetaData() { - return { - 'artwork': [], - 'title': '', - 'artist': '', - 'album': '', - }; + getMetadata() { + // Not implemented } /** @override */ diff --git a/extensions/amp-video/0.1/amp-video.js b/extensions/amp-video/0.1/amp-video.js index b8c1d3190189..09401a31ecf4 100644 --- a/extensions/amp-video/0.1/amp-video.js +++ b/extensions/amp-video/0.1/amp-video.js @@ -305,13 +305,18 @@ class AmpVideo extends AMP.BaseElement { } /** @override */ - getMetaData() { - return { - 'artwork': [], - 'title': '', - 'artist': '', - 'album': '', - }; + getMetadata() { + const poster = this.element.getAttribute('poster'); + if (poster) { + return { + 'title': '', + 'artist': '', + 'album': '', + 'artwork': [ + {'src': poster}, + ], + }; + } } /** @override */ diff --git a/extensions/amp-youtube/0.1/amp-youtube.js b/extensions/amp-youtube/0.1/amp-youtube.js index fe14f2859ef3..16a67d0947dd 100644 --- a/extensions/amp-youtube/0.1/amp-youtube.js +++ b/extensions/amp-youtube/0.1/amp-youtube.js @@ -467,13 +467,8 @@ class AmpYoutube extends AMP.BaseElement { } /** @override */ - getMetaData() { - return { - 'artwork': [], - 'title': '', - 'artist': '', - 'album': '', - }; + getMetadata() { + // Not implemented } /** @override */ diff --git a/src/mediasession-helper.js b/src/mediasession-helper.js index b030269610a8..76bb161aabfe 100644 --- a/src/mediasession-helper.js +++ b/src/mediasession-helper.js @@ -16,32 +16,35 @@ import {tryParseJson} from './json'; +/** @const {./video-interface.VideoMetaDef} Dummy metadata used to fix a bug */ +export const EMPTY_METADATA = { + 'title': '', + 'artist': '', + 'album': '', + 'artwork': [ + {'src': ''}, + ], +}; + /** * Updates the Media Session API's metadata - * @param {!./ampdoc-impl.AmpDoc} ampdoc - * @param {!./video-interface.VideoMetaDef} metaData - * @param {function} playHandler - * @param {function} pauseHandler + * @param {!./service/ampdoc-impl.AmpDoc} ampdoc + * @param {!./video-interface.VideoMetaDef} metadata + * @param {function()=} playHandler + * @param {function()=} pauseHandler */ export function setMediaSession(ampdoc, - metaData, - playHandler = null, - pauseHandler = null) { + metadata, + playHandler, + pauseHandler) { const win = ampdoc.win; const navigator = win.navigator; if ('mediaSession' in navigator && win.MediaMetadata) { // Clear mediaSession (required to fix a bug when switching between two // videos) - navigator.mediaSession.metadata = new win.MediaMetadata({ - title: '', - artist: '', - album: '', - artwork: [ - { src: ''}, - ] - }); - // Add metaData - navigator.mediaSession.metadata = new win.MediaMetadata(metaData); + navigator.mediaSession.metadata = new win.MediaMetadata(EMPTY_METADATA); + // Add metadata + navigator.mediaSession.metadata = new win.MediaMetadata(metadata); navigator.mediaSession.setActionHandler('play', playHandler); navigator.mediaSession.setActionHandler('pause', pauseHandler); @@ -54,7 +57,7 @@ export function setMediaSession(ampdoc, /** * Parses the schema.org json-ld formatted meta-data, looks for the page's * featured image and returns it - * @param {!./ampdoc-impl.AmpDoc} ampdoc + * @param {!./service/ampdoc-impl.AmpDoc} ampdoc * @return {string|undefined} */ export function parseSchemaImage(ampdoc) { @@ -62,39 +65,39 @@ export function parseSchemaImage(ampdoc) { const schema = doc.querySelector('script[type="application/ld+json"]'); if (!schema) { // No schema element found - return undefined; + return; } const schemaJson = tryParseJson(schema.textContent); - if (!schemaJson || !schemaJson.image) { + if (!schemaJson || !schemaJson['image']) { // No image found in the schema - return undefined; + return; } // Image definition in schema could be one of : - if (schemaJson.image['@list'] - && schemaJson.image['@list'][0] - && typeof schemaJson.image['@list'][0] === 'string') { - // 1. "image": {.., "@list": ["http://.."], ..} - return schemaJson.image['@list'][0]; - } else if (schemaJson.image['url'] - && typeof schemaJson.image['url'] === 'string') { - // 2. "image": {.., "url": "http://..", ..} - return schemaJson.image['url']; - } else if (schemaJson.image[0] - && typeof schemaJson.image[0] === 'string') { - // 3. "image": ["http://.. "] - return schemaJson.image[0]; - } else if (typeof schemaJson.image === 'string') { - // 4. "image": "http://..", - return schemaJson.image; + if (typeof schemaJson['image'] === 'string') { + // 1. "image": "http://..", + return schemaJson['image']; + } else if (schemaJson['image']['@list'] + && schemaJson['image']['@list'][0] + && typeof schemaJson['image']['@list'][0] === 'string') { + // 2. "image": {.., "@list": ["http://.."], ..} + return schemaJson['image']['@list'][0]; + } else if (schemaJson['image']['url'] + && typeof schemaJson['image']['url'] === 'string') { + // 3. "image": {.., "url": "http://..", ..} + return schemaJson['image']['url']; + } else if (schemaJson['image'][0] + && typeof schemaJson['image'][0] === 'string') { + // 4. "image": ["http://.. "] + return schemaJson['image'][0]; } else { - return undefined; + return; } }; /** * Parses the og:image if it exists and returns it - * @param {!./ampdoc-impl.AmpDoc} ampdoc + * @param {!./service/ampdoc-impl.AmpDoc} ampdoc * @return {string|undefined} */ export function parseOgImage(ampdoc) { @@ -103,13 +106,13 @@ export function parseOgImage(ampdoc) { if (metaTag) { return metaTag.getAttribute('content'); } else { - return undefined; + return; } }; /** * Parses the website's Favicon and returns it - * @param {!./ampdoc-impl.AmpDoc} ampdoc + * @param {!./service/ampdoc-impl.AmpDoc} ampdoc * @return {string|undefined} */ export function parseFavicon(ampdoc) { @@ -119,6 +122,6 @@ export function parseFavicon(ampdoc) { if (linkTag) { return linkTag.getAttribute('href'); } else { - return undefined; + return; } } diff --git a/src/service/video-manager-impl.js b/src/service/video-manager-impl.js index bbf319f2826a..869c08128a05 100644 --- a/src/service/video-manager-impl.js +++ b/src/service/video-manager-impl.js @@ -41,6 +41,7 @@ import { import {map} from '../utils/object'; import {layoutRectLtwh, RelativePositions} from '../layout-rect'; import { + EMPTY_METADATA, parseSchemaImage, parseOgImage, parseFavicon, @@ -546,18 +547,10 @@ class VideoEntry { this.hasFullscreenOnLandscape = fsOnLandscapeAttr == '' || fsOnLandscapeAttr == 'always'; - listenOncePromise(element, VideoEvents.LOAD) - .then(() => this.videoLoaded()); - // Media Session API Variables /** @private {!../video-interface.VideoMetaDef} */ - this.metaData_ = { - 'artist': '', - 'album': '', - 'artwork': [], - 'title': '', - }; + this.metadata_ = EMPTY_METADATA; listenOncePromise(element, VideoEvents.LOAD) .then(() => this.videoLoaded()); @@ -596,12 +589,12 @@ class VideoEntry { this.video.play(/*isAutoplay*/ false); }; const pauseHandler = () => { - this.video.pause(/*isAutoplay*/ false); - } + this.video.pause(); + }; // Update the media session setMediaSession( this.ampdoc_, - this.metaData_, + this.metadata_, playHandler, pauseHandler ); @@ -653,7 +646,7 @@ class VideoEntry { this.inlineVidRect_ = this.video.element./*OK*/getBoundingClientRect(); }); - this.fillMetaData_(); + this.fillMediaSessionMetadata_(); this.updateVisibility(); if (this.isVisible_) { @@ -666,37 +659,36 @@ class VideoEntry { * Gets the provided metadata and fills in missing fields * @private */ - fillMetaData_() { + fillMediaSessionMetadata_() { if (this.video.preimplementsMediaSessionAPI()) { return; } - if (this.video.getMetaData()) { - this.metaData_ = map(this.video.getMetaData()); + if (this.video.getMetadata()) { + const mapped = map(this.video.getMetadata()); + this.metadata_ = /** @type {!../video-interface.VideoMetaDef} */ (mapped); } - if (!this.metaData_.artwork || this.metaData_.artwork.length == 0) { - const posterUrl = this.video.element.getAttribute('poster') - || this.internalElement_.getAttribute('poster') - || parseSchemaImage(this.ampdoc_) + if (!this.metadata_.artwork || this.metadata_.artwork.length == 0) { + const posterUrl = parseSchemaImage(this.ampdoc_) || parseOgImage(this.ampdoc_) || parseFavicon(this.ampdoc_); if (posterUrl) { - this.metaData_.artwork = [{ + this.metadata_.artwork = [{ 'src': posterUrl, }]; } } - if (!this.metaData_.title) { + if (!this.metadata_.title) { const title = this.video.element.getAttribute('title') || this.video.element.getAttribute('aria-label') || this.internalElement_.getAttribute('title') || this.internalElement_.getAttribute('aria-label') || this.ampdoc_.win.document.title; if (title) { - this.metaData_.title = title; + this.metadata_.title = title; } } } diff --git a/src/video-interface.js b/src/video-interface.js index 577b3ea210ec..5ee4f067b384 100644 --- a/src/video-interface.js +++ b/src/video-interface.js @@ -18,7 +18,7 @@ import {ActionTrust} from './action-trust'; /* eslint no-unused-vars: 0 */ /** * @typedef {{ - * artwork: string, + * artwork: Array, * title: string, * album: string, * artist: string, @@ -121,28 +121,19 @@ export class VideoInterface { * title (string): Name of the video * artist (string): Name of the video's author/artist * album (string): Name of the video's album if it exists - * @return {!VideoMetaDef} metadata + * @return {!VideoMetaDef|undefined} metadata */ - getMetaData() { - return { - 'artwork': '', - 'title': '', - 'artist': '', - 'album': '', - }; - } + getMetadata() {} /** * If this returns true then it will be assumed that the player implements * the MediaSession API internally so that the video manager does not override - * it. If not, the video manager will use the metaData variable as well as + * it. If not, the video manager will use the metadata variable as well as * inferred meta-data to update the video's Media Session notification. * * @return {boolean} */ - preimplementsMediaSessionAPI() { - return false; - } + preimplementsMediaSessionAPI() {} /** diff --git a/test/functional/test-mediasession-helper.js b/test/functional/test-mediasession-helper.js index 10dce56cecc7..747a9cc8dff3 100644 --- a/test/functional/test-mediasession-helper.js +++ b/test/functional/test-mediasession-helper.js @@ -71,20 +71,20 @@ describes.sandboxed('MediaSessionAPI Helper Functions', {}, () => { document.documentElement.innerHTML = template; ampdoc = { win: { - document: document, - navigator: { - mediaSession: { - metadata: { + 'document': document, + 'navigator': { + 'mediaSession': { + 'metadata': { 'artist': '', 'album': '', 'artwork': [], 'title': '', }, - setActionHandler: () => { + 'setActionHandler': () => { }, - } + }, }, - MediaMetadata: Object, + 'MediaMetadata': Object, }, }; }); @@ -116,11 +116,12 @@ describes.sandboxed('MediaSessionAPI Helper Functions', {}, () => { 'artist': 'Some artist', 'album': 'Some album', 'artwork': [ - 'http://example.com/image.png' + 'http://example.com/image.png', ], 'title': 'Some title', }; setMediaSession(ampdoc, fakeMetaData); - expect(ampdoc.win.navigator.mediaSession.metadata).to.deep.equal(fakeMetaData); + const newMetaData = ampdoc.win.navigator.mediaSession.metadata; + expect(newMetaData).to.deep.equal(fakeMetaData); }); }); diff --git a/test/functional/test-video-manager.js b/test/functional/test-video-manager.js index 340c7b42fb59..bdb9de859b78 100644 --- a/test/functional/test-video-manager.js +++ b/test/functional/test-video-manager.js @@ -526,13 +526,8 @@ function createFakeVideoPlayerClass(win) { } /** @override */ - getMetaData() { - return { - 'artwork': '', - 'title': '', - 'artist': '', - 'album': '', - }; + getMetadata() { + // Not supported } /** @override */ From 588aeb1695de2146b3e5ff1de6c88a20410bcdd8 Mon Sep 17 00:00:00 2001 From: Wassim Gharbi Date: Tue, 1 Aug 2017 14:20:45 -0700 Subject: [PATCH 7/7] Fixed test --- src/mediasession-helper.js | 7 +- src/video-interface.js | 2 +- test/functional/test-mediasession-helper.js | 95 ++++++++++++--------- 3 files changed, 56 insertions(+), 48 deletions(-) diff --git a/src/mediasession-helper.js b/src/mediasession-helper.js index 76bb161aabfe..d13f52f176d4 100644 --- a/src/mediasession-helper.js +++ b/src/mediasession-helper.js @@ -78,16 +78,13 @@ export function parseSchemaImage(ampdoc) { // 1. "image": "http://..", return schemaJson['image']; } else if (schemaJson['image']['@list'] - && schemaJson['image']['@list'][0] && typeof schemaJson['image']['@list'][0] === 'string') { // 2. "image": {.., "@list": ["http://.."], ..} return schemaJson['image']['@list'][0]; - } else if (schemaJson['image']['url'] - && typeof schemaJson['image']['url'] === 'string') { + } else if (typeof schemaJson['image']['url'] === 'string') { // 3. "image": {.., "url": "http://..", ..} return schemaJson['image']['url']; - } else if (schemaJson['image'][0] - && typeof schemaJson['image'][0] === 'string') { + } else if (typeof schemaJson['image'][0] === 'string') { // 4. "image": ["http://.. "] return schemaJson['image'][0]; } else { diff --git a/src/video-interface.js b/src/video-interface.js index 5ee4f067b384..ee0b0e97ee44 100644 --- a/src/video-interface.js +++ b/src/video-interface.js @@ -117,7 +117,7 @@ export class VideoInterface { /** * Returns video's meta data (artwork, title, artist, album, etc.) for use * with the Media Session API - * artwork (string): URL to the poster image (preferably a 512x512 PNG) + * artwork (Array): URL to the poster image (preferably a 512x512 PNG) * title (string): Name of the video * artist (string): Name of the video's author/artist * album (string): Name of the video's album if it exists diff --git a/test/functional/test-mediasession-helper.js b/test/functional/test-mediasession-helper.js index 747a9cc8dff3..f1ce57b832d0 100644 --- a/test/functional/test-mediasession-helper.js +++ b/test/functional/test-mediasession-helper.js @@ -21,54 +21,63 @@ import { parseFavicon, } from '../../src/mediasession-helper'; -const template = ` - - - - - - -

Yesterday Something Happened

-

Lorem ipsum dolor set amet..

- +const schemaTemplate = ` +{ + "@context": "http://schema.org", + "@type": "NewsArticle", + "mainEntityOfPage": "something", + "headline": "Something Happened", + "datePublished": "Fri Jul 28 12:45:00 EDT 2017", + "dateModified": "Fri Jul 28 12:45:00 EDT 2017", + "description": "Appearantly, yesterday something happened", + "author": { + "@type": "Person", + "name": "Awesome Author" + }, + "publisher": { + "@type": "Organization", + "name": "Aperture Science", + "logo": { + "@type": "ImageObject", + "url": "logo-url", + "width": 133, + "height": 60 + } + }, + "image": { + "@type": "ImageObject", + "url": "http://example.com/image.png", + "height": 392, + "width": 696 + } +} `; describes.sandboxed('MediaSessionAPI Helper Functions', {}, () => { let ampdoc; + let favicon; + let schema; + let ogImage; + let head; beforeEach(() => { - document.documentElement.innerHTML = template; + head = document.querySelector('head'); + // Favicon + favicon = document.createElement('link'); + favicon.setAttribute('rel', 'icon'); + favicon.setAttribute('href', 'http://example.com/favicon.ico'); + head.appendChild(favicon); + // Schema + schema = document.createElement('script'); + schema.setAttribute('type', 'application/ld+json'); + schema.innerHTML = schemaTemplate; + head.appendChild(schema); + // og-image + ogImage = document.createElement('meta'); + ogImage.setAttribute('property', 'og:image'); + ogImage.setAttribute('content', 'http://example.com/og-image.png'); + head.appendChild(ogImage); ampdoc = { win: { 'document': document, @@ -90,7 +99,9 @@ describes.sandboxed('MediaSessionAPI Helper Functions', {}, () => { }); afterEach(() => { - document.documentElement.innerHTML = ''; + head.removeChild(favicon); + head.removeChild(schema); + head.removeChild(ogImage); }); it('should parse the schema and find the image', () => {