Photo & Video - Using Enact Framework
February 18, 2022
Overview
This tutorial demonstrates how to use the Enact framework to create a typical photo and video app for webOS OSE. Enact is a React-based JavaScript framework optimized for developing web apps for webOS OSE.
The Photo & Video app makes use of the Moonstone theme to compose the user interface (UI) of the app.
The Photo & Video app offers the following features:
- Displays videos and images in a single grid layout.
- Provides support for internal/external storages.
- Launches the Photo/Video player based on the content type.
- The Photo Player provides the following features:
- Displays the properties of the image such as size, resolution, and title.
- Displays an option to support Full or Fit sizes.
- Slide show with controls.
- Slide show with speed controls: Slow, Normal, or Fast.
- Zoom/Rotate/Settings.
- Transition effects on slide shows such as Fade In or Slide.
- Thumbnail view on the selected device.
- The Video Player provides the following features:
- Play/Pause/Previous/Next video controls.
- Fast forward and Backward operations.
- Drag-and-drop support on the slider.
You can use this Photo & Video app in the following ways:
- Install the app as-is on a webOS OSE target device.
- Update the source code as required and then deploy on a webOS OSE target device.
- Analyze the source code to understand the usage of the different Enact components.
Prerequisites
You must install Node.js version 10 or later. Download and install Node.js from the Node.js website.
Install the Enact command-line interface (CLI) globally by using the npm install
command:
$ npm install -g @enact/cli
Also install the webOS OSE CLI as follows:
$ npm install -g @webosose/ares-cli
Source Code
The source code is available at the samples repository. Clone this repository to download the source code on your local development system, and find the com.reference.app.photovideo directory under the ref-apps
repository.
Analyze the source code to get an understanding of the functionalities implemented in this app. Refer to the snippets provided in this section.
Using Enact Components
The below code snippet uses the Moonstone theme on the Photo & Video app. The following UI components are used in the app:
Panels
: Provides a way to manage the different screens of an app.TabLayout
: Provides the space for displaying connected devices on the Tab in webOS OSE device.MediaList
: Lists the media files (video and image) that are available on the device.
import { TabLayout, Tab } from "@enact/sandstone/TabLayout";
import { Panel, Header } from "@enact/sandstone/Panels";
...
<Panel {...rest}>
<Header
onClose={handleClose}
/>
<TabLayout>
{devices.map((device) => {
return device.deviceList.length > 0 && device.deviceList.map((deviceList, index) => {
return (
<Tab
className={css.tab} key={deviceList.uri}
icon='usb'
onTabClick={() => onSelectDevice(deviceList.uri)}
title={deviceList.name}
>
<MediaList
key={index}
videoList={videoList}
imageList={imageList}
handleNavigate={handleVideoNavigate}
/>
</Tab>
)
})
})}
</TabLayout>
</Panel>
...
VirutalGridList
- Populates the grid list display by using media files.
- Media files are rendered by using the
ImageItem
component.
import ImageItem from "@enact/sandstone/ImageItem";
import { VirtualGridList } from "@enact/sandstone/VirtualList";
...
<ImageItem
{...rest}
src={encodedPath}
placeholder={mediaList[index].mediaType && mediaList[index].mediaType === "video"? VideoImg : placeHolderImg}
onClick={() => mediaList[index].mediaType && mediaList[index].mediaType === "video" ?
handleNavigate('/videoplayer', mediaList[index], index-imageList.length):
handleNavigate('/photoPlayer', mediaList[index], index)
}
>
{mediaList[index].title}
</ImageItem>
...
<VirtualGridList
direction='vertical'
dataSize={mediaList.length}
itemRenderer={renderItem}
itemSize={{
minWidth: ri.scale(500),
minHeight: ri.scale(500)
}}
/>
...
Using LS2 API
The application uses the com.webos.service.mediaindexer
LS2 API in the following methods.
Video Player Services
getVideoList
- Gets the available video file list included in the attached devices.
- If the
uri
is specified, the video file list for the specified URI is provided. Else, the image file list for all attached devices is provided.
getVideoMetadata
- Gets the detailed metadata information of the specified URI for the given video file, such as file_size, thumbnail, file_path, duration, album, and title.
...
getVideoList: ({uri, ...rest}) => {
let params = {
uri: uri
};
return luna('com.webos.service.mediaindexer', 'getVideoList', params)(rest);
},
getVideoMetaData: ({uri, ...rest}) => {
let params = {
uri: uri
};
return luna('com.webos.service.mediaindexer', 'getVideoMetadata', params)(rest);
}
...
Photo Player Services
getImageList
- Gets the available image file list included in attached devices.
- If the
uri
is specified, the image file list for the specified URI is provided. Else, the image file list for all attached devices is provided.
getImageMetaData
- Gets the detailed metadata information of the specified URI for the given image file, such as file_size, thumbnail, file_path, and title.
...
getImageList: ({uri, ...rest}) => {
let params = {
uri: uri
};
return luna('com.webos.service.mediaindexer', 'getImageList', params)(rest);
},
getImageMetaData: ({uri, ...rest}) => {
let params = {
uri: uri
};
return luna('com.webos.service.mediaindexer', 'getImageMetadata', params)(rest);
}
...
Device Services
getDeviceList
- Gets the list of all the attached storage devices.
- The list can contain devices that are currently attached or the devices attached in the past.
...
getDeviceList: ({subscribe, ...rest}) => {
let params = {
subscribe: subscribe
};
return luna('com.webos.service.mediaindexer', 'getDeviceList', params)(rest);
}
...
Common Luna Service
- All the service request calls fall into this generic request.
- Responsible for handling of success and failure responses.
...
const luna = (
service,
method,
{subscribe = false, timeout = 0, ...params} = {},
map
) => (
({onSuccess, onFailure, onTimeout, onComplete, ...additionalParams} = {}) => {
const req = new LS2Request();
req.send({
service: 'luna://' + service,
method,
parameters: Object.assign({}, params, additionalParams),
onSuccess: handler(onSuccess, map),
onFailure: handler(onFailure),
onTimeout: handler(onTimeout),
onComplete: handler(onComplete, map),
subscribe,
timeout
});
return req;
}
);
...
Custom Components used in the App
Photo Player Component
The Photo Player component provides the following features:
- Handling slideshow for the listed images
- Thumbnail view on the bottom screen
- Zoom control option and Rotate option for the user selection
- Handling Transition events
...
const PhotoPlayer = ({handleNavigate, slideDirection, slides, startSlideIndex}) => {
return (
<SettingsProvider>
<PhotoPlayerBase
handleNavigate={handleNavigate}
slides={slides}
startSlideIndex={startSlideIndex}
slideDirection={slideDirection}
/>
</SettingsProvider>
);
};
...
const PhotoPlayerBase = ({handleNavigate, hideActionGuide, hideZoomUI, slides = [], slideDirection, startSlideIndex = 0}) => {
...
}
...
Video Player Component
The Video Player component provides the following features:
- Supports playing mp4 format.
- The
handlePrevious
andhandleNext
APIs handle playing the previous and the next file, respectively. - The
handleBack
API moves from the video player to the media list screen. - Displays filename, duration, current time, and file size over the player screen.
...
const VideoPlayer = (
{
// actionGuideLabel,
handleBack,
handleNext,
handlePrevious,
playlist,
...rest
}
) => {
...
return (
<VideoPlayerBase
{...rest}
onJumpForward={handleNext}
onJumpBackward={handlePrevious}
// onEnded={handleNext}
onBack={handleBack}
loop={state.repeat.loop}
poster={playlist.thumbnail}
thumbnailSrc={playlist.thumbnail}
title={playlist.title}
infoComponents={playlist.title}
>
<source src={playlist.file_path} type="video/mp4" />
</VideoPlayerBase>
);
};
...
Installing the App on the Target Device
Go to the app
directory of the downloaded source code and execute the following commands:
Package the enact source code.
$ enact pack
A directory named
dist
is created.Package the app to create an IPK.
$ ares-package dist
An IPK named
com.app.photovideo_1.0.0_all.ipk
is created.Install the IPK on the target device.
$ ares-install --device <TARGET_DEVICE> com.app.photovideo_1.0.0_all.ipk