 So in the last entry, I think I talked about the service worker and some of the offline stuff. And I actually heard from a lot of you in the feedback that that was like, it was complicated stuff. And I agree, it is complicated stuff and complex. And it's not that much fun to write. It's quite fiddly work. But there is hope because things like the Cache API, hopefully they can be updated, these APIs that I'm having to sort of work around and kind of work with to make my stuff work. Hopefully, in the long term, we'll get solutions for those at the platform level. And in the interim, I think it's a good thing that you can actually work with these lower level primitives and make the thing that you want to make. That's the genius of the extensible web. Give you the low level primitives. Yeah, there's complexity there, perhaps. Perhaps not. But if there is, you can still write that code. And it's much better than saying, oh, we've got this high level thing. And if it doesn't fit for your particular needs, sorry about that. That's the old web. That's the web we used to have. So anyway, I just kind of wanted to say that just to say, partly, I am reading the comments. I do love reading those comments. And I'm going to say this at the end of the video, but today I'm starting with that. I do read them. I love them. They're really insightful. And often, as you'll also be seeing, sometimes we feel like, why did you do that? Why didn't you do that? And the next thing will be a comment from me to the GitHub commit, where I'm going to change the code, because that was a better idea. So teamwork, that's what this is. Anyway, so I don't want to say that I've solved all the major problems with the app because I haven't. But I did want to spend a little bit of time today looking at some of the smaller details, the little touches that I've been putting into the app. And I just thought I'd pick out two of those for you today, and the first one is white seal and screen. Let me show you. OK, so here we have the video. And I'm just going to pause it. Why is that green? Oh, that's peculiar. We'll ignore that for now. But what I wanted to show you is this down here at the bottom. You see as I'm dragging along, we get this little thumbnail like that. Let's release that. Wow, I do not know what's happened to Pete there. Sorry, Pete. You look like the incredible Pete. That's bizarre. Goodness knows what's going on there. Anyway, but this little thumbnail, how am I doing this? I thought this was really a really nice way to visually sort of orientate yourself. We've all seen this on apps and so on. Oh, we can't leave it on that thumbnail. There we go. That's me. Right. Well, the way I do it is I have created an image, one big image for the video. So this is part of the image processing that I do. When I take the video in and I'm making the webms and everything else, I also ask FFMpeg, and you can find the command for it just by searching around, to say every, I think it's like every three seconds, I think I time it to the segment length, actually, because I break my video into three-second segments. And if that's sort of news to you, then definitely watch the ones earlier on in the Dev Diary where I was talking about dash and how you split your video down into these segments, all the same. I ask FFMpeg every few seconds, please can you output an image for me, a small thumbnail? And it is small. It's only like 144 pixels by, was it 256? Whatever it is, it's tiny. Anyway, so I get a bunch of these and then I use image magic, actually, to stack them all up into one big image. And again, that's just this one line in the command line. And you see that it outputs this video like this, as like a strip, film strip. In the source code, I load this image as, now do I use it, I think I use it as a div, a background image on a div, okay? So I have a div, imagine this, let me show you this here. The white box around it with a little down tick on it, that's a div, and inside is another div that's slightly smaller, which is the same size as one of those frames of the video. And in there, I position another div, a third div, which sounds bizarre. You might be like, well, why is he doing this? Why has he got so many divs all the way down? Surely he could just set background position, he could just drop this in as a background image and then set background position and all would be fine. Yes, I could, but actually, we don't typically want to do that. What we would rather do is we'd rather use a transformer. I always say this about performance, transforms, and opacity, I best friends, I'm almost tired of hearing me say it, but there I go again, doing it once more. What I do is I drop in that image as a full-size div, and I then just transform it up and down. So it's like a sliding window. So you've got this kind of the container element and inside is this div that we just sort of slide up and down with a transform. And how do we know how much to transform it by? Aha, now that's what this code is here. So the available height is basically how tall is that film strip of images? Take away the height of one of the stills, which is, it is 81 pixels. There you go, now that's the answer. I think it's, yeah, it's probably 144 by 81, I think is what it is for each one of those stills. Anyway, so you take away, so you've got the height of the whole thing minus one of one of the frames because you don't want it to be able to go past the end. And then we figure out the index, and the index is basically taken normalized position. So if we, I normalize the time. So if you're, it's a value between zero and one, zero being the start of the video, one being the end. And I say, well, you are say 0.5 way of the way through the video or where you're scrubbing is 0.5 on that line. And what I do is I then say, well, times that by the available height, that should give you where you would need to be in that film strip. And then I round it to the nearest frame so that we don't sort of see half of a frame or three quarters of a frame or sort of fraction of a frame. We want it to be snapped to whole frames. And that's what we do. We just basically find the, that's why there's the floor there. We find this, what I call the index, which is which one of the frames should we be looking at based off that normalized position between zero and one. And then we do a transform on that, the film strip to move it by the correct amount. And that's it. So we've pre-baked this film strip and then we drop it into the page and then we just slide it up and down depending on how far you've slid across the timeline. So that's one. That's one of the things. Let me show you the other one, which I'm quite fond of as well. It is just a little thing. Imagine, I'm just gonna, I'm gonna get rid of the incredible Pete there and just go back to normal image. I'm gonna find out why that happened after this. It's classic, isn't it? It's been working fine, but I effectively do a demo and it all breaks. Brilliant. Anyway, here we go. So what is the other thing? Yes, it's this. Remember now, imagine you're in this situation and you're thinking, oh, look, all this content, isn't that amazing? But imagine you go offline. And that didn't work. Why did that not work? There it, it did work. It just took a while for it to propagate through. You see now, it's faded out and disabled all the things that it thinks we can't click. So let's bring that back online. There we go. You see, it picks that up as well. Imagine then we go and let's go into one of these. Let's pick, let's pick this one. And we will make that available offline. Round it goes, there we are. And we could do the same again, but I've also added this little settings area where we can prefetch popular content, which was the whole, we talked about that in a previous episode, how one of the use cases is, let's prefetch the first like 30 seconds or the first minute of some footage so that we get a flying start. But the other setting, I make that easier setting by the way, because it will use bandwidth and I want to be respectful of people's bandwidth. So I don't just switch it on for everybody. I let them decide that they want to do it. The other one is show offline content only, which is the, I know I'm going to be on a flight or on, you know, some kind of underground train, which seems to be the nature of my life sometimes. If I click that on, you can see it goes into this downloads only and I do, again, this is a little little thing. I'm showing that little marker in the top right hand corner just to remind you, you're in downloads only mode so that you don't kind of go, why is nothing working? And you'll see when I scroll down, this one is still lit up. And there you go, I can click into it and I could in theory watch the video. There we are. I'm going to scroll along as well. Ta-da! So let me just talk about some of that stuff. I won't get into probably all of it, but it is interesting stuff. So part of it is the offline-ness of it and then part of it is kind of detecting the offline-y state, which is relatively easy to do if you've got navigator.offline or online or whatever it is, navigator.online, that's what it is, with a capital L. Never really fully understood that one. Navigator.online, it's like you're shouting the line. I think Jake made that point actually in one of his talks. Oh well, we're obviously on the same page. Anyway, let's have a look at a little bit of code. So the vast majority of this is actually hidden in app and we find where it is. La, la, la. It is. This is the thing where I want to find, there we are. Right, disable unavailable links. What we do is we look for all the things that can be effectively disabled by going offline. So that's gonna be like the thumbnails in particular for the videos. And we look and we just say, given all those links, I basically pull out the href from those links and sometimes for specific things, there's an href on the dataset. But what I do is I ask the offline cache, do you have this item, the thing for this path? And if you do, then that's great. If you don't, then we're gonna add on this disabled class which will fade it out and it desaturates the image. And it does all the stuff that I just showed you where it kind of goes, yeah, you're not gonna click on this, are you? So the offline cache.has, let me show you that. Has, has, there it is. So this is an interesting one from my point of view because it's, the simple version of this would be to look inside, and the first version of it, this is exactly what it did. If you imagine that each video has a URL like, you know, show name slash episode name, then I can just look inside the caches and just say, do you have that, yes or no? And if the answer is yes, then I have it and I can just show that link, great. And in the, inside of one of those caches would be, let me show you what it looks like. Neenie, diddy, diddy, diddy. Inside here, inside the cache storage. There we are, CDS show real. It's the video, of course, yes, but it's also, I'll just zoom out a little bit. There, it's the poster frames. It's the URL itself, the video, the page itself. So we've got the HTML, the artwork. We know that the main cache will have all the CSS and icons and everything else. So between those two caches, we should be able to show this particular page. So if we've got a cache with the name CDS slash show real, we're gonna enable that link. But what happens if you were part way through downloading that cache was partly filled and maybe you got the artwork, but you didn't get the video or you didn't get the, or all the video. You got just some of the stuff, but not all of the stuff. I have this purge partial downloads for purge, can't even say it. Take a breather, here we go. Purge partial downloads, there you go, nailed it. Purge partial downloads, I keep saying it, function. Which we use to kind of go in and go, does this look full? Does this look like it's got everything that we'd expect it to have? And I am using the video as a proxy for that. So again, this is something that I might come back to and might refine a little bit further because this is the nature of this. It's a process, I'm sharing with you the process. I'm not saying I have all the right answers and this is just how it's going to be. Very often, as I said at the start, you know, I read the comments or whatever and people are like, you should really have any thought about that and that gives me all the thoughts. Anyway, this is how it currently is. What I do is I go through and I get all the items in that particular cache and I locate the video chunks in particular on the assumption that the video is the likeliest candidate for being the last thing into the cache. So, you know, the artwork and all these things, they're probably gonna arrive much more quickly than say a 40 megabyte or 100 megabyte or a 200 megabyte video. So, I look at the video in particular and I go through and I look for all the chunks. So, I look through every item in the cache but I look in particular for the video chunks and I look for the highest numbered video chunks. So, maybe I say get video chunk 30. And then the thing I do is I look at a video chunk and I say, based off your content length, I would have expected to see 50 chunks. So, I know that I'm expecting 50 but I've only got 30. Therefore, I'm pretty confident I have a partial download. And so, at that point, if I find, I mean, I can compare the largest chunk index to what we would expect to have. If it's not the, if the expected and the max index don't match, then I basically delete that cache. And then, and only then do I actually check whether the cache exists. So, before I respond with, yeah, we've got a cache for CDS slash show reel, I'm gonna go in and say, just get rid of anything that was pending or partial first and foremost. Now, where this might get changed is in the future with something like background fetch. When the background fetch stuff lands, it's possible that the cache would be partially filled because of background fetch. And I don't want to purge it if it's partially filled by background fetch. So, I might have to rethink this. Don't know yet. We're gonna find out, aren't we? That's the exciting part of this. So, there you go. Just a couple of things like feeding out the unavailable links when somebody goes offline, which they can either control themselves or they can, you know, they can mark it as showing the offline content or they can actually, you know, if they're in airplane mode or something like that, it works in that case too because you can get events on the window object for those kinds of things. And having the thumbnail image, just little touches that I hope make it a little bit more fun for people to use the app. Definitely have a look at the source code. It's on GitHub. As always, drop your comments in and you can subscribe as well. And I'll catch you. Yep, always the same. Ready, steady, next time. Hello, thanks for watching. If you enjoyed this video, well, you may enjoy other videos that we make too. So, don't forget you can subscribe and you'll get notified when we push out a new video. And there's more videos over there or down there depending on how you're watching the YouTube's right now. Definitely click on those.