Auto-generated transcript Hey, in this video, we're going to be building our own self-hosted, completely free alternative to Loom. The reason why I'm even building this is because I'm planning to improve my cold outreach campaigns, right? Just some business stuff. And one of the things I'm doing now is recording custom Loom videos for my clients, right? The only problem is that Loom is a paid product. And look, I have no problems paying for this service. The only thing is that I can actually build this myself in like two days, just wipe coding a solution that works for me and not have to pay a single cent for it. And so that's what I'm going to try to do in this video. This might become a really long video, or I might actually divide it into a couple of parts. But the idea is I'm going to be building this project live in front of you and just show you my process of building and how I am building new software in the age of AI agents and everything. I've made project series before. As you know, if you follow my channel for a very long time, you know, I've built things like the Redis database clone in Go and a bunch of other projects. And those have like 20 30 different parts of like a whole video series I stopped doing those kinds of videos because like AI writes all my code now anyways And I mean if you can prompt it if you can prompt the agent you can pretty much build whatever you want nowadays So it like is this really valuable is what I was thinking But turns out if you a junior developer, or you just haven't been developing for a very long time, if you're just a pure vibe coder, you probably still suck at writing good prompts and understanding the architecture, right? No offense. So in this little video, not little video, this is probably going to be an hour long. But in this video, we're going to build an actual project and you're going to see how I work and how I use these agents to write code faster, but also make sure that that code is maintainable, reliable, and all those other things, right? So those are little Excalibur presentation over here. We're just going to go through this real quick. Some potential solutions for some potential alternatives and free solutions to loom and why i'm not going with them instead of hosting a client's like outreach video on loom which is a paid service and i'm avoiding that you could use google drive to host the video file and just send a link to that video to the client you could do that the only problem is that this is just going to have the google drive own video player it going to have google own branding and it not really very scalable right And when you recording like 20 different videos every single day sending out 20 different proposals to 20 different clients, uploading all of these to Google Drive and managing all of that, that becomes kind of a headache, right? So this is obviously not going to be my solution. The second one, and then even more horrible solution, is to use YouTube unlisted videos now this is just a very very bad idea and this was actually my first instinct but turns out this is a horrible idea because yeah you could upload a video on youtube set it to unlisted and then share a link to that video with your client the only problem is this is extremely unprofessional you're gonna have the youtube sidebar with suggested videos popping up and if your client doesn't have an ad blocker installed which let's be honest a lot of non-technical people, you know, the kind of people that I'm targeting with my content and my products and services, they probably don't have an ad blocker installed, which means they're going to have to go through like 10 different ads, like 15 second unskippable ads in my outreach video where I'm trying to explain to them why they should hire me. And then there's some fucking ad playing, right So this is a horrible approach And obviously you have the YouTube video player with its own custom controls and branding and everything And that the least of the problems in this whole list So obviously we can go with YouTube unlisted either So we going to have to build our own self-hosted alternative to this. Now, the way we're going to build this is we're going to have a Next.js web app to actually host the video player. This is going to be a very minimal app. It's just going to have like one or two pages, right? each video that you put on this app is going to have its own little page and the client can just view that page and watch the entire video you can just share that link with the client super simple we're going to have an we're going to have a cloudflare r2 bucket this is basically like amazon's s3 but it's free at least for the first 10 gb of storage which is more than enough for our needs and finally we're going to have a bunch of shell scripts to manage the r2 bucket automatically. These are just going to be a bunch of simple bash scripts that I'm going to have on my local machine over here. And I can run these anytime to upload a new video to delete some older videos to make more storage. So we stay under a 10 GB limit, and so on and so forth. And here is a step by step building plan for building this entire project. So without further ado, let's head into my code editor. And let's start building this whole thing. Okay, so instead of going to my code editor, let's first go to let's first go to cloudflare and actually configure all of this stuff so i've already done this but what you basically need to do is go over to cloudflare sign up for their cloud service head over to r2 object storage over here in the storage and databases section create a new bucket over here i've already created one called loom this is where i'm going to store all of my videos uh this resume is just a test example file i put over here just to make sure things were working ignore that and you're just gonna set up a bucket over here and then you can proceed now before cloudflare allows you to create and create an r2 bucket it's also going to ask you to verify your payment account your payment method and basically ask you to add a credit card you can use a card for this it's completely safe nobody's going to charge you any money as long as as long as you stay under the free tier limit which is 10 gb of storage but just to be safe you can go over here to billing usage and you can set up a budget alert i've already set one up for literally just 10 cents as in if if i so much as if this thing if this thing starts to cost me any money at all i'm going to get a budget alert and i can go and turn it off if i want to so you can set that up as well just to make sure that you spend zero money on this and this is completely free set up the budget alert add your payment method create your r2 bucket and then go over here in manage api tokens and create an account api token for you to use this is going to give you a bunch of credentials you'll need i've already created one as you can see i just realized i'm gonna have to blur so much information in my cloudflare account just to make sure nobody hacks me but But once you create an account API token, it's going to give you some things like the access key ID and the secret access key. And these are basically a bunch of credentials that you're going to use to access the Cloudflare R2 bucket programmatically from your web app, basically. Okay, so go over here, create an account API token. Once you've done all of this, then let's proceed to the next step. The next thing we're going to do is configure our clone. So our clone over here is basically a CLI tool that allows us to use pretty much any cloud storage without having to necessarily write all the code for it. So what you do or how you use this is, let me zoom in as well so you guys can see this more clearly. You just say our clone config. And then as you can see, I've already got a bunch of remotes over here. This is the one we going to use on Cloudflare what you do is you just press n this is going to help you create a new remote and it going to ask you a big bunch of questions the name of the remote the access key id and the secret access key and all the other stuff you're going to need you can get all of those things all those credentials from cloudflare fill all of them in fill all of them in and then you're going to have your r clone remote which you can use to upload files and download files from this s3 bucket and just to show you an example i can say something like r2 dash loom that's the name of the remote over here as you can see and then i can say slash loom that's the name of the bucket in cloudflare over here as you can see and it has a resume.pdf file right i can i can just send this command and as you can see it does see that resume.pdf file so we know this is working properly so we can start to use this okay and And once you've set all of those things up, I'm gonna just mark this completed. We can move on to the next step, which is to create a shell script to upload a lossless compressed video to R2. So every word I just wrote there is important. We want to obviously upload a video to R2. That kind of the point of this project but we want to make sure that it is compressed but we also want to make sure that the compression method we use is lossless so we don lose any video quality The reason we want to compress this is because like look this is going to be a bunch of screen recordings right And screen recordings are notoriously bloated in most software, especially in most software, including OBS, which is the one I'm using to record this video over here. We want to compress it so that we can save more on storage and store more videos, because remember, Cloudflare, are two buckets for free are just going to give you 10 gb so you want to make sure you're getting the most out of it so we're gonna have to compress it as well now this is again really simple in fact in fact i have the command for this right here i just asked gemini to give me a command to compress an oba screen recording without losing any quality the reason i asked gemini is because i'm no expert at ffmpeg and video compression and all these other things so i'm just going to ask these bots to give me the right command and I'll just paste that in, right? So now we need a script, a shell script basically that can compress our video file and just upload it all the way over to our R2 bucket. So what I'm going to do is I'm going to copy this whole command and head over to my video editor again and open up my agent and let me open up my file explorer over here as well. So I have a script, I'm going to copy this whole command and head over to my video editor again and open up my folder. I'm just going to create a script over here called upload to R2.sh. I'm going to pass that in over here in my context. And then let me just write a prompt for this. I want you to write a bash script that's going to use Rclone to upload a file to my Cloudflare R2 bucket. Before you upload the file, make sure that you compress it using the ffmpeg command that I'm going to give you below. This is going to be a video file. You're just going to take the path to the video file from the user and you're going to compress it using the command i give you below and you're going to upload it to cloudflare r2 okay there we go now the ffmpeg the ffmpeg command i'm going to give it is right here and let me just mention use the rclone remote r2 dash loom and the bucket name loom and now let's just let it do its thing by the way i hope i can just zoom out a little bit because I hate working in such a zoomed in environment but it is necessary for the video so you guys can see this too but here we go here's the script so we have this script over here I'm not going to read the entire code by the way more on that later but let's just read the agent output over here okay it validates all of our requirements checks that both ffmpeg and rclone exist compresses the video using the command we gave it and then just uploads it to R2 And I didn ask for this but this is a very useful feature to have Thank you Gemini It's going to automatically delete the temporary compressed file on exit, even on failure or cancellation. Now let's actually test this out, right? I'm going to go scripts, upload to R2. And as you can see, it asks me for a video file. So let me see if I have one over here, videos, if i go over to my youtube projects freelancer intro maybe i shot i recorded a new intro for my freelance profile so let's try using that uh where uh final.mp4 i think that should be fine and i'll give it a custom name as well why not freelancer intro.mp4 there we go so now this is running an ffmpeg command to compress the entire video file before it uploads it to r2 basically right? This video file is 39.92 MB as you can see over here. So let's see how much this script can actually compress it. Now while it does this, I'm going to just minimize this a bit and start building the actual user interface the actual web app over here right So I already know what color I want to use It right here This is like a light blue kind of a color We going to use this as the primary color for this app just because I like it Obviously, you can use whatever you want. So let me think about what prompt I'm going to give to this agent right now. Actually, instead of having a homepage, we should probably like what we really need over here is a video player, right? Actually, instead of building the homepage first, what we should build is probably a page to view the videos right so i'm just going to say something like we and then i'm gonna create a id folder over here and then a page inside it this is just how navigation in next.js works right uh let's maximize that i'm gonna pass that in over here and i'm just gonna say use the hsl color that i just gave you to create a bunch of shades i want you to create at least 12 shades to create these shades just increase or decrease the lightness of the color after every three or four shades increase or decrease the saturation by just one or two points to make the color seem less dull as you're increasing or decreasing the lightness once you have all 12 shades use them as the primary brand color for this entire website what i want you to do is create this page over here that going to take a video id and it going to display that video in a custom video player Let just give it that prompt By the way the reason I giving it such specific instructions to create these 12 shades and how to create them is because I learned this from a design book called Refactoring UI. It's built, it's written by the same people who built Tailwind. It's a very, very good book. You should probably check it out. But we also need to, but anyway, back to the project. We also need to think about what kind of video player we want to use. So let's say, suggest me some options for what video player to use. Again, I'm no expert in these things. I do know how to write good apps, but I'm no expert in video player or which one's the best or which FFmpeg command I'm supposed to use or all these other things, right? So I'm going to ask the agent to give me a bunch of suggestions that I can then review. And the agent is probably smart enough to figure this one out, but I should say it anyway. Add all 12 shades to my Tailwind theme in globals.css. Let's make sure my agent sees that as well. And while I was doing all that, it turns out my upload command finished as well, successfully uploaded to R2Loon. So let's go see if that worked right here. Let's open up. Okay, it's already open. Let me refresh this and there we go. So the original file was 40 MB all, most this is 25 mb so the compression works and the upload also works so yeah step one is complete now let's review these options over here it looks like the base hsl color value wasn't included in your message that's my fault put that over here as well let's just say use this color there we go and then ask for the video player options with stack player modern built specifically for react and next.js fully headless production grade video playback okay custom handcrafted player probably not a good idea it's probably best to use a video player that someone else has already built and is being used in many websites instead of creating your own over here media chrome composable web components around the standard video html element i don't know what this is player react clean minimalist default aesthetic okay so let me actually go and google vidstack real quick and just see what it looks like okay so i'm definitely loving the design that these guys have over here this looks like a very well made website okay so they have a player demo over here let's just see what that looks like okay okay so i actually like this a lot it has all the controls i need the icons look very nice it has chapters as well that going to be very useful for us because i plan to include those things as well Timestamps and chapters right Okay Yeah I think we just going to use this Use Widstack as our video player. There we go. Okay, and one thing I just realized is we missed a step over here. Let's also... Okay, and while it's doing that, let me go back to my little whiteboard over here and add another step. We're going to also add timestamped chapters to each video. And yeah, that's going to be the final thing that we add. And then we're going to add the testing and final changes. Let's go through the implementation plan it wrote for us, brand palette, video player, video URL source. Okay, let's see. Okay, so I guess before we approve this plan, we want to figure out how to actually, you know, display our videos, or at least fetch our videos from Cloudflare R2. Because I did some research on this, and you can't actually use the public endpoint of the bucket because that that is heavily rate limited throttled and it is not meant for production use what you need to do is create is configure your own sub domain to use Cloudflare CDN so let's figure out how to do that so Cloudflare has their own AI agent over here it is extremely useful I been talking to it a lot and it is very good at helping you achieve things on the cloudflare cloud platform right so i want you to help me set up cloudflare cdn to serve the files the video files in my r2 bucket called loom got a custom domain hosted on namecheap i'm just going to keep it over there but i want you to help me connect it to cloudflare cdn over here so yeah for those of you who don't know i my domain is hosted on namecheap and yeah just letting you know and as you guys can see i'm building this right alongside you i haven't built this project before this is my first this is my first time building it i'm just gonna put this whole video out there so you guys can actually see how i build the problems and challenges i have to go through and how i actually overcome them i think this is going to be very valuable for especially junior developers okay so this is kind of a problem i can't actually keep my domain on namecheap the domain is registered on namecheap but the name servers are uh are hosted on vercel and i'd rather keep them over there okay so this is gonna be a bit of a problem because apparently i can't host my domain on vercel and still use cloudflare cdn so we're gonna have to to figure that one out but for now i guess while i figuring this out i can use the r2 public bucket url This is not meant for production As you can see it rate limited and does not use CDN caching But just for now, just to get this working, right? Let's just use that. So I'm going to go over to the bucket settings. I'm going to enable the public development URL, type in allow to allow this. There we go. And okay, so that's the URL for this bucket. Let me just test this. What if I said resume.pdf over here? Would I get the file? Okay, so I would get the file. Perfect. Yeah, this is my resume, guys. If anyone wants to hire me, you can read through this. But yeah, this thing works. We're gonna configure Cloudflare CDN later on. But for now, let's just get this app working, right? I'm gonna mention over here in the agent chat, gonna give it this URL and say use. Okay, that's not gonna work. It just asked me to approve a command. This is one thing that's very annoying about EnterGravity. as you're typing a prompt, if it asks you for a command, it just loses all input of that of that prompt. So that that really sucks. So let me just say use this URL to get the videos from Cloudflare, we will switch to Cloudflare CDN later on. Now the next thing is how How exactly do you figure out which ID belongs to which video, right? Well, that is going to have to be done via some sort of a database. But I actually don't want to use a database. I think that's way too overkill for such a simple app. So I'm trying to figure out what a better way or a simpler way to do that would be. And as I'm thinking out loud, I just figured it out. Let me show you. So here's what we could do. What we can do is we can have the ID of the file be the file name, basically. So instead of having custom file names over there, what we can do is we could set each file name to be, first of all, the actual name of the file, like Freelancer Intro, right? And then we could add a UUID over here and then the final video extension. And what we can do then is we can search for the file that has this particular UUID, just get it and play it, right? So let's implement that. And the reason we're doing it this way is because we eliminate the need for a database. I don't need to have a database anymore, right? Let me draw a quick database over here. Just drawing a table over here, right? Let's say we have an ID column, we have a file name column We don need to have a database to store each ID and each file name separately right we can put all of those information inside the actual file name that eliminates the need for us to manage a Postgres or SQLite database and you can get all that information from the file name so yeah let me bring that back let's go with an approach like that skip the build I'm gonna wait for the agent finish over here before I give it the next prompt. Just accept all the changes. This is the first stage of the app, so we don't really need to worry about the code right now. Modify the upload to R2 shell script to add a UUID to every single file before uploading it. Use the format I give you below. And then I'll pass in the upload script over here so it knows which file to edit. The file name format is going to be a file name dash uuid dot mp4 this way each file name will have a unique uuid at the end just before the file extension you can use this to figure out which file to play on the actual video player page so when i go to my website over here and i go to a video page you can grab the uuid that i using from the url and you can use that to search which file we gonna have to view in R2 Actually now that I think about it this approach might not actually work because like how would you know the name of the file oh my god wait a second yeah this might be a bit of a problem or actually no no i'm i'm overthinking this because we do know the file name and the url because we're yeah because we're sharing the links right i'm i'm retarded okay this is going to work i'm just overthinking this a lot anyway just let the script do its whole thing and i just realized they gave it a bit of a confusing prompt over here uh you don't need to find the video using the uuid alone you're going to be given the entire file name as well here's how we can structure this we can have a url segment that matches the file name entirely instead of using the uuid but we can also add the uuid to the file name let's do both. So as you can see, I'm going back and forth over here because I didn't actually plan this project beforehand. So like, so like, you can see me going through all of these different things, different approaches, and trying to select the best approach by actually iterating over here. So you can say something like website slash v slash file name let use that approach instead of using a UUID And let keep the shell script adding a UUID to the file name so that each file name has the original file name plus a unique UUID so that people can't exactly guess the file name on their own. And that's basically the reason why I wanted to use UUIDs in the first place. Okay, so that should be good enough. Let's let the agent do its own thing. In the meantime, let's run this website as well in development mode and see what the agent cooked for us. Okay, so we're just using the default Next.js page over here. No big deal. I have no problems with that. There we go. All the changes have been done. So now what we should do is we should re-upload the, what is it, the freelancer intro that we just uploaded. Let me get the command for that. There it is. And I wonder if I can still send a custom file name and it'll still add a UUID. I hope it does because if not, we're going to have to tell the agent to go fix that. But yeah, this is the kind of URLs we're going to generate from now on. Like I'm gonna put this on some subdomain that I own and it's gonna be so easy for a client to just go to this URL, watch the entire video I made for them. And you know, by the way, this video is getting extremely long. I've been recording for like 40 minutes now. it's probably best if I cut this project into a bunch of parts. Because as you can see, we literally only did like the first two steps or the first three steps, depending on how you want to count it. And we're already out of time. This video is getting way too long. So I'm going to let this script finish. We're going to test that we can actually view the video file. Hopefully we can, because I want to end the video now and film the next part later. But yeah, this is what I'm going to be building for the next couple of videos at the next couple of days at least. By the way, I know that I was very like all over the place, didn't have my thoughts organized and just didn't know what the fuck I was doing in this video. But that's mostly because this is how I build a new project, right? I have an idea for something that I want to build and then I just start implementing some things, start figuring things out, start iterating a lot, especially now that AI is writing all of my code. It's just so easy to iterate, right? I can ask the agent to produce like 500 lines of code and then not like the approach that I'm taking and just discard those 500 lines because code is so cheap now. And so this is how I build just having some ideas, trying to implement them, seeing how that looks and how that works iterating on every single idea and just trying to figure out the best approach Anyway the upload finished So let clear my screen now Let run the development server again. Hopefully this works. Maybe I need to configure cores as well. Cores, origins, and all those other policies. Hopefully not. If I refresh my bucket over here, you can see I have this URL. Let me just grab the entire file name for this and just paste it over there in my app. There we go. Let's copy that. Slash v slash. There's the video file. Let's see what happens. Okay, so the design isn't too bad, but it looks horrible. It looks very vibe coded. We're going to have to fix that later on. This intro text is completely hidden because it's, I don't know, I guess it's trying to mix some light or dark themes over here. What happens if I turn on dark mode? Okay, now that actually looks a lot better. So the design needs a lot of work. I guess they added a bunch of information down here as well. This is kind of cool, but we're going to have to remove most of it. The design needs a lot of work. And the video is not loading. So let's try to figure out why that is. Yeah, so as I thought, we're going to have to configure the course policy as well. Let's go back to our settings over here. There's the course policy. Configure course headers that R2 will set when accessing this bucket content from a browser Okay so I already have my course policy over here so why isn this working right okay so they already gave me this as a default let's try adding that and see what happens your bucket is now configured with the specified course rules let's refresh this and see what happens sometimes it does take like a couple minutes to refresh the cloudflare cache okay there we go you can see my funny clown face over there and now we're playing this video now just look at how much how much more professional this is right isn't this just so much better than using some youtube unlisted video that's going to have ads and sidebars and suggested videos or using some google drive link that just looks so cheap and unprofessional this is so much better right if i send a link like this to the client like look i could just copy this link and just send it over to some client and you can view the same video as this that looks so much more professional right and so this is what i'm going to be building over the next couple of days and i'm going to film the entire process and hopefully someone can get some value out of this anyway this video is getting way too long so let's stop right there i hope this new series that i starting is a bit useful for you guys because like let be honest i haven made these kinds of videos in a very long time i don film any project build series where I build a project like this and show you how I building it I haven't done that in a very long time. And the reason is, I just didn't think those videos were valuable anymore or that anyone would watch them because AI writes all of our code. You can give your agent any prompt and it can go and build the app that you want to build. But I also think that these videos are more valuable or these videos are valuable for a different reason for a better reason and that is you get to see a software engineer actually building the app actually going through so many different problems as you saw in this video my mind was just in like 20 different places at once i had no idea what i was doing because i was just so new to building this app right like i hadn't planned this script i hadn't planned this i didn't plan this video out very, like in a very, I didn't plan this video a lot. I just, you know, had the idea for a project and just started building it right in front of you, live alongside you. This is the kinds of problems you're going to be facing as you're developing your apps as well. And I just hope that, you know, these kinds of videos can help you out as you can see me going through the same challenges and just see how I overcome them. And hopefully that helps you as well. Anyway, again, this video is getting way too long. I'm not going to take any more of your time. 47 minutes. We've been recording for a very long time. Let's stop now and, you know, we'll do the rest in part two. See ya.