Videos are one of the most popular forms of content online. They help people start new professions and they keep us endlessly entertained. That's why you have to make sure any video-based apps you work on give users a good experience.
Users like it when they can customize videos to display what they need. In this tutorial, you'll learn how to let your users make custom video slideshows with Redwood and Cloudinary.
Setting up media
We're going to be working with Cloudinary to handle our media because they make it easy to work with. To start, make sure you have a Cloudinary account. Then go to the Media Library and upload any images and videos you want users to be able to select from.
You'll also need to upload the template for a slideshow to your Media Library. Right now, you have to download this slideshow template because it's the only one supported by the slideshow generation functionality.
Before we jump into the code, it'll help to take a look at the URL we'll be working with to generate these custom slideshows. You can also find an explanation of this URL in the docs.
https://res.cloudinary.com/`cloudname`/video/upload/fn_render:`global-settings`;vars_(`slide-settings`(`individual-slide`))/`global-transformations`/`template`.`ext`
You can find the cloudname
in your Cloudinary console.
-
global-settings
: general settings applied to your video, like the height and width that Cloudinary should return as well as the duration that the video should be. -
slide-settings
: settings that apply to each slide. This will include things like how long the image is shown before switching to the next one, what kind of transition is applied between slides, and how long the transition lasts. -
individual-slide
: how you add images or videos based on theirpublic_id
to your slideshow. We'll use this to add the specific images we want a little later. -
global-transformations
: how you can use the regular transformations Cloudinary has available, like automatically adjusting the quality of the video so that it's optimized for your users. -
template
: how we let Cloudinary know we're going to create a video slideshow. This is the template we downloaded earlier.
This is what all of our work is leading up to: letting the user control their own slideshow.
Creating the customizations
We're going to let users define which videos are in the slideshow, the duration of the slideshow, the width of the video, the duration of each slide, the height of the video, and the duration of each transition. Let's set up the Redwood app with the following command.
yarn create redwood-app ./user-generated-slideshows
This will make a fresh Redwood project for you. We'll be working in the api
directory to handle the database and back-end and the web
directory to handle the front-end.
We'll start by making a new schema for our Postgres database.
If you want to follow along with the database set up, you'll need to have Postgres installed locally.
In the api > db
directory, open the schema.prisma
file. The first thing we'll do in here is update the database provider. Right now it's set to sqlite
. We need to update this value to postgresql
.
You'll also see an example of a schema model here and we'll be replacing that with the following.
model Property {
id Int @id @default(autoincrement())
width Int
height Int
videoDuration Int
slideDuration Int
transitionDuration Int
videos String[]
}
This defines all of the properties of the slideshow a user can set. There are a few more things we need to do to make sure our database gets set up correctly. In the root of the project, open the .env
file. There's a line assigning a value to DATABASE_URL
and it's commented out.
Uncomment that line and update the URL to the connection string for your local Postgres database instance. It might look something like this.
DATABASE_URL=postgres://postgres:admin@localhost:5432/slideshows
Now you have everything in place to run a database migration! To do that, run the following command.
yarn rw prisma migrate dev
This will get Prisma to generate your migration and update the database according to your schema definition. We can move on to the back-end and front-end now.
Making the back-end
Now it's time to create a few things to get the back-end working. First, we'll generate a GraphQL schema and a resolver with the following command.
yarn rw g sdl property
Going to the api > src > graphql
folder will show you a new sdl file. This holds all of the GraphQL types you need to get started. Since we're already in this file, let's add a new mutation type. This mutation will create a new set of properties when the user submits the form we'll create on the front-end later.
Below the UpdatePropertyInput
type, add the following mutation type.
type Mutation {
createProperty(input: CreatePropertyInput): Property
}
This adds the create mutation to our GraphQL definitions. Now we need to add the actual functionality that will update the database.
Go to api > src > services
and you'll see a properties
folder. The files inside this folder were generated when we ran that yarn rw g sdl property
command earlier. It has a resolver for fetching all of the properties from the database. Now we need to add a resolver that will handle the creation of a property.
At the bottom of the properties.js
file, add the following code for the create resolver.
export const createProperty = ({ input }) => {
return db.property.create({ data: input })
}
That's it for the back-end! So far we have the database created and connected the GraphQL resolvers to it. All that's left is for us to make a front-end for users to interact with.
Building the front-end
Let's make a home page that displays the video slideshow and the form with the options they choose. We can let Redwood generate a new page and add the new routing with this command.
yarn rw g page home /
If you go to web > src > pages
, you'll see the HomePage
folder. The files inside were created with the command we just ran. If you take a look at Routes.js
, you'll see the new route for the home page is already there.
Then we'll update the HomePage
component to display the form users will use to make their custom slideshow.
Adding the form
We'll need a form to get the info we need to make these custom slideshows. Redwood has its own form helpers to make them easier to work with. In the HomePage
component, we'll import a few things at the top of the file.
You can delete the existing imports because we won't be using them.
import { Form, Label, TextField, Submit } from '@redwoodjs/forms'
import { useMutation } from '@redwoodjs/web'
We'll use these imports to make our form and send the new property to the database through the GraphQL server. Let's add the form first.
You can delete everything inside the HomePage
component. We'll be adding completely new elements.
const HomePage = () => {
return (
<Form onSubmit={onSubmit}>
<Label name="video1">First video</Label>
<TextField name="video1" />
<Label name="video2">Second video</Label>
<TextField name="video2" />
<Label name="video3">Third video</Label>
<TextField name="video3" />
<Label name="width">Video width (px)</Label>
<NumberField name="width" max={500} />
<Label name="height">Video height (px)</Label>
<NumberField name="height" max={500} />
<Label name="videoDuration">Video duration (ms)</Label>
<NumberField name="videoDuration" max={11500} />
<Label name="slideDuration">Slide duration (ms)</Label>
<NumberField name="slideDuration" max={5500} />
<Label name="transitionDuration">Transition duration (ms)</Label>
<NumberField name="transitionDuration" max={5000} />
<Submit>Save</Submit>
</Form>
)
}
We've made all of the fields to match the data we need to store in the database. That means it's time to bring in the GraphQL so that we send these values correctly. This is how we'll create a new property. Inside of the HomePage
component, add this code above the return
statement.
const [createProperty] = useMutation(CREATE_PROPERTY)
const onSubmit = (data) => {
const videos = [data.video1, data.video2, data.video3]
createProperty({
variables: {
width: data.width,
height: data.height,
videoDuration: data.videoDuration,
slideDuration: data.slideDuration,
transitionDuration: data.transitionDuration,
videos: videos,
},
})
}
Here, we make a mutation out of CREATE_PROPERTY
which we'll be making right after this and we make the onSubmit
for the form to handle the GraphQL call. At the bottom of the file, above the export
statement add this code.
const CREATE_PROPERTY = gql`
mutation CreateProperty(
$width: Int!
$height: Int!
$videoDuration: Int!
$slideDuration: Int!
$transitionDuration: Int!
$videos: [String]!
) {
createProperty(
input: {
width: $width
height: $height
videoDuration: $videoDuration
slideDuration: $slideDuration
transitionDuration: $transitionDuration
videos: $videos
}
) {
id
width
height
videoDuration
slideDuration
transitionDuration
videos
}
}
`
This GraphQL mutation is what we use in the useMutation
method to create a property. It passes all of the data to the resolver.
If you run the app with yarn rw dev
, you should see something like this in your browser.
Go ahead and create a new property and save it. This will help us later in the tutorial.
Adding the video
We can finally add the video to the page! Earlier we looked at the slideshow generation URL, now we're going to use it with a few placeholder values. First, we'll create a new variable under the mutation definition.
const properties = {
width: 500,
height: 500,
videoDuration: 15,
slideDuration: 3000,
transitionDuration: 1000,
videos: ['beach-boat', '3dogs', 'reindeer'],
}
This gives us some default values to start with so that our video will load. Now we'll actually add the video. Below the form, add the following code.
You'll need to wrap the form and the video in a fragment so that React stays happy.
<video controls>
<source
src={`https://res.cloudinary.com/milecia/video/upload/fn_render:w_${
properties.width
};h_${properties.height};du_${properties.videoDuration};vars_(sdur_${
properties.slideDuration
};tdur_${
properties.transitionDuration
};transition_s:circlecrop;slides_(${properties.videos
.map((mediaDoc) => `(media_i:${mediaDoc})`)
.join(';')}))/f_auto,q_auto/slideshow_hlhpzw.mp4`}
type="video/mp4"
/>
Your browser does not support the video tag.
</video>
This URL looks pretty crazy since we've added a bunch of extra parentheses and curly braces, but it's just loading the values into the placeholders we described earlier.
If you run your app again, your home page should look similar to this.
So you have the form in place and the video displaying on the page. The only thing left is to load in a property so that the user sees their custom slideshow.
Loading the saved properties
We'll need to add one more GraphQL query to the front-end and we'll be able to set our initial video state. First, we'll add a new method to an existing import.
import { useMutation, useQuery } from '@redwoodjs/web'
Then we'll create a query inside of the HomePage
component, just above the mutation.
const { loading, data } = useQuery(PROPERTIES)
Next we'll add PROPERTIES
just above our CREATE_PROPERTY
mutation at the bottom of the file.
const PROPERTIES = gql`
query Properties {
properties {
id
width
height
videoDuration
slideDuration
transitionDuration
videos
}
}
`
The generates the GraphQL query that we need to fetch all of the properties from the database. Now let's update our default variable to read the latest property that was added.
const properties = data.properties[data.properties.length - 1]
To make sure we have data to read, we're going to use the loading
variable we received from useQuery
. Right above the last line of code we wrote, add this.
if (loading) {
return <div>Loading..</div>
}
This is going to prevent the app from crashing while the data is loading. Now if you refresh your app, you should see a slideshow generated with the values you saved earlier! You can try it out with a new value if you want and then reload the page.
Keep in mind that video slideshow generation is still a beta feature so it might be a little buggy sometimes.
Finished code
You can check out the finished code in this Code Sandbox or in this GitHub repo in the 'user-gen-vids` folder.
Conclusion
Adding customizations can make it easier for people to learn and interact with your site. Video content is not slowing down so giving your users more control can be something that gives your site an edge over the others. Plus, working with Redwood will hopefully make it easier for you to keep things up to date!
Top comments (0)