- [Instructor] So far, we've made a small HTTP server
that can stream an MP4 video to the browser.
But our streams only work in Firefox and Chrome.
They don't work in Safari and we can't skip ahead.
We cannot request the different range.
For our video to work in Safari,
we have to implement range requests.
And this will also allow us to skip ahead
to different starting points using the HTML5 video player.
So let's go ahead and implement these range requests.
I'm inside of chapter three, chapter three, lesson two.
And in the Start folder, you'll find an index file
that has the server that we left off creating.
So you can start there or you can continue working with
the file from the last chapter.
Now, these range requests are actually handled
via the headers.
So if we wanna see what a range request looks like,
we need to get this range variable
from the headers.
So req.headers or request headers.
Range will let us know if we have a request range.
And we'll go ahead and log that.
And we'll take a look at what our range looks like.
And I can come over here to the terminal
and node dot to run our server.
And then we can come over here to the browser
and hit localhost:3000.
And then I'll go ahead and click
somewhere out here on the bar.
Notice we're not skipping ahead when I make that click.
But what I've done is I've sent a range request.
So let's take a look at what that might look like.
And here, inside of the terminal, you see that
there is a first range request for undefined.
That undefined request is the request that the browser makes
for the favorite icon that has nothing to do with the video,
so obviously, it doesn't have a range header.
But when I make a request for our video,
notice that we have a range header.
So we have this bytes equals zero through.
And this should give us the range that we wanna request,
bytes zero through the full video
or bytes halfway through the video to the end of the video,
so on and so forth.
So in order to handle range requests,
we need to parse this header.
So I'm gonna go ahead and stop the server.
Clear it out.
And let's get started implementing these range requests.
So what we need to do is we need to make sure
that we have a range request to deal with.
I'm gonna get rid of this console.log here
and just check for the range variable.
So if we have a range, we will handle the range request.
Otherwise we're just gonna go ahead and send the video back.
So I will take everything from our writeHead response
to where we're streaming the fileName back to the client
and just place that within the else statement.
So if we don't have a range request,
we'll just go ahead and send the full video back.
But if we do have a range request, we wanna handle it.
And the first thing we need to do is parse
the start and end ranges.
So I'm gonna go ahead and create a couple of variables here.
So we use some array destructuring.
And I will get the first value of the array
that's gonna be returned and I will set that to start.
And the second value of the array that will be returned
will be our end value.
So I'm gonna go ahead and take our range, which is a string,
and I'm gonna replace that byte string.
So I wanna make sure our range doesn't contain that,
so we'll do a regular expression that looks for bytes
or bytes equal.
And we will replace it with nothing, with an empty string.
So the range header is a string and it says bytes equal
from the start position to the end position,
and both of these positions are separated with a hyphen.
So what I'm gonna do is I'm gonna split this into an array
and I'm gonna split it on the hyphen.
This array will have two values, a start and end value.
The start value should be the first value in the array
and the end value should be the second value.
So we pulled both of those values out of here
just by destructuring them.
And then I wanna make sure that these values are integers
because it starts out as a string.
So we'll go ahead and do start equals parseInt
just to make sure this variable is the integer
and we'll parse it as a base 10 int.
And then we're gonna take our end value.
Now, the end value isn't necessarily always gonna be present
because as we make range requests,
we're actually making a request from a starting position
through the rest of the video,
so it might not always be there.
So what I wanna do is search for the end value.
Did we have an end value?
And if we did, I'll go ahead and make sure that's an integer
and we can parse that, so we'll go ahead and parse
the end value as a base 10.
Otherwise if we don't have an end value,
we're gonna set up our own end value
and the end value is gonna be the size of the video.
So we'll take our size variable and subtract one from it.
So the end value will be the end of the entire video.
So now that we have start and end values,
what we can do is write our response header
to respond to the range request.
So we'll do res.writeHead.
And when we do writeHead this time, we will do 206.
And what 206 means is partial content.
So we're gonna be streaming a part of the video,
not a full video, so that will let the browser know that
we are handling this range request.
So I'm gonna add a Content-Range key
and in this Content-Range, we're gonna specify
what the byte range is.
So we'll say bytes
and we'll take our start value
and we will subtract it from our end value
and then we can go ahead and divide that by our size value.
So we're telling the browser in this header
what the bytes are that we're gonna start with,
what we're gonna end with, and the size,
the current full size of the video.
I'm also gonna add an Accept-Range as header.
And we wanna know that we're working with bytes,
so we're gonna accept ranges as bytes.
And then we will also add our Content-Length.
Now, our Content-Length isn't just the full size
of the video.
What it is is actually the end value minus the start value
and plus one.
So now, we have our Content-Length.
If there is a range request,
we will actually get our Content-Length by calculating
the end value minus the start value.
And if the range request doesn't have an end value,
that end value will be the full size of the video.
This actually tells the browser what is the size
of each chunk that we're gonna be sending in this range.
So now, I'm gonna go ahead and just specify our Content-Type
and this is video/mp4.
All right, so we've written the header
for the range request.
Now, it's time to actually stream the video file.
So I'm gonna go ahead and create a ReadStream
to the fileName.
But we're not gonna create a stream for the full video.
When I create this ReadStream,
I'm gonna send some additional options.
So the second argument that I can add here
are additional options
and they include a start and end values.
So I can create a ReadStream that only streams
part of the file, starting at the start point
and ending at the requested end point.
So this will actually handle our range request.
All I need to do is now pipe this to our response
and we should be good to go.
So I'll go ahead and save this code here
and we'll go into the terminal window
and type node dot to run it.
We have a server running on localhost:3000.
Let's take a look at it.
So here's our video.
And can I skip ahead?
Yes, I can skip to different points of the video.
So we are handling range requests.
And we can also go ahead and give this a test out on Safari.
Does this work in Safari now?
Going to localhost:3000.
Yes, it does.
So Safari requires that we handle these range requests
and because we are, our video now works.
So now, our video handles range requests
and we also introduced some stream options
that we can add to file streams,
specifically setting up a smaller piece of the stream
using start and end values.
So now, we've streamed video content to the browser.
In the next two lessons,
we're gonna go ahead and stream content from the browser.