AEM 6550 - AEM Dynamic Media SPA React Container for Video Backgrounds


The following steps explain creating a React SPA Container component (eaem-sites-spa-dm-video-container/components/container) extending Core Container component (core/wcm/components/container/v1/container) for Video Backgrounds and Component (eg. text) overlays...

For creating a simple SPA navigation app check this post. For product sample check adobe documentation

For configuring Dynamic Media check this post


Demo | Package Install | Github

         


Dynamic Media Configuration



Sync Configuration at Folder Level


Set Dynamic Media Video Profile


Video Renditions (Encodes)



Component Dialog


View as Published




Solution


1) Encodes for video are fetched and shown in the dropdown dynamically, when a user selects a dynamic media video...




2) To get the video dynamic renditions (encodes) of an asset,  create the following nt:file /apps/eaem-sites-spa-dm-video-container/video-dyn-renditions/video-dyn-renditions.jsp to return the encodes of a video as JSON (serves as a client side datasource for Encodes drop down, created in next steps...)


<%@include file="/libs/granite/ui/global.jsp"%>

<%@page session="false"
        import="org.apache.sling.commons.json.JSONObject"%>
<%@ page import="org.apache.sling.api.SlingHttpServletRequest" %>
<%@ page import="com.day.cq.dam.api.Asset" %>
<%@ page import="com.day.cq.dam.api.renditions.DynamicMediaRenditionProvider" %>
<%@ page import="com.day.cq.dam.api.Rendition" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.List" %>

<%
    response.setContentType("application/json");

    SlingHttpServletRequest eaemSlingRequest = slingRequest;
    String contentPath = eaemSlingRequest.getRequestPathInfo().getSuffix();

    Resource currentResource = eaemSlingRequest.getResourceResolver().getResource(contentPath);
    Asset asset = (currentResource != null ? currentResource.adaptTo(Asset.class) : null);

    JSONObject dynRenditions = new JSONObject();

    if( (asset == null) || !(asset.getMimeType().startsWith("video/")) || (asset.getMetadata("dam:scene7ID") == null)) {
        dynRenditions.write(response.getWriter());
        return;
    }

    DynamicMediaRenditionProvider dmRendProvider = sling.getService(DynamicMediaRenditionProvider.class);
    String s7Domain = String.valueOf(asset.getMetadata("dam:scene7Domain"));
    String scene7FileAvs = String.valueOf(asset.getMetadata("dam:scene7FileAvs"));

    HashMap<String, Object> rules = new HashMap<String, Object>();
    rules.put("remote", true);
    rules.put("video", true);

    List<Rendition> dmRenditions = dmRendProvider.getRenditions(asset, rules);
    JSONObject dynRendition = new JSONObject();
    String image = null, avsName = scene7FileAvs.substring(scene7FileAvs.lastIndexOf("/") + 1);

    dynRendition.put("url", getScene7Url(s7Domain, scene7FileAvs));
    dynRendition.put("image", getRendThumbnail(s7Domain, scene7FileAvs));
    dynRendition.put("name", avsName);

    dynRenditions.put(avsName, dynRendition);

    for (Rendition dmRendition : dmRenditions) {
        dynRendition = new JSONObject();

        image = dmRendition.getPath();

        if(image.endsWith(".mp4")){
            image = image.substring(0, image.lastIndexOf(".mp4"));
        }

        dynRendition.put("name", dmRendition.getName());
        dynRendition.put("image", getRendThumbnail(s7Domain, image));
        dynRendition.put("url", getScene7Url(s7Domain, dmRendition.getPath()));

        dynRenditions.put(dmRendition.getName(), dynRendition);
    }

    dynRenditions.write(response.getWriter());
%>

<%!
    private static String getScene7Url(String s7Domain, String rendPath){
        return s7Domain + "/s7viewers/html5/VideoViewer.html?asset=" + rendPath;
    }

    private static String getRendThumbnail(String s7Domain, String rendPath){
        return s7Domain + "/is/image/" + rendPath + "?fit=constrain,1&wid=200&hei=200";
    }
%>

3) Create /apps/eaem-sites-spa-dm-video-container/video-dyn-renditions/content and set the sling:resourceType to /apps/eaem-sites-spa-dm-video-container/video-dyn-renditions. This is the content node for fetching renditions...

http://localhost:4502/apps/eaem-sites-spa-dm-video-container/video-dyn-renditions/content.html/content/dam/experience-aem/Ayutthaya_360_VR.mp4



4) Create node /apps/eaem-sites-spa-dm-video-container/clientlibs/clientlib-dmvideo of type cq:ClientLibraryFolder, add String[] property categories with value [cq.authoring.dialog.all], String[] property dependencies with value lodash.


5) Create file (nt:file) /apps/eaem-sites-spa-dm-video-container/clientlibs/clientlib-dmvideo/js.txt, add

                        spa-dmvideo.js

6) Create file (nt:file) /apps/eaem-sites-spa-dm-video-container/clientlibs/clientlib-dmvideo/spa-dmvideo.js, add the following code...

(function ($, $document) {
    var DM_VIDEO_FIELD = "./eaemDMVideo",
        ENCODE_SELECT = "./eaemDMEncode",
        DM_VIDEO_RENDS_URL = "/apps/eaem-sites-spa-dm-video-container/video-dyn-renditions/content.html";

    $document.on('dialog-ready', handleVideoField);

    function handleVideoField(){
        var $videoField = $("foundation-autocomplete[name='" + DM_VIDEO_FIELD + "']");

        if(_.isEmpty($videoField)){
            return;
        }

        setSelectedEncode($videoField.val());

        $videoField.on("change", addEncodes);
    }

    function addEncodes(event){
        var $encodeField = $("coral-select[name='" + ENCODE_SELECT + "']"),
            selVideoPath = event.target.value;

        if(_.isEmpty(selVideoPath) || _.isEmpty($encodeField)){
            return;
        }

        $encodeField.find("coral-select-item").not("[value='NONE']").remove();

        loadEncodesInSelect(selVideoPath);
    }

    function getCoralSelectItem(text, value, selected){
        return '<coral-select-item value="' + value + '" ' + selected + '>' + text + '</coral-select-item>';
    }

    function loadEncodesInSelect(videoPath, selectedValue){
        var $encodeField = $("coral-select[name='" + ENCODE_SELECT + "']");

        $.ajax(DM_VIDEO_RENDS_URL + videoPath).done(function(renditions){
            _.each(renditions,function(rendition){
                $encodeField.append(getCoralSelectItem(rendition.name, rendition.name,
                                        ((selectedValue == rendition.name) ? "selected" : "")));
            });
        });
    }

    function setSelectedEncode(selVideoPath){
        if(!selVideoPath){
            return;
        }

        var dialogPath;

        try{
            dialogPath = Granite.author.DialogFrame.currentDialog.editable.slingPath;
        }catch(err){
            console.log("Error getting dialog path...", err);
        }

        if(!dialogPath){
            return;
        }

        $.ajax(dialogPath).done(function(data){
            var dmEncode = data[ENCODE_SELECT.substr(2)];

            if(!dmEncode){
                return;
            }

            loadEncodesInSelect(selVideoPath, dmEncode);
        })

    }
}(jQuery, jQuery(document)));

7) Create the component /apps/eaem-sites-spa-dm-video-container/components/container extending Core Container component core/wcm/components/container/v1/container  (core container component does not provide render script for React) for the SPA Editor. In the next step we'd be creating a react render script (coded in ES6)...




8) Add the component render script in eaem-sites-spa-dm-video-container\ui.frontend\src\components\DMVideoContainer\DMVideoContainer.js with the following code...
import React from 'react';
import {MapTo, withComponentMappingContext, Container, ResponsiveGrid, ComponentMapping} from '@adobe/cq-react-editable-components';
import {Helmet} from "react-helmet";

class EAEMContainer extends Container  {
    get containerProps() {
        let containerProps = super.containerProps;

        containerProps.style = {
            "width": '100%',
            "height": '500px'
        };

        return containerProps;
    }

    get videoDivProps() {
        return {
            "id": "eaem-dm-video-viewer",
            "style": {
                "zIndex": "0",
                "position": "relative"
            }
        };
    }

    get overlayDivProps() {
        return {
            "style" : {
                ...{
                "position": "absolute",
                "zIndex": "1"
                } ,
                ...this.props.overlayDivStyle
            }
        };
    }

    componentDidMount() {
        const timer = setInterval((() => {
            if(window.s7viewers){
                clearInterval(timer);
                this.loadVideo()
            }
        }).bind(this), 500);
    }

    loadVideo(){
        new window.s7viewers.VideoViewer({
            "containerId": "eaem-dm-video-viewer",
            "params": {
                "asset": this.props.dmVideoEncode,
                "serverurl": this.props.dmServerUrl,
                "videoserverurl": this.props.dmVideoServerUrl
            }
        }).init();
    }

    render() {
        return (
            <div {...this.containerProps}>
                {   this.props.dmVideoPath &&
                    <Helmet>
                        <script src={ this.props.dmVideoViewerPath }></script>
                    </Helmet>
                }

                {   this.props.dmVideoPath  &&
                    <div {...this.videoDivProps}>
                        <div {...this.overlayDivProps}>
                            { this.childComponents }
                            { this.placeholderComponent }
                        </div>
                    </div>
                }

                {   !this.props.dmVideoPath &&
                    <div>
                        { this.childComponents }
                        { this.placeholderComponent }
                    </div>
                }
            </div>
        );
    }
}

export default MapTo('eaem-sites-spa-dm-video-container/components/container')(EAEMContainer);

9) DMVideoContainer was imported in ui.frontend\src\components\import-components.js

                             import './DMVideoContainer/DMVideoContainer';
                             import './Page/Page';
                             import './Text/Text';

10) #88 specifies the script was mapped to component eaem-sites-spa-dm-video-container/components/container for rendering..

                             export default MapTo('eaem-sites-spa-dm-video-container/components/container')(EAEMContainer);


11) #63 adds the Video Viewer JS (eg. https://s7d1.scene7.com/s7viewers/html5/js/VideoViewer.js) to the <head> tag...

                             <Helmet>
                                    <script src={ this.props.dmVideoViewerPath }></script>
                             </Helmet>

12) #48 loadVideo() loads the video into container div eaem-dm-video-viewer

13) Create a Sling Model Exporter com.eaem.core.models.impl.EAEMDMVideoContainerModelImpl for exporting the component properties





14) Add the following code in com.eaem.core.models.impl.EAEMDMVideoContainerModelImpl.

package com.eaem.core.models.impl;

import com.adobe.cq.export.json.ContainerExporter;
import com.day.cq.dam.api.Asset;
import com.day.cq.wcm.foundation.model.responsivegrid.ResponsiveGrid;
import com.eaem.core.models.EAEMDMVideoContainerModelExporter;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import com.adobe.cq.export.json.ComponentExporter;

import javax.annotation.PostConstruct;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

import java.util.HashMap;
import java.util.Map;

@Model(
        adaptables = {SlingHttpServletRequest.class},
        adapters = {ContainerExporter.class, ComponentExporter.class},
        resourceType = {"eaem-sites-spa-dm-video-container/components/container"}
)
@Exporter(
        name = "jackson",
        extensions = {"json"}
)
@JsonSerialize(as = EAEMDMVideoContainerModelExporter.class)
public class EAEMDMVideoContainerModelImpl extends ResponsiveGrid implements EAEMDMVideoContainerModelExporter{
    @ScriptVariable
    private Resource resource;

    @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
    private String eaemDMVideo;

    @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
    private String eaemDMEncode;

    private Map<String, Object> metadata;

    @PostConstruct
    protected void initModel() {
        super.initModel();

        if( (this.resource == null) || StringUtils.isEmpty(eaemDMVideo)) {
            return;
        }

        ResourceResolver resolver = this.resource.getResourceResolver();
        Resource videoRes = resolver.getResource(eaemDMVideo);

        if(videoRes == null){
            return;
        }

        metadata = videoRes.adaptTo(Asset.class).getMetadata();
    }

    public String getDmAccountName(){
        if(metadata == null){
            return "";
        }

        String fileName = String.valueOf(metadata.get("dam:scene7File"));

        if(StringUtils.isEmpty(fileName)){
            return "";
        }

        return fileName.substring(0, fileName.indexOf("/"));
    }

    public String getDmServerUrl() {
        if(metadata == null){
            return "";
        }

        return metadata.get("dam:scene7Domain") + "is/image/";
    }

    public String getDmVideoViewerPath() {
        if(metadata == null){
            return "";
        }

        return metadata.get("dam:scene7Domain") + "s7viewers/html5/js/VideoViewer.js";
    }

    public String getDmVideoServerUrl() {
        if(metadata == null){
            return "";
        }

        return metadata.get("dam:scene7Domain") + "is/content/";
    }

    public Map<String, String> getOverlayDivStyle() {
        Map<String, String> divStyles = new HashMap<String, String>();
        ValueMap vm = this.resource.getValueMap();

        divStyles.put("top" , vm.get("overlayTop", ""));
        divStyles.put("left" , vm.get("overlayLeft", ""));
        divStyles.put("backgroundColor" , vm.get("overlayBGColor", "#FFFFFF"));
        divStyles.put("padding" , vm.get("overlayPadding", "10px 20px 10px 20px"));

        return divStyles;
    }

    public String getDmVideoPath() {
        return eaemDMVideo;
    }

    public String getDmVideoEncode() {
        return getDmAccountName() + "/" + eaemDMEncode;
    }
}

No comments:

Post a Comment