[0:00] Hey there. Welcome to day 15. In this [0:02] one, we're going to be processing video, [0:05] audio, and images using Python in a [0:08] package called Movie Pie. [0:11] [Music] [0:13] Now, we're going to start off by [0:15] creating thumbnails from any given [0:17] image, which is actually really useful [0:19] because you can automate the process of [0:21] creating thumbnails for all of your [0:23] videos, which is also really awesome if [0:26] you work on, let's say, for instance, [0:28] YouTube and you want to, you know, use [0:31] one of the screenshots from a video and [0:33] you don't want to have to scrub through [0:35] the the video itself and do a [0:36] screenshot. That's one of many examples [0:39] of why you'd want to use Movie Pie. So [0:42] before we actually get started, what [0:43] we're going to do is we have to set up [0:45] our system a little bit more. And the [0:48] reason for that is because Movie Pi is [0:50] really supercharged with two other [0:52] packages. One is called Image Magic and [0:55] the other one is called FFmpeg. So to [0:58] actually follow along with our guide on [1:01] getting this going, you can go to our [1:04] GitHub, which of course is [1:05] github.com/coding forrepreneurs. go to [1:08] the 30 days of Python repo and into day [1:11] 15 and setup.mmd. [1:14] This will show you some of the things [1:15] that we're doing and then the [1:16] installation requirements ffmpeg and [1:19] image magic. Now, if you're on Windows, [1:21] you can go to this timestamp right here. [1:23] That's going to actually show you how to [1:25] do all of it. Install ffmpeg and image [1:27] magic. And then we'll actually all come [1:29] back and work together. If you're on [1:31] Linux, well, you know that Linux has a [1:33] lot of different distributions, so we [1:35] can't cover installing on every Linux [1:37] distribution. So, just check out the [1:39] links that we have there. Really, it's [1:42] just ffmpeg.org or imagemagic.org. You [1:45] can go to both of those places and just [1:46] download the process that you need. [1:49] Okay, so for us Mac users, we're going [1:51] to be using Homebrew. Now, if you don't [1:53] have Homebrew installed, go to brew.sh [1:55] sh and then copy and paste this command [1:58] into your terminal so that you can do [2:00] something like this brew. Now, if you [2:03] see this, that's a good sign. And what [2:06] you're just going to want to do is [2:07] upgrade brew. So, just go ahead and do [2:10] brew update and maybe even brew upgrade. [2:13] So, after that, we're just going to run [2:15] brew install ffmpeg [2:19] and [2:21] image magic with a K at the end. We hit [2:25] enter. And of course, this installation [2:27] process might take a while depending on [2:29] your system. But after you do it, we can [2:31] just type out ffmpeg. And now your [2:34] entire system can use ffmpeg. We're [2:36] going to talk about this more in just a [2:37] moment. Uh but now that we have that, [2:39] it's really simple. Our movie pie is now [2:42] ready to actually use it because once [2:44] it's on your system, movie pie can [2:46] actually run with it. And now we can [2:48] finish the rest of the setup process. [2:50] So, if you're skipping the Windows part, [2:52] which you probably are if you're on Mac, [2:54] go ahead and jump to this timestamp. [3:01] To get the most out of Movie Pie on [3:03] Windows or really any system, we're [3:06] going to want to use FFmpeg and Image [3:08] Magic. Now, in this one, I'm going to [3:10] show you how to install FFmpeg directly [3:12] from that or you can use image magic. [3:15] Just go ahead and download its [3:16] installation and make sure FFmpeg is [3:18] checked. I'll show you to do both [3:20] things, but I want to start with [3:22] ffmpeg.org [3:24] and then going into download, clicking [3:26] on the Windows icon and Windows builds. [3:29] Notice this is provided by another [3:31] service, but it is a reliable one as [3:33] ffmpeg.org references it. So, we're [3:36] going to go ahead and download this zip [3:38] file. Once it downloads, you'll see [3:40] something like this in your downloads [3:42] folder. I'm going to go ahead and [3:44] actually rename this to just ffmpeg. [3:48] And I'm going to go ahead and bring this [3:49] over into my C drive. So into my C drive [3:54] and I'll go ahead and hit continue. [3:58] Now that it's in my C drive, I'm going [4:00] to go ahead and extract it here. So with [4:03] sevenzip, I'm going to hit extract here. [4:07] And the reason for this is this is now [4:08] where my location for ffmpeg will be. Uh [4:13] as you see that this directory has now [4:14] been created. And so I can actually [4:17] reference this in my environment [4:19] variables. So all I need to do is add [4:22] this to my systems path for the bin. So [4:25] I can actually call the application of [4:28] ffmpeg. I could certainly move that [4:31] binary right there anywhere I want to on [4:33] my system and be able to run it, but [4:35] it's probably best to just leave it [4:36] inside of this ffmpeg folder. Um, so I'm [4:40] actually going to delete this other one [4:42] that I have in here. If yours was [4:44] renamed this way, like if you still have [4:46] this naming convention al together, just [4:49] rename it to ffmpeg. It's going to save [4:51] us some time in just a moment. So now [4:54] what I'm going to do is add this to my [4:55] path. I'll hit the start menu and type [4:58] out environment [5:00] variables. And you're going to want to [5:01] edit the system environment variables. [5:05] And then we're going to go ahead and [5:06] click on environment variables. This [5:08] should pop up right here. Click on that [5:10] link. And then with that, we will see [5:14] this window icon and we're just going to [5:18] want to edit our system environment [5:20] variables for path right there and then [5:25] click on edit. [5:27] Okay, so in here you just want to make [5:29] sure that you add this right here. So c [5:33] ffmpeg/ben [5:35] and with that you will be able to [5:37] actually use ffmpeg in command prompt [5:41] and powershell. Uh so let's go ahead and [5:43] verify that by just opening up [5:46] powershell and ffmpeg. I should get [5:49] something like this. Now the reason of [5:51] course I renamed it was so that my [5:54] environment variables was really easy to [5:56] work with. Right? If I didn't rename it [5:58] then I would have to have a longer item [6:00] here for the path. once I added it into [6:03] those environment variables. Now, that's [6:05] the manual way of adding FFmpeg. A [6:07] automated way is using image magic. And [6:10] of course, we want Image Magic anyway. [6:12] So, I'm going to go ahead and go to [6:13] imagemagic.org. [6:14] Click on download. And then I'm going to [6:16] scroll down to the Windows binary [6:19] release. And I want to grab the first [6:22] one that it has here with the DLL.exe. [6:26] You can try some of the other ones, but [6:28] this one I found is the easiest one to [6:30] work with. So, I hit download on the [6:32] HTTP. [6:34] And of course, in my case, I actually [6:36] already downloaded it. Uh, it's right [6:38] here. So, I'll go ahead and double click [6:39] and open that. And we're going to run [6:41] this. So, uh, naturally, we're going to [6:44] accept to the agreement. Hit next. We [6:47] can install it in the default location. [6:49] That's fine. You can leave it as the [6:51] default name or you can change it. It's [6:53] up to you. Notice that in here, this is [6:56] the option to install FFmpeg. So, we [6:58] could have saved like two minutes or [7:00] three um installing ffmpeg by just using [7:03] image magic. But I wanted to show you [7:05] that there is the manual way of doing it [7:07] and that is a valid way. So, as it's [7:11] installing, I'm actually going to open [7:12] up my program files. So, back into my C [7:16] drive into program files and I'm going [7:19] to look for image magic. I'm assuming [7:20] it's already done. It looks like it is. [7:22] And I'm not going to view the [7:23] index.html. [7:25] So going into image magic here, if I [7:28] scroll down a little bit, I will see [7:30] that binary of FFmpeg. I believe it's [7:32] the same size as the one we just [7:34] downloaded. So let's go ahead and just [7:36] check that out by opening up a file [7:39] explorer, going into there into our bin, [7:43] and we've got FFmpeg right here. [7:48] So there the one that we downloaded [7:50] directly from ffmpeg is just a little [7:52] bit smaller than the one that's inside [7:54] of image magic. Perhaps they add some [7:56] additional things to it. Uh but now we [7:59] have ffmpeg installed two times. So we [8:02] can definitely use ffmpeg on our command [8:05] line which also means that movie pi will [8:08] be able to use it as well. [8:13] Now that we have FFmpeg installed, we [8:15] can open up terminal or PowerShell and [8:18] actually type out the command and see [8:20] all of these different options. Now [8:22] FFmpeg is powerful in of itself. You [8:24] don't actually need a whole lot of [8:26] things beyond it. But customizing it and [8:28] actually using it, I would argue is not [8:30] that friendly for a new developer, let [8:31] alone a Python developer. So we use [8:34] Movie Pi to do all sorts of things that [8:36] FFmpeg can do out of the box. One of [8:38] those things being like changing from an [8:40] AVI file to an MP4 file. So video format [8:44] changing. That's not that big of a deal [8:46] with it. Just FFmpeg or extracting audio [8:49] from a video. Also not that big of a [8:52] deal. But we're not going to actually [8:53] spend a whole lot of time on this. I [8:55] just wanted to mention it because it is [8:56] a really cool program and if you want to [8:58] learn more about it, I recommend that [9:00] you do. Okay. So with that out of the [9:02] way, let's go ahead and start our [9:04] project. I'm going to create a directory [9:06] called day 15. Now, in my case, I [9:08] actually have already created this [9:10] because there's a number of things I [9:11] want to do prior to actually getting [9:13] here. So, I made a folder called day 15 [9:15] and I made a pipv, which we'll do in [9:17] just a moment as well, but I also [9:19] downloaded a couple files here. I [9:21] downloaded an audio sample as well as [9:23] sample.mpp4. [9:25] So, if you want to actually do exactly [9:27] what I did, you're going to go back into [9:29] that repo and you can scroll towards the [9:32] bottom and look at the base project [9:34] start. Right? So, we're going to make [9:36] these directories here. These are where [9:38] we're going to store some of those [9:39] files. And then we're going to create [9:41] conf.py, which I'll do in a moment. And [9:43] then you can download these audio and [9:45] video samples. These are things that you [9:47] can use if you want. They're not that [9:49] big of a deal. It's just more of um we [9:51] need an MP3 file and an MP4 file. also a [9:54] video file and an audio file that we can [9:56] actually work with. In my case, my video [9:58] file also has audio. So, we will be [10:01] doing that as well. So, now let's go [10:04] ahead and actually create our project. [10:05] So, again, I already have day 15 as a [10:07] folder created. So, I'll cd into day15 [10:10] and then I'll go ahead and do pipv [10:12] install and we'll do python 3.8 and [10:16] movie pi. We'll hit enter. And of [10:19] course, mine's already installed. I [10:21] already have all of that thing, all [10:22] those things set up. Uh, but yours might [10:24] not. Next, what we want to do is [10:26] actually make all of these directories [10:28] here, right? So, inside of this project [10:30] that I'm working on, I want these [10:32] directories so I have somewhere to store [10:35] my outputs. That's the main thing. But [10:37] then also a uniform way of where our [10:40] inputs are coming from. So, if you're [10:42] following along with me, I recommend [10:43] that you do it in this way. That way you [10:47] won't have any issues with the code as [10:49] it relates to where things are stored. [10:52] Okay, so let's go ahead and go into pipv [10:54] shell and the first thing I need to do [10:58] is make dur data. I already have it. [11:01] Next one is make dur samples. Again I [11:04] already have it and then inputs and [11:08] outputs. [11:10] Okay, next what I want to do is actually [11:12] make a file called conf.py. Pi. Now, all [11:15] of my other actual modules will be [11:18] importing and using at least the sample [11:21] inputs and the sample outputs. Uh, so we [11:24] want to just go ahead and grab that and [11:26] make sure that we have it in our project [11:27] as well. So, on day 15, I'm of course [11:31] now in VS Code. I'm going to go ahead [11:32] and do conf.py [11:35] and just paste these things in there. [11:37] Now, if you're confused at all on how [11:39] these are working, uh, just a quick [11:41] recap. First of all, this gives us the [11:43] path directly to where conf.py is. [11:45] Baster gives us the directory that it [11:47] lives in, right? In my case, it's day [11:49] 15. And then we have paths to the [11:52] directories we just created, the data [11:53] one, the samples one, and all that. [11:56] Then, of course, the audio and sample. [11:58] You want to put that into the inputs [12:00] directory itself so that you have your [12:04] video and audio actually there and ready [12:07] to go. [12:12] Now, let's actually get to some of the [12:14] more interesting parts, and that's [12:16] actually coding it. So, in day 15, I'm [12:18] going to make a new file here, and I'm [12:20] going just going to call this one and [12:23] thumbs.py, [12:26] as in one thumbnails. And let's make [12:29] sure that this is inside of day 15. And [12:32] I'm going to go ahead and do from conf [12:34] import. Well, the things that I want to [12:36] import are sample inputs and sample [12:38] outputs. So, sample inputs and sample [12:42] outputs. [12:44] Now, of course, I want these things [12:46] installed so that I have something to [12:48] work from, right? So, I want to actually [12:50] grab my source file or in my case, I'll [12:53] call it source path. And I'll do [12:55] ospath.join [12:57] and it's going to be sample inputs and [13:00] then sample.mpp4. [13:04] Now in my case that is how I named the [13:07] video file. So of course that is how I [13:10] need to reference it right. Okay cool. [13:13] So that should give me the video file. [13:16] The next thing I need to do is actually [13:18] import from movie pie. I'm going to go [13:20] ahead and do from movie pie [13:25] editor [13:27] import [13:29] all. So this will import everything. Uh, [13:32] this isn't always standard practice, but [13:34] I'm actually going off of what they have [13:35] in their documentation to make this easy [13:37] for us. Now, if you want to be more [13:40] explicit, you could say video file clip. [13:43] This is actually the class that we'll [13:45] end up using, but again, I'm going to go [13:47] ahead and use all. Now, what I'm going [13:50] to do is initialize a clip. So, the clip [13:54] instance or this object that I'm [13:56] creating will come from a video file. [13:59] Um, so as far as I know, Movie Pie [14:02] doesn't work well with webcam. So you [14:04] can't actually do what I'm about to do [14:06] from webcam footage. It has to be a [14:08] stored and saved file. Although I think [14:10] there's a lot of different file types [14:12] that are that are actually supported. So [14:14] MP4 is not just the only one. So we'll [14:16] go ahead and say video file [14:19] clip. And then we're going to reference [14:22] the source that we had, which of course [14:23] is our source path. So I can do some [14:26] cool stuff with this. And I can actually [14:28] print out the clip reader. FPS. FPS [14:34] means frames per second. [14:38] So what this will give me is the amount [14:40] of frames that are shown every single [14:42] second. And I can also print out clip [14:45] reader [14:46] number of frames. So in frames and that [14:49] will actually give me the number of [14:51] frames that this entire clip has. Of [14:54] course, if you divided these numbers [14:56] together, you would actually get what [14:57] that clip's duration is, but you can [14:59] also do clip.duration, [15:02] and that should actually give us the [15:04] file duration of how long that is. [15:06] Anyway, so let's go ahead and open up [15:08] the terminal inside of our project here [15:10] inside of VS Code. I'm going to close [15:12] down the explorer for a moment, and [15:15] let's list things out. Let's go ahead [15:17] and change into day 15 and pipv shell. [15:22] Okay. and then python- i one thumbs.py. [15:28] Okay, so what we should see is three [15:30] print statements. The first one is the [15:33] frames per second. The second one is the [15:36] actual number of frames and then finally [15:38] the duration of the clip. And that's in [15:41] seconds, right? So this is seconds. [15:44] So what we want to do is actually turn [15:46] each frame into its own image, right? [15:50] And there's several different ways on [15:51] how we could go about doing this. One [15:54] way is to take what the duration of the [15:57] clip is and go every second. Like let's [16:02] actually do that. So I'll go ahead and [16:04] say duration equals to clip.duration. [16:08] Another way to call this is also clip [16:10] reader.duration. [16:13] And we can say for I in range well we [16:17] would do zero and then duration + 1. Now [16:22] that's to include that last second. And [16:26] what I is is this is going to be the [16:29] frame at [16:31] i seconds. [16:35] So whatever frame that is at i seconds, [16:37] you would actually be able to see that. [16:39] Okay. So let's go ahead and make sure [16:40] that that's string substitution is [16:43] correct there. So what I can do is I can [16:46] say frame equals to clip.get [16:50] frame and then the seconds being [16:54] whatever i is. Now if you remember back [16:56] to when I did the print statement of the [16:58] duration you actually need to use an [17:00] integer value. So this is 30.17 [17:04] seconds. So you actually need to use a [17:07] 30 seconds or 10 seconds, 9 seconds and [17:10] so on for this get frame call. So I'm [17:13] just going to call int of I. And with [17:16] this I can actually save this somewhere. [17:18] But before I do that, I'm just going to [17:20] go ahead and print out this frame so we [17:22] can talk about it. So I'm going to go [17:23] ahead and exit out of the the [17:25] interactive shell and I'll run that [17:27] again. Uh and and it's giving me a float [17:31] cannot be interpreted as an integer. So [17:33] this is my range here. So let's actually [17:35] turn my range and I'll go ahead and say [17:37] max duration [17:40] because of course ranges or this call [17:42] right here has to be a number. And going [17:45] back 30.17 [17:47] is not a number but rather a float. So [17:51] we'll just do int of duration + one. So [17:54] max duration is going to be whatever [17:57] that is. Okay. So um let's actually try [18:00] it again without integer of i. So we'll [18:04] see if that still gives me an integer or [18:06] not. And actually it should it should [18:08] actually give me an integer because of [18:10] what I just did with max duration [18:12] because range will loop through each [18:14] iteration. It will be iteration one or [18:16] actually iteration zero 1 2 and so on. [18:20] Um so let's go ahead and run that again. [18:22] So we see something very strange. We see [18:24] the frame at a certain number of [18:25] seconds, right? So that's that print [18:27] statement. Uh but then we see all of [18:29] these numbers here. And what this is is [18:32] actually what's called a numpy array. [18:35] And this is actually giving us the color [18:37] values for each individual pixel. And as [18:41] you know, an image can have thousands of [18:44] pixels. So it's actually giving us all [18:47] of that data. Now, this is not [18:50] necessarily that useful yet because you [18:53] may or may not know machine learning or [18:55] computer vision, but if you understand [18:58] or study any of those things, they [19:01] actually learn from the pixel value of [19:05] any given image. Right? So, this is [19:07] actually really cool because we can see [19:09] sort of the underlying basis for what [19:11] will be much more advanced usage of [19:14] grabbing images. So in other words, if [19:16] you were using machine learning, you [19:19] could run what's called inference right [19:21] here on this numpy array, which is [19:25] sweet. But what we can also do is use a [19:28] package called pillow to actually turn [19:32] each frame into its own image. So what I [19:36] need to do is actually install one more [19:38] thing. So, let's go ahead and close out [19:40] of that [19:42] interactive shell and I'll do pip envo. [19:46] This is the Python image library. What [19:49] pillow allows me to do really easily is [19:52] to take a numpy array and turn it into [19:56] an actual image. In other words, take [19:59] all of these values that are in this big [20:02] array, like think of it as thousands of [20:05] numbers and turn that into an actual [20:08] image in a very easy way and something [20:10] you'll see a lot once you get into [20:13] computer vision if you ever do. So, [20:15] we're going to do from pill import image [20:18] the actual image class. And this we are [20:20] going to go ahead and say new img equals [20:24] to image [20:26] from array. [20:28] Okay. So from array we're going to pass [20:30] in that frame there. So each frame is a [20:33] numpy array useful for inference or to [20:37] create new images. So we can just do new [20:39] image new img.save and then some sort of [20:43] output path. Right? So uh we did create [20:47] a directory called sample outputs. So [20:50] now what I'm going to do is I'm going to [20:52] create another one and I'm going to call [20:54] this the thumbnail [20:57] dur and we'll say ospath.join. [20:59] It's going to be the sample outputs and [21:02] thumbnails. [21:03] And then we'll do os.makedurs [21:06] and this thumbnail dur exist. [21:10] Um okay equals to true. [21:14] Okay. So now I have a directory as to [21:16] where I can store these things. So all I [21:18] need to do is say new img [21:22] file path equals to ospath jojoin that [21:27] new directory that we just created and [21:29] then some sort of file name. In my case, [21:31] I'm going to keep it as the name of the [21:34] number of seconds it is. So in other [21:36] words, the actual iteration that it is. [21:39] So f and then i. So I need to turn it [21:43] into a string of JPEG and then we will [21:45] save it to this path in just a moment. [21:48] Now the reason I recommend that you do [21:50] this as well has to do with sorting [21:52] later when we want to actually reuse [21:54] these images in the future to actually [21:57] sort them and turn them into a logical [21:59] video in the correct order. Uh which [22:02] we'll talk about that in just a little [22:03] bit. So let's go ahead and save this. [22:05] And this time I'm not going to print out [22:06] the frame itself. when you print it out, [22:09] usually print statements actually take [22:12] up some some memory and some bandwidth. [22:14] So, it actually slows things down quite [22:16] a bit. So, with that running, I'll go [22:18] ahead and run Python 1 thumbs.py. No [22:21] interactive shell this time. And I'll [22:23] just go ahead and run it. It's going to [22:25] do every single frame at those [22:27] individual seconds. Uh perhaps I want to [22:29] actually do frame at those seconds and [22:32] then also print out the file path as [22:34] well. So, I'll just go ahead and add [22:37] that in there. [22:38] saved at [22:40] that file path. So that's going to be [22:42] our new print statement. Uh which we [22:44] could run it again if we wanted to [22:46] actually see what that's going to be. Of [22:48] course, in my case, that's that's not a [22:50] big surprise as to where it is. But if I [22:52] look at my outputs, I can see each image [22:55] that's actually coming through, right? [22:58] So I've now actually created every [22:59] thumbnail per second, right? So every [23:02] second I can actually create a new [23:04] thumbnail. And now that's cool. So, we [23:06] can do it based off of the duration, but [23:07] what if I wanted to do it, let's say, [23:09] for instance, based off of the actual [23:12] number of frames or the frames [23:13] themselves, not this range here. So, [23:17] what I can do is let's just copy this [23:19] exactly and I'm going to paste right [23:22] underneath it. I can actually iterate [23:24] through all of the frames. So, let's [23:26] change the [23:28] variable inside of the iterable to [23:30] frame. And then this time, I'm just [23:32] going to say clip. [23:35] frames. [23:37] What this allows me to do is actually [23:39] circumvent using the get frame here. And [23:43] this will actually create for every [23:45] single frame. Now remember when we [23:47] actually printed out the number of [23:48] frames, I got something like 900. But [23:51] what I want is well, do I actually want [23:54] 900 of these frames? Potentially. And I [23:58] also want to actually make sure that I [23:59] know what frame number I'm on, right? [24:02] The iteration of this frame. Now, of [24:04] course, I could say something like I [24:05] equals to zero and then at the end of [24:08] this doing I plus equals to zero or one, [24:11] right? So, that actually counts each [24:13] time I'm looping through this. Or what I [24:16] could do is something even easier called [24:18] enumerate. So if I wrap this in [24:22] enumerate [24:25] a built-in Python function that actually [24:29] turns it into [24:31] enumerated like iterations right so now [24:34] I is that actual same number of [24:36] iteration much like we did with this [24:38] range here where it's a number this time [24:41] it's a number and then whatever the [24:43] iterable item is in this case it's a [24:45] frame so so this is doing it now every [24:48] single frame and it's going to actually [24:50] create a brand new image for me. So, [24:54] it's no longer based off of se uh [24:56] sections or seconds, but rather the [24:58] actual number of frames themselves. So, [25:00] I'm going to make another directory. [25:02] This time, I'm not going to call it [25:03] thumbnails dur. I'm just going to call [25:05] it thumbnails [25:07] dash frame or per frame. [25:12] So, thumbnail per frame dur. [25:18] and just leave it in as that. So, I'm [25:21] going to I'm going to get rid of my [25:22] print statements here, uh, just so I can [25:25] make it run just a little bit faster. [25:27] And I'll run that once again. And now, [25:30] if we look inside of that directory, I [25:33] might actually need to create the [25:34] directory. I think I skipped that part [25:36] of make dur. And yes, I did. So, let's [25:39] close this out. Let's make that [25:41] directory [25:43] as well. Let's run it again. And now I [25:47] should get both of those directories. It [25:49] might take a moment to actually run [25:50] through of it. And especially depending [25:52] on how long your clip is, uh, it might [25:55] take even longer. So this will do every [25:58] single frame, right? And I get [26:02] it. [26:04] Oops, I did enter frames. It should be [26:07] iter frames. [26:10] Well, little mistake there. Like iterate [26:13] the frames, right? iterate iter frames. [26:17] Okay. Um, so again, this is going to go [26:19] through every single frame. Now, if I [26:22] actually wanted this based off of [26:23] seconds and frames per second, let's [26:26] think about this for a moment. So, if I [26:29] grabbed the number of frames and the [26:32] number of frames per second, I can say [26:36] FPS equals to clip.reader.fps [26:40] and then the number of frames. So in [26:42] frames equals to that same thing up [26:46] here. [26:48] Now I can actually say seconds equals to [26:51] the number of frames divided by frames [26:53] per second. I might want to times it by [26:55] 1.0 to make sure that it's not rounding [26:58] on me. Python 3 doesn't actually do that [27:01] very often, but in case you need to, [27:03] just make sure you just run something [27:04] like that. Therefore, you don't have any [27:06] rounding errors. Um, so what I get here [27:10] is the number of seconds inside of this [27:12] clip. Of course, this is the [27:14] mathematical way to find it, even though [27:15] you can grab it directly from the clip. [27:18] So if I actually wanted to do this it [27:20] frames thing for those seconds, I need [27:23] to think about, well, what iteration do [27:25] I actually want to run this? Well, it's [27:27] on every second. So if I want to run it [27:29] every second, I need to do it based off [27:32] of some value of frames per second, [27:35] right? So if it's 10 frames per second, [27:38] I need to save a new image every single [27:42] 10 frames. So to think of this, I can [27:45] actually go off of the current [27:47] iteration, which is actually like this [27:49] is going to be you could call it in or [27:52] in frame or frame index, right? There's [27:57] all sorts of things you can think of [27:59] that as I'm going to leave it in as I. [28:02] So what I is going to say is this is [28:05] frame zero, this is frame one, this is [28:07] frame two and so on. So for me to know [28:09] that it's one of those seconds, it's [28:11] actually a very similar formula of this [28:14] up here. I would say that if I divided [28:18] by frames per second [28:21] um basically has a leftover a remainder [28:25] equals to zero, that means that it's [28:27] divisible by frames per second. And that [28:30] means that we could make the argument or [28:32] the case that it's actually a second [28:36] because again frames per second will [28:38] count the number of frames that it [28:39] happens every second. So another way to [28:41] actually write this of course is using [28:43] modulo. So if I said I modulo fps [28:49] equals equals to zero then I can [28:52] actually run this call and this would [28:54] actually happen at the current time. So [28:57] I could say current seconds now is equal [29:01] to the I / FPS, [29:06] right? So this will give me whatever [29:08] those current seconds are. And if I want [29:10] to actually change it into milliseconds, [29:12] which might be a good idea, we just [29:15] times this by a th00and. [29:18] So this is now going to be in here as [29:21] current seconds. And again, it's going [29:23] to be current milliseconds [29:27] or current [29:29] ms. [29:33] Okay, so this gives me a much different [29:35] look at this. Instead of having, you [29:37] know, 900 frames or however many frames, [29:40] this is actually going to do it on the [29:42] in number of frames that I want, right? [29:46] Which is pretty cool. So that also means [29:48] that if I wanted to change this to let's [29:51] say for instance every half second then [29:54] this call here is just going to be [29:56] slightly different. So let's go ahead [29:58] and grab this and paste underneath here. [30:03] And up here I'm going to go ahead and [30:05] make one more thing. One more directory [30:08] frames per [30:10] half half second [30:13] per half [30:16] second dur. [30:18] And let's go ahead and make sure we're [30:19] making that one. So os make dur and [30:22] exist. Okay. [30:25] Equals to true [30:27] and close this down. Okay. So going back [30:30] down to the very bottom one. This is [30:31] going to be every half second. Right. So [30:34] this this part is going to be a little [30:36] bit different. Um I can still enumerate [30:38] through the actual number of frames, [30:40] right? So either way I want to enumerate [30:42] through the number of frames and I can [30:45] say frames per I'll call it frames per [30:48] half second or FPHs. [30:51] Pretty sure that's not an actual acronym [30:53] anyone uses. But this is just going to [30:55] be the integer of frames per second [30:58] divided by 2.0. Right? So we want to [31:01] round it up to the nearest integer. So [31:03] this means then if I change that to FPS, [31:07] this should actually give me the every [31:11] frames per second or every half frame [31:13] per second. So if it's 30 frames per [31:15] second, this is going to be every 15 [31:18] frames I will be divisible by 15 with [31:21] zero remainder, which again was what [31:23] that's saying. This can still take the [31:26] current time, right? So I divided by FPS [31:30] is still giving us whatever that current [31:31] time is because again it's iterating [31:34] through all of them. But this time now [31:36] it's actually every half second. So [31:39] that's giving me now every half second [31:42] of when this is going to happen. So we [31:44] can run this [31:49] and again the reason that it's every [31:51] half second is we take frames per second [31:53] and we divide it by two. That means the [31:56] seconds have been cut in half and we now [31:59] have 15 frames per half second or if you [32:02] know if it's 30 frames per second then [32:03] it's 15 per frames per half second and [32:06] then we can actually run that iteration [32:08] here. Now you could do that every fourth [32:10] or or you could do it every other frame. [32:12] I mean, there's a lot of different ways [32:13] on how you could go about doing this, [32:14] but the idea is that now we have a [32:17] little bit better understanding, [32:19] hopefully a little bit better [32:20] understanding of how to do the math on [32:22] all of these different things, which I [32:24] don't think it's that complicated to do. [32:27] So, one of the things that I actually [32:28] ran into just now is I have one of my [32:32] directories with all of the items. So, [32:36] what I'm going to do is I'm actually [32:38] going to delete the entire outputs [32:40] directory. [32:42] Okay. And one one other thing I noticed [32:45] really quickly was my current [32:47] milliseconds turns into being um a float [32:50] number. So I just want to turn it into [32:52] an integer by using so that the file [32:55] name doesn't have any other periods [32:57] other than the JPEG there. And that will [33:00] that will solve that problem for me [33:02] there. And I also want to do that up [33:04] here as well. Okay. So when we actually [33:08] delete directories, this make durus will [33:11] make the parent directory as well, [33:14] right? So it doesn't just make the [33:15] directory at the end of it, but all of [33:17] them. So I'll go ahead and run this [33:19] again [33:20] and [33:22] we look in our outputs. [33:25] We get all of our thumbnails. Um, you [33:27] know, it's going to take a moment for [33:29] all of them to finish. Uh but once they [33:31] do, we now will have all of the [33:33] thumbnails or various ways on how to [33:35] create thumbnails from this im these [33:38] videos. Um and of course this works for [33:40] any video. Now uh the speed as to which [33:43] it's going to actually create the [33:45] thumbnails all depends on a number of [33:47] factors. One and the main one is how big [33:50] your machine is, how much memory is [33:52] being used, how big the video is. Like [33:54] those numbers obviously are going to [33:56] make a big difference on actually [33:57] creating any given thumbnail. So the [34:00] next thing is um the least efficient [34:03] method that I've done was this one right [34:05] here where it's getting the frame each [34:06] time. It's actually much better to just [34:08] iterate through the frames. This will be [34:10] a lot more effective. But that's a [34:13] little bit more complicated than just [34:14] saying oh through some duration. [34:16] Obviously it's going to iterate through [34:18] each second because the duration itself [34:20] is in seconds. Um so that makes th that [34:25] whole process a lot easier. But iter [34:27] frames or iter frames will absolutely be [34:30] more efficient and thus much faster um [34:33] getting all of our data. But now if we [34:35] see in our thumbnails per half second um [34:37] we've got 0 500 1,000. Again if it's in [34:41] milliseconds that gives me these half [34:43] seconds. So 500 milliseconds is half of [34:46] a second. And that's the reason I [34:48] actually made it into milliseconds is to [34:51] make sure that we're not putting 0.5 [34:54] point JPEG, but rather 500 milliseconds. [34:57] And the reason I did it in per frame as [34:59] well is just to kind of get in the habit [35:01] of like, yeah, I want to make sure that [35:03] this is done in milliseconds. This is a [35:07] a very much or it's a much more robust [35:09] way to actually grab what any given time [35:12] stamp is because it's a really simple [35:14] calculation to then grab what the actual [35:17] seconds are. Just knock three zeros off [35:20] of it and you get the number of seconds [35:21] that are in there or the number of half [35:23] seconds. Um, cool. So, that's actually [35:26] creating thumbnails. Um, now of course [35:28] this is just one piece of the very many [35:32] things that Movie Pie can do for us. Um, [35:35] but this is a very practical one. Um, so [35:38] hopefully this part makes a lot of sense [35:40] for you. But stay with us cuz we're [35:41] going to do a lot more with Movie Pie. [35:48] Now, we're going to go ahead and create [35:49] a video from a folder of images. And in [35:54] that last one, I created the Python [35:56] module one thumbs. I'm actually going to [35:58] change it to being the number one [36:01] instead of OE. [36:04] This just gives me a little bit better [36:05] order of how my files are going to come [36:07] out. That's it. So, inside of day 15, of [36:10] course, I'm going to go ahead and make a [36:11] new file in here. And we're going to [36:13] call this two. And this is dur to vid as [36:17] in directory to video or folder to [36:20] video. So, back in one thumbs, I'm going [36:23] to go ahead and just copy the first [36:26] eight lines into here. Now, the reason I [36:30] have these first eight lines, well, [36:31] first of all, I probably don't need that [36:33] source path anymore, [36:36] but I do want these different [36:37] directories. And the reason for that is [36:39] because I'm probably going to make a [36:40] video off of each one, or at least have [36:42] the option to do that in the future. So, [36:45] let's go ahead and just grab a path or [36:49] set a path for our output video. And [36:53] this is going to be sample outputs [36:57] just like we did here. And then this one [36:59] is going to just be the thumbs.mpp4. [37:04] Okay. So the nice thing about movie pie [37:06] is it will actually infer a lot of [37:08] things from the extension that you use [37:10] as in the codeex necessary to actually [37:13] make that. Um we're not going to worry [37:15] about that too much. So just like what [37:18] we saw before we had the image or rather [37:22] the video file clip. [37:25] We can also do another one called image [37:27] sequence clip. So, I'm going to go ahead [37:29] and say clip equals to image sequence [37:34] clip. [37:36] And this is also imported from the Movie [37:39] Pi editor. So, if you in the last one [37:42] didn't use the all import, you would [37:43] want to make sure you import that. And [37:46] this is now a directory or file paths of [37:50] the images that we want. Right? So, if I [37:53] actually put in this [37:56] thumbnail dur, it might work, but I [37:59] actually want to make sure that this [38:00] thumbnail directory actually has the [38:02] files I need. So, I'll go ahead and just [38:05] say this dur equals to whatever that [38:07] thumbnail directory is. But really, uh, [38:10] we're going to go ahead and say [38:11] ospath.list [38:14] dur as in list directory. So, all of the [38:17] items in there are there. So, I'm going [38:20] to go ahead and say the file paths [38:23] equals to well, we're going to do an [38:25] iteration here. I'll just go ahead and [38:27] first off say path for path in this dur. [38:32] Uh, but what that actually is going to [38:34] give me is not a path, but rather a file [38:36] name. But I just wanted to put that just [38:39] to make sure that I can add this if [38:41] statement if path.sith [38:45] JPEG. I just want to make sure that all [38:47] of my JPEG images are coming through [38:48] here. Because in the case of this [38:50] thumbnail directory, there might be [38:52] other files in there. And of course, I [38:54] want to ignore what those other files [38:55] are. And this is actually not a path. [38:58] This is a file name. So I'm going to [39:00] just change it to fname as in file name [39:03] like that. So these are actually file [39:05] names. Now to turn it into a path, we [39:08] would just do ospath.join [39:10] that original thumbnail dur. So [39:13] thumbnail dur [39:16] and that file name. [39:20] Now this is an inline iteration. Another [39:22] way to write this I will just show you [39:25] is file path equals to this for fname in [39:30] this dur then we'll say if fnames [39:34] with jpeg [39:38] then we'll go ahead and say path equals [39:39] to ospath.join join thumbnail dur fname [39:45] and then file paths.append [39:48] path [39:50] uh that is the exact same thing but this [39:52] one is just all in one line. Cool. I'm [39:55] going to leave that commented out. [39:58] That is a little bit of a review but [40:00] hopefully it makes sense. But now that [40:02] we've got these file paths I can go [40:04] ahead and grab this clip just like that. [40:08] And I can run something called [40:09] clip.right [40:12] video [40:14] file. Okay, so image sequence clip. No [40:17] surprise here. It actually is a sequence [40:20] of images and we're just bringing them [40:22] all together. And then we're going to [40:24] write this file. But I also need to [40:26] declare the frames per second here. So [40:29] in this case, I'm going to do four [40:30] frames per second because again I'm [40:32] using only a handful of images. So if I [40:35] want this to be a little bit longer, [40:37] I'll have the images last a little bit [40:39] longer. So each image as a frame will [40:42] last a little bit longer. Um, so that [40:44] turns it into a frames per second of [40:46] being four. And then the writing to the [40:49] file will be our output video. [40:52] Okay. So now that in our project, I'll [40:55] go ahead and run python 2_dur [40:58] to vid.py [41:01] and hit enter. [41:03] And I'm getting we don't have list der. [41:06] Oops. That should not be os path but [41:08] rather os.lististerlistister. [41:11] There we go. And we run it again. And [41:14] this time it does a little bit more [41:16] processing. And now it's actually [41:17] showing us the movie pi thing. And if we [41:20] look at our outputs, we should see a [41:22] video here. [41:24] And if I reveal it in the finder window [41:27] or the file explorer, depending on where [41:29] you are, uh you should actually see it. [41:32] it kind of running in a way that that I [41:36] expected. Okay. So, um that is using [41:41] every second, right? And and it's every [41:43] second as a frames per second. So, if I [41:46] wanted to change it to where it was [41:48] literally every second, I would just [41:50] change frames per second to being one. [41:53] And what this would do is actually turn [41:54] this into a 30- secondond video. It [41:57] doesn't actually have all of the frames, [41:59] but it is the same duration of a video [42:02] as the original one. So, it it's really [42:05] it's going to be a lot more choppy than [42:07] the original one. So, that's kind of [42:09] cool. But, we actually have a problem [42:12] with how this is. And that's our file [42:14] paths. So, I'm not going to actually [42:17] write this clip just yet. I'll go ahead [42:19] and print out the file paths themselves. [42:22] And I will also run the interactive [42:25] shell here with dash I. Hit enter. Close [42:29] this down a little bit. Open this up. [42:33] And what I see is some sort of strange [42:36] ordering going on here. Right. The very [42:38] first item is 8.jpeg. The next one is [42:42] nine. And actually upon further [42:45] inspection, you might see some issues [42:47] with the video itself, like the ordering [42:49] of the video, like this is out of order, [42:53] uh, which you could verify by actually [42:54] looking at the video. Um, so yeah, this [42:57] is nice. This is super convenient of a [43:00] method to actually be able to just, you [43:02] know, get a list of file paths and then [43:05] output them to a video. Uh, so that's [43:08] definitely the easy way, but this is [43:10] incorrect. This is not actually how we [43:12] want to go about doing this. So I'll [43:13] leave this method in here in case you [43:15] want to create a video from these file [43:17] paths in this way. But what I want to do [43:20] is actually take a little bit of a [43:22] harder step and that is actually better [43:25] understanding how to go through an [43:27] entire directory using a method called [43:30] walk. Um so the first thing I want to do [43:32] is create a dictionary and I'm going to [43:35] call this dictionary directory as in the [43:38] directory itself. So directory and it's [43:41] equal to an empty dictionary. And then [43:43] we're going to do for root dur files n [43:48] os or rather not files in files but [43:51] rather files n os.walk [43:54] and then the directory that I want to go [43:56] through. In this case I'm going to do [43:58] thumbnails per frame. [44:00] Okay. So this will actually walk through [44:04] that directory, every file that's in [44:06] there, including child files. So if you [44:09] have other directories in there, it's [44:10] going to definitely go through all of [44:11] those. So to get the file path, we need [44:15] to actually go through each file. It's [44:18] really simple. We just do for the I'll [44:21] say underscore file in files. The reason [44:24] I'm using underscore file or well, let's [44:26] just use fname in files. The file path [44:29] is ospath.join. join and there's going [44:32] to be root and fname. [44:36] Okay, so this will give us the file path [44:38] that I'm looking for for every single [44:40] file. We could print all that out to see [44:43] exactly what it is that I mean by that. [44:45] Um, but what I want to do is actually [44:47] grab and create key value pairs for [44:50] every single file. So that means that [44:53] I'm going to go ahead and come in here [44:54] and say try key equals to the float of [45:00] name.replace [45:03] and it's going to replace JPEG because I [45:06] know it's a JPEG image with an empty [45:08] string. And then it's going to try and [45:11] grab from the fname. It's going to try [45:13] and look for a float item in there. [45:17] Otherwise, it'll run an error. Like if [45:19] there isn't an actual float in there, [45:21] it'll run an exception. And that in that [45:24] case, we'll just say key equals to none. [45:27] And then we'll do if key is not equal to [45:29] none, then I'll go into my dictionary [45:33] value that I set up here for the key. [45:37] And we'll set that equal to the file [45:39] path or the actual value that we're [45:42] going to be using. So this now gives me [45:44] a directory full of key value pairs that [45:47] are coming from the name of the file [45:49] itself. So the name of the file will [45:51] have a number or um you know a big [45:54] number. It doesn't really matter. It's [45:56] going to turn that into a float and then [45:57] that's going to be my new key value pair [46:00] for this d uh directory this directory [46:04] dictionary that is. Okay. Um, so now [46:07] that I've got that, what I can do is I [46:09] can say for K in sorted [46:13] directory [46:15] keys [46:17] and then I can actually print out what K [46:19] is. Okay, so let's go ahead and try [46:22] this. I'm going to open up the terminal [46:23] here. Let's clear out what we've got. So [46:26] I'll exit out of this and run the [46:28] interactive shell again. And what I see [46:31] is all of these numbers, right? So I've [46:33] got 0.0 zero and so on all the way down [46:36] to 3,00. So, they're actually in order. [46:39] And that's actually why I did this is to [46:41] to add the name of the file to the [46:46] directory and turn it into the file [46:47] path. Now, you actually don't have to [46:50] convert it into a float. That's not 100% [46:52] necessary. You could just remove the [46:54] file extension and use that as the key [46:57] and then sort it as well. So this will [46:59] also sort ABC or or like character [47:01] values as well if if you need to. Uh but [47:04] with this what I'm going to do is I'm [47:06] basically going to grab whatever the [47:08] file path is at this key value pair and [47:11] say new paths equals to this empty [47:17] like list here. And so I'll go ahead and [47:20] grab the directory at whatever that key [47:24] value is. [47:26] And then that's going to be my new file [47:28] path. So I'll say file path equals to [47:31] that. So new paths.append [47:35] file path. [47:38] And I don't need to print out that print [47:40] statement anymore. And I can scroll back [47:42] up to my clip or that original clip and [47:47] actually output this based off of my new [47:51] paths now. [47:53] Okay. So, that's another image sequence [47:56] clip. It's now in a different order. Uh, [47:59] so if I exit this out, uh, I'm still [48:02] doing frames per second. Let's just [48:04] change that to, I don't know, 10. Going [48:06] to be a much shorter video. Um, so this [48:08] should now be in an order that makes [48:10] sense based off of how I named those [48:13] files. Uh, and then let's go ahead and [48:16] open up the finder [48:18] for this one. And this looks a lot [48:21] closer to the order that I actually had [48:25] in that video. It should be actually [48:27] pretty accurate to that order. Uh but [48:30] there's actually one more thing that I [48:31] can do is I can actually turn each [48:34] individual file path into a frame [48:36] itself. So I want to use something [48:39] called image clip. Not just image [48:42] sequence clip, but image clip. Uh it's [48:45] another class that's imported by the [48:47] editor by default. So now what I can do [48:50] is say [48:52] after I have these new paths I can say [48:55] my clips equals to just an empty list [48:59] here. And now I can say for path in the [49:03] list of [49:05] these new paths [49:09] which it is a list but I'm going to [49:10] change that in just a moment. Um, now [49:13] I'm going to go ahead and say clip or [49:14] rather frame equals to image clip of [49:19] that path. And let's go ahead and just [49:21] print out what that frame is and what it [49:23] looks like. So we'll exit out of here [49:26] and run that again. And we're getting an [49:29] object here. It's an image clip object. [49:32] Uh, if you printed out the [49:36] dur. So doing the dur gives you all of [49:38] the methods available on there. uh sub [49:41] clip is one of them that's really useful [49:43] for images which we'll cover in a little [49:45] while. Uh but what we want to find is [49:48] the img. So we actually want to append [49:51] my clips.append [49:54] frame img. [49:57] And after we do that we will then make [50:02] our image sequence clip. Actually, let's [50:05] make sure we keep this in as a backup or [50:09] for reference that is. We'll put my [50:11] clips in here now. And our frames per [50:14] second, I'll just do 22. We'll exit out [50:16] of here. And then we'll run that again. [50:20] And of course, if you wanted to actually [50:21] see what the frame img looked like, [50:25] I'm going to guess that you might have [50:28] some intuition as to what that will look [50:31] like. And if you didn't, that's okay. [50:33] but it's a it's a numpy array just like [50:36] what we talked about before. So that's [50:39] actually how you would get the numpy [50:40] array from a specific image itself. So [50:43] if you wanted to open up any given image [50:44] and turn it into a numpy array, that's [50:47] one way to do it with actual movie pie. [50:50] Uh but now that we've done this, I'm [50:51] going to go ahead and take a look at my [50:54] new outputed clip. [50:56] And this one's going to go really fast [50:58] cuz it's only it's 22 frames per second [51:01] and there's not that many frames. Um, so [51:04] that's the the more challenging way to [51:06] do it. Um, now of course you could use [51:10] your new paths this order and just stop [51:12] here. Just like reorder them in some [51:15] sort of way. There are more advanced [51:17] methods of reordering this stuff. So [51:20] like based off of the date and time that [51:22] is created and and stuff like that. [51:24] Those things I'm just not going to get [51:25] into here. But the purpose of this was [51:27] to see that there's multiple ways to [51:29] take a directory and turn it into a [51:32] video. So now that we understand how to [51:34] go from video to images and images back [51:38] to video, now let's go ahead and take a [51:40] look at how to turn a video into a [51:42] subclip and then a GIF. [51:47] All right. So, let's go ahead and create [51:49] a new file in our project here. And [51:52] we'll call this three create gif.py. [51:56] And we're going to do a lot of the same [51:58] imports we did already. [52:01] We might not need pill, but I'll leave [52:03] it in there just in case. And I'm also [52:04] going to do another import that we'll [52:07] come up with in a moment, which is from [52:10] movie pie.fx.all [52:13] import crop. Now, the actual location of [52:16] this might change, but the idea is that [52:18] we want to crop our video clip at some [52:21] point as well. And I want to go back to [52:24] the first one to grab the source video [52:27] itself. [52:30] So, coming back into three. [52:33] Okay. So, we've got our source file [52:36] here. And as you remember, just creating [52:38] a clip is as simple as doing clip equals [52:40] to video file clip and the path that we [52:45] have it. In our case, it's the source [52:46] path. [52:48] And it's really simple. We can set our [52:50] frames per second. And I'll just set the [52:53] frames per second to the exact clips [52:55] frames per second. So clip reader.fps. [52:59] And then we can get a subclip of this. [53:02] Now a subclip is just a portion of the [53:05] clip. Now, I would check the MoviePie [53:08] docs if you want more details on this. [53:11] But the general idea is that I can say [53:12] clip equals to clip subclip [53:16] and then the time that I want. So, if I [53:19] wanted between, you know, 10 and 20 [53:22] seconds, so this is a 10-second long [53:25] clip, you would just write that. But [53:28] yes, there are more advanced options for [53:30] this. Now, when you actually call [53:32] subclip, it returns back another clip. [53:34] So I'm just going to call this sub clip. [53:38] And then that itself is a video file [53:41] clip itself from the original one. And [53:44] so I can just call subclip.right [53:48] GIF. And then we want to actually have a [53:51] output path. So let's go ahead and copy [53:54] our source path and call this output [53:58] path [54:00] one. And I'll call this sample.gif. GIF [54:05] sample one.gif and we'll put this in [54:09] our sample outputs. And we might as well [54:12] actually make a gif dur. So I'll say gif [54:15] dur equals to ospath jojoin sample [54:20] outputs and gif and os.maked [54:25] gifter and exist. [54:29] Okay. [54:31] Okay. So now I'll actually put it into [54:34] my GIFs directory. Let's call it GIFs [54:38] instead of just GIF. Okay. So my first [54:41] output is like this. Now notice I only [54:44] took a small clip of it. I didn't [54:46] actually change the size of it. So I [54:48] will change the size of it to speed up [54:51] the time it takes to actually make this [54:53] GIF. So we can say subclip equals to and [54:57] this is going to now be subclip.resize. [55:01] and it's going to take a dynamic width. [55:04] So, we'll just say width equals to 320. [55:08] So, this will automatically resize it [55:11] without breaking the [55:13] scale of the actual clip itself, which [55:17] we'll see in just a moment. And you [55:19] could designate something like height [55:21] equals to 320 as well, um, which we'll [55:24] also take a look at as in just a second. [55:26] But the idea here is whenever you run a [55:29] method like this or like this, it [55:31] actually returns back the original [55:33] instance. So running subclip like this [55:36] will actually allow me to resize it. But [55:39] if you forget and you run something like [55:41] this and you're wondering, hey, why [55:43] isn't it being resized? Well, that's [55:45] because you need the result from this. [55:48] So by setting sub clip equaling to this [55:52] result, it just sort of resets this [55:54] variable. um all the way through. Okay, [55:57] cool. So, now that we've got that, let's [55:59] go ahead and try this out. Of course, [56:01] I'm inside of day 15. So, python- [56:04] i3_create_gif.py. [56:08] Hit enter. And notice it's saying it's [56:12] using image io. Now, this actually takes [56:15] a good amount of time considering that [56:17] it's just a gif image. So, you can [56:20] actually do one more thing on this. [56:22] Write gif. You can set a couple options. [56:25] One of them being frames per second. So, [56:27] I can use it based off of the original [56:29] clip, which is what its default is, or I [56:32] can set my own frames per second. You [56:34] know, if I want it more choppy, I could [56:36] set it slower. If I want it to be [56:39] faster, also, well, either way, if it's [56:41] not the same frames per second, it's [56:42] going to be choppy feeling. Uh, and then [56:45] I can also declare the program I want to [56:47] use. So, in my case, I actually want to [56:48] use ffmpeg. [56:50] Um, but it used image io as a default. [56:54] So, we can take a look at sample [56:55] one.gif. And there it is. So, it has [56:58] that width that I set, but this is [57:00] actually the GIF image. And it will loop [57:02] over and over and over again. That's [57:04] what gifs do by by default. Uh, so this [57:06] is pretty cool. So, this is a nice way [57:08] if you had a like a tutorial of some [57:12] kind for a product and you just wanted [57:14] to have gifts of them, this would be a [57:16] way to do it. Um, but let's actually use [57:18] the program ffmpeg and run this again. [57:21] So, I'll exit out of here. And this time [57:24] I won't use the interactive shell. I'll [57:25] just call it and we'll run it again. So, [57:29] now it's using ffmpeg. Um, it's going to [57:31] be a little bit faster, but it also [57:33] might make a much smaller GIF file. [57:36] FFmpeg seems to be a little bit more [57:38] efficient at creating GIF files [57:40] themselves. Um, but you get sometimes [57:43] you'll get this like weird blurriness to [57:45] it. So gifts aren't a perfect, you know, [57:48] a perfect thing that happen. They don't [57:50] always work great. Um, but you can also [57:54] instead of resizing it, you can just [57:56] leave it as the original size. Um, so [57:59] that's another thing to consider. Now, [58:01] if you're getting a lot of very [58:02] pixelated items, uh, one of the options [58:06] would be to resize a clip, save it as a [58:09] video, and then reload it as a new clip [58:12] from that video and that new size. and [58:14] then run this as well. So, that is [58:17] another option that I've had some pretty [58:19] good results with, too. Okay, so this [58:21] one crashed. It actually crashed that [58:23] window, which is pretty funny. Uh, but [58:26] the reason it crashed it is probably [58:27] because of the size of the GIF itself. [58:29] So, if I look inside of the directory [58:31] here, I see the GIF is 175 megabytes at [58:35] the original size of the video. Um, so [58:40] when I run it now, it's not nearly as [58:42] pixelated as it was. Um, but it's still [58:45] a ginormous file. Uh, so that is [58:49] something to think about, too. Okay, so [58:51] back into 30 days. I'll go back to [58:54] create GIF. And there we go. So I do [58:58] want to use that resized version. And [59:00] perhaps you'll use it maybe not that [59:03] quite that small. uh you can use 500 as [59:06] a width, but it's really up to you on [59:08] how you go about doing that. Okay, so [59:10] the next thing is actually creating a [59:13] cropped clip. Okay, so I'm going to be [59:16] going off of this original clip still. [59:19] So it's still that same clip. And I'm [59:22] going to go ahead and grab the width and [59:23] the height of this by doing clip. size. [59:27] And then I already have the frames per [59:28] second declared, which is this right [59:30] here. And now I'm going to go ahead and [59:32] grab a clip of it. A new clip or let's [59:35] call it a subclip [59:38] 2. That's clip.ub. [59:42] And again, I'll do the between 10 and 20 [59:46] seconds. And now I'm actually going to [59:48] go ahead and crop this clip using that [59:50] function that I brought in. So to crop [59:53] this, it's really simple. We'll just [59:55] call it cropped [59:58] clip. That's going to be equal to [60:00] calling that function itself and then [60:02] calling subclip on it and then the [60:06] width. So this is going to crop it on [60:09] how we want. So I'm going to go ahead [60:10] and say 320 and the height being 320 and [60:15] then the x center being the width [60:19] divided by two and then the y center [60:23] being the height divided by two. [60:25] So this is basically saying, hey, how do [60:27] we center this image out itself? So this [60:30] is going to take the exact center of the [60:32] original clip. It's going to go right in [60:34] the middle of that, which is what this [60:35] divided by two is. The Y center is going [60:38] to take right in the middle of of that [60:39] one as well. So even if it's widescreen [60:41] or it's not already a square, this will [60:43] actually turn it into a square. So you [60:46] can think of this as a square clip [60:50] or square cropped clip, right? And now [60:55] we can do square cropped clip right GIF. [61:00] And this time I'm going to go ahead and [61:02] give a new output path. And we'll give [61:04] this two sample two. [61:09] So right GIF and [61:12] output path frames per second. This time [61:15] I'm going to go ahead and use the [61:16] original frames per second. And then the [61:19] program again being ffmpeg. [61:23] We save that. The other one, the other [61:26] GIF that I create, I'm going to go ahead [61:27] and just comment that one out. Let's [61:30] open the terminal back up. And I [61:32] probably need to reactivate [61:34] my virtual environment. So, pipv shell. [61:38] And then I'll go ahead and run python [61:42] 3 create.py. [61:46] We hit enter. And it's just hanging. So, [61:51] C to cancel it out. Uh, if I scroll [61:53] down, I actually did a spelling error on [61:57] ffmpeg. That must have been the issue [61:59] before as well. So, let's go ahead and [62:02] run this. Now, it's actually running. [62:04] Okay, cool. Um, so let's go ahead and [62:07] take a look at our results. So, in the [62:09] finder window, sample 2.gif, [62:12] um, it is a square image now, and it's [62:15] off of the time that I designated [62:18] before, which of course we could compare [62:20] that to that other sample. uh to to just [62:23] see what that is. And that sample is [62:24] still way too large. So I'm going to go [62:27] ahead and use the resized version. [62:32] And I'm also going to use the other [62:35] frames per second. [62:38] Try that again with sample one. [62:41] And this one, of course, still takes a [62:43] while because I made it still fairly [62:45] large image. Um so let's see what's [62:49] going on with this one. [62:52] There we go. Um, so it should be 500 [62:55] pixels wide, [62:57] which we can verify inside of the [63:01] Finder. It shows me that it's 500 pixels [63:03] wide. And this is giving me, you know, [63:05] exactly the same thing that's going on [63:07] here. It's just it's cropped in a [63:09] different way. Okay, so that's how you [63:12] make gifts. Now, how useful is this in [63:15] particular? It's hard to say. I think [63:18] that if you have a crop size that you're [63:20] actually looking for, um, it's probably [63:22] going to be a lot easier of actually [63:25] cropping a part of a video and then [63:28] saving that in particular. All right, so [63:31] I don't know how often you're going to [63:33] be making gifts necessarily, but the [63:35] nice thing to know is that Movie Pie has [63:38] the ability to write gifts on any clip. [63:40] So you just have to write underscore gif [63:42] just like that on any given video clip [63:45] and it will create a GIF for you. And I [63:48] would recommend that you use ffmpeg [63:49] because the size of the gif is going to [63:51] be a lot smaller. Um so that's pretty [63:54] cool. And then the other part of this is [63:56] that if you remember creating a video [63:59] file is just write underscore video file [64:02] on any given video clip as well or an [64:04] image sequence clip. In fact, you can [64:07] try out write GIF on a whole variety of [64:11] different places if you need to. Uh, so [64:14] I'm going to leave these write [64:15] statements out and I'll let you use it [64:17] uh how you see fit. Uh, but that's [64:19] writing gifts. [64:25] So now what we're going to do is add a [64:27] background soundtrack to our video that [64:31] just has spoken narration basically. So, [64:35] what we want to do is verify that our [64:38] inputs have audio and sample. Now, if [64:42] your video doesn't actually have audio [64:44] yet, a lot of the methods here will [64:46] still work. You just skip a step, which [64:48] I'll show you in just a moment. But, [64:50] let's go ahead and start off by creating [64:52] a new file inside of day 15. And this is [64:56] going to be four. And I'll call it mix [64:58] audio.py. [65:00] And I'm going to go ahead and go to [65:02] thumbs.py Pi and just grab some of the [65:05] defaults there. Going to grab the sample [65:08] related items here. And then I'm also [65:11] going to grab the output dur or at least [65:14] some of the base of it. And this one I'm [65:16] going to call this mix audio dur. And [65:20] I'll call this mixed [65:22] audio and ospath dot or rather os.maked [65:29] and mix audio dur exists. [65:33] Okay, being true. Okay, cool. [65:36] So, like we've seen before, we're going [65:38] to go ahead and grab our original clip. [65:40] So, the clip itself, of course, is video [65:44] file clip, and it's going to be the [65:46] source path. I want to go ahead and grab [65:50] the original audio here. So, original [65:54] audio, [65:55] and that's going to be clip.audio. [65:59] And this original audio I'm going to go [66:01] ahead and write an audio file. [66:07] And this of course I need to actually [66:08] set to a a path of its own. Uh and that [66:12] path is going to be similar to this mix [66:14] audio dur but it's actually going to use [66:15] that one. So I'll go ahead and say og [66:18] audio path equals ospath.join [66:22] and this mixed audio directory with [66:26] og.mpp3. MP3. [66:30] Okay. Uh, and this I'm going to just go [66:32] ahead and write that audio file out. So, [66:35] I'm going to go ahead and run this. Now, [66:37] let's just make sure that I can actually [66:39] run it and I can get the correct audio [66:42] coming out. So, mix audio.py [66:46] and I hit enter. It's makes og.mpp3. [66:50] So, in outputs, we got mix audio here. [66:54] Ogmpp3. Let's go ahead and reveal this [66:56] in our Finder or of course if you're on [66:59] Windows, it would be in your file f [67:02] explorer. And in this case, I actually [67:04] have it working. I don't know if you [67:05] could hear it or not, but um I have that [67:08] audio working. I definitely just [67:09] verified that that is the original audio [67:12] clip. Now, the reason I did it this way [67:15] has to do with just the finicky nature [67:19] of Movie Pie. um and what we're trying [67:22] to do here, which is mixing two [67:24] different audio clips into one and then [67:27] bringing it right back to that original [67:28] video clip. Um so that was that was [67:31] something that we have to consider with [67:32] that. Okay, so we have our source video [67:35] path. Now I'm going to add my source [67:38] audio path which I believe I just called [67:42] and here we called it audio.mpp3. [67:46] So this is going to be audio.mpp3. [67:50] Okay. And you could open that up and [67:52] hear what it sounds like. And it's [67:54] essentially just some free music that we [67:57] can use. Um, so what I want to do then [67:59] is grab that as a clip itself. So we're [68:02] going to go ahead and say background [68:04] audio [68:06] clip equals to audio [68:09] file clip and then our audio source [68:13] path. [68:15] So audio file clip is automatically [68:17] imported with this as well. So I can [68:22] actually use that one which is nice. And [68:24] now what I actually want to have is a [68:27] subclip of this. Right? So I want it to [68:29] be the same length that this video is. [68:33] So I'm going to go ahead and just call [68:34] this BG music equals to this background [68:38] audio clip. And we'll say subclip. [68:42] And I'm going to go zero and [68:44] clip.duration. [68:47] See, I've been using clip a lot here. [68:49] So, why don't I call this the actual [68:50] video clip so I don't actually get too [68:53] confused as to what's going on here with [68:55] the various clips and sub clips and all [68:58] that. So, now I've got my original video [69:00] clip. It creates an audio path for me. [69:04] And then I have a new audio file, the [69:06] sample, and then we just get a clip of [69:09] that or a sub clip of that. Now, it's I [69:11] think it's safe to assume that the video [69:14] file clip, our source video file, its [69:17] audio clip is going to be the same [69:19] length as our original video clip. So, I [69:22] don't actually have to subclip that one [69:24] either. But, of course, you could if you [69:26] wanted to clip out pieces of it. Um, so [69:29] now that we've got this, what I want to [69:30] do is actually change the volume of this [69:33] background music. Now you can inspect it [69:36] on your own, but the idea is that that [69:38] actual audio is kind of loud. Now maybe [69:41] you hear this or not, but it is kind of [69:43] loud in comparison to the source audio [69:46] or the original audio uh that is coming [69:48] through. So I want to change that [69:50] volume. Now there's a few different ways [69:52] on how to go about doing this. One of [69:55] them is just doing it directly here. So [69:58] I'll say bgusic equals to bgmusic. [70:01] volume [70:02] x and then whatever I want to times the [70:07] original volume or whatever the actual [70:10] volume of that source is times whatever [70:13] this value is. So if it's at 100 then [70:16] this would be 10% of that. Right? So [70:18] this is 10% of that original volume. And [70:21] that actually might be still kind of [70:23] loud. It really just depends on on the [70:25] volume itself. So, one method to inspect [70:28] this is you can actually write this as [70:31] an audio file, too. I'm not going to do [70:33] that. I'm not going to verify it here. [70:35] Uh, but you totally could and [70:37] potentially should to verify that the [70:39] volume is actually changing. Of course, [70:42] there's another method we can use to [70:44] actually change the volume. So, the [70:47] method that we would use is from movie [70:50] pi.audio.fx.all [70:54] all going to import the [70:57] volume [70:59] x method and then instead of calling it [71:03] like this we can use another method [71:06] which is FX and then passing volume in [71:10] here or volume X rather in here and that [71:13] will do the exact same thing or at least [71:15] it should do the same thing. So try [71:18] either one of those methods to see how [71:19] the volume is. Now, keep in mind if you [71:22] have both of these going, it's going to [71:23] change the volume here and then it's [71:25] going to change it again right there, [71:27] which, you know, may or may not be what [71:29] you want. Okay, so now we have two [71:33] different audio clips and then our [71:35] original video clip. So, what I want to [71:37] do is actually combine those two audio [71:39] clips into being my final audio. And [71:42] this is called a composite [71:46] audio clip. Now, it's a composite audio [71:49] clip because it's actually stacking them [71:52] on top of each other. There's another [71:54] one called a concatenated audio or [71:56] concatenated video where it brings them [71:59] together like from end to end. Composite [72:02] layers them on top of one another in the [72:05] order that you specify. So, if I list [72:07] put a pass a list through here, I'm [72:10] going to pass the original audio and [72:12] then the BG music, right? So again, that [72:15] original audio is from this file here. [72:18] So we may or may not want to reload the [72:20] original audio in here. Or we may or may [72:23] want to use like the original audio [72:26] file, actually grab that audio file and [72:29] reload it in as a clip itself to do this [72:33] actual final audio clip. That is a [72:35] method that you might consider trying [72:37] because again, this movie pie is really [72:39] good, but there's some little bugs that [72:41] sometimes just don't work. And I found [72:43] that audio is one of those things that [72:45] has that potential issue. So with this, [72:48] I'm going to go ahead and actually [72:49] create a final audio output. So the OG [72:53] audio path, I'm going to put the final [72:57] audio path. And I'll call this final- [73:00] audio.mpp3. [73:03] This final audio I will write out. So [73:05] final audio [73:10] write audio file just like that. And yet [73:13] another thing that we could verify here [73:15] that the original audio and the [73:17] background music are now combined prior [73:19] to ever even adding it it to a video. Um [73:23] so now that we've got that, all we need [73:25] to do is actually set the new audio to [73:29] our video. Okay. Okay, so we can take [73:31] the original video clip from up here and [73:34] we can set audio or change the audio [73:36] based off of this final audio. So it's [73:40] real simple. We say final [73:42] clip equals to and this is going to be [73:45] video clip set audio [73:49] and in this case I did the final audio [73:52] itself and then we're going to go ahead [73:54] and say final clip.right [73:58] write [74:01] video file. And now we need an output [74:04] video file. So I'm going to right next [74:07] to the final audio path, I'm going to [74:08] call this the final video path [74:13] and final video [74:16] mp4. [74:19] Going down to final video. And there we [74:21] go. [74:23] Okay. So now I'm going to go ahead and [74:27] leave it like this. and we're going to [74:28] run it. I think that there might be an [74:31] error, but let's go ahead and try it [74:33] out. We'll do Python 4_mix_audio.py. [74:37] We hit enter. Okay, so it's going to [74:40] output a number of files. Oh, and I get [74:43] a my first error. Composite audio clip [74:46] has no attribute frames per second. So [74:50] the frames per second in this case is [74:52] going to be related to the original [74:54] audio. So this write audio file um or or [74:58] rather the actual time that we export [75:01] it. We want to do FPS equals to well we [75:05] want to get the original audio. Whatever [75:06] that speed is, that's what we're going [75:08] to want to use. So original audio.fps. [75:11] Not the same as frame per second for a [75:14] video. Uh but that's what we need to [75:15] pass. Just like that. Let's try that [75:18] again. [75:21] Okay, it seems to be out outputting all [75:23] of my audio. Now, let's go into my [75:26] outputs, mixed audio. So, I should see [75:29] final audio here. [75:31] And I actually can tell that the [75:33] background music that I created is now [75:35] working with that new background music. [75:38] Uh, so the original files here, just the [75:41] audio, which is kind of cool to see, [75:42] too. We can parse out the audio from [75:44] original file just like that. Uh, and it [75:47] looks like my video is ready. So, let's [75:48] go ahead and try it. [75:52] And [75:53] I don't have any audio. [75:56] Okay. So, this actually didn't write the [75:59] file correctly. So, I could potentially [76:02] have the correct audio, but I think it's [76:05] because of my encoding. Actually, I know [76:07] that. So, I'll go ahead and say codec [76:10] equals to libx264. [76:14] Now, this should be something that [76:15] FFmpeg has for you. Uh, if it doesn't, [76:18] you might have to install these [76:19] additionally. And of course, let me know [76:21] if that is your case because again using [76:25] audio and video is not all does not [76:28] always play that well together. Then the [76:30] audio codec is going to be AAC. [76:34] So this should actually solve the MP4 [76:36] video file. It might not, but let's go [76:39] ahead and try it out. Of course, it's [76:41] going to make all of those files all [76:43] over again. Um, which you didn't [76:45] necessarily have to do here. But if you [76:48] run into any errors with these codecs, [76:49] then obviously this part would have [76:52] stopped and stopped you in the tracks. [76:53] And please let me know in the comments [76:55] because I definitely want to help you [76:56] solve this problem. And hopefully other [76:58] people can chime in as well if you know [77:00] the answer to that. Uh because these [77:02] codec things, it's just it's just [77:04] another nightmare to worry about. Okay, [77:06] so now that I changed the codecs on the [77:08] output, [77:09] it's all working. So the audio and video [77:11] in my case is definitely working. Um, so [77:14] a couple things to try if you do run [77:17] into errors. One of them being that we [77:19] take this final audio [77:22] and do a new audio set. So I'll say new [77:25] audio equals to the audio [77:29] file clip and then actually grabbing in [77:32] what that final audio path is. And then [77:35] your final clip instead of being based [77:38] off of the instance that we created [77:40] there, but rather being based off of a [77:43] newly loaded in audio. That is one thing [77:47] that you could try for this. Of course, [77:50] there's different codecs if you need. [77:52] Um, but more than likely, if this [77:54] worked, then this should as well with [77:56] the correct codec. And it's all going to [77:58] be dependent on what video file you end [78:01] up using. So MP4 needs these two codecs. [78:04] So, we should be good for you for you to [78:08] be able to do this on your machine as [78:09] well as long as you have FFmpeg [78:12] installed. So, that's mixing audio. Um, [78:15] I realize that it's [78:18] probably just a lot of like writing [78:20] things and not fully understanding [78:22] exactly what's going on, but just keep [78:24] in mind that the composite audio clip, [78:26] that's the thing that brought it [78:28] together. Everything else was stuff that [78:30] we pretty much already covered to some [78:32] degree, except instead of using video, [78:34] now we're using audio. [78:40] Now, what we're going to do is overlay [78:41] text on our video. We're even going to [78:43] make a little text intro with some music [78:46] and then add it to our original input [78:48] file and see what the result is. Now, [78:52] you could use a lot of these methods to [78:55] also put an image somewhere in there [78:58] where it's overlaid of an image, but uh [79:01] we're just going to cover the text [79:02] portion of this. If you need the image [79:04] part, just check the docs for that cuz I [79:07] think once you know how to do the text, [79:08] it's really easy to do the image. Okay, [79:11] so let's go ahead and make a new file [79:12] here. And I'm going to call this five [79:14] overlay [79:17] text [79:19] pie. [79:20] So, in mix audio, I'm actually going to [79:22] copy a lot of these original things all [79:25] the way down to the background audio [79:28] clip. [79:30] Okay. [79:32] So, let's close this down. So, the [79:34] background audio clip, um, I actually am [79:37] not going to leave it that duration. Any [79:39] like the BG music is not actually going [79:41] to be that duration anymore. I'll change [79:44] it to something different. And then this [79:46] final video will be the overlay. [79:50] video and perhaps the overlay audio. I'm [79:54] not actually poss sure if we'll have the [79:56] entire final audio video clip in there [79:59] if we need. All right, first things [80:01] first. Let's go ahead and actually [80:03] create a text video and that's it. [80:07] Nothing else, just a text video. And I'm [80:10] going to go ahead and say duration or [80:12] rather intro [80:15] duration and we'll say 5 seconds. So go [80:19] intro text and this is going to be equal [80:21] to a text clip. Now the text clip itself [80:26] is going to be imported from movie pie [80:28] editor. Make sure you import that if you [80:31] need it. So text clip and I'll just go [80:34] ahead and say hello world exclamation [80:36] mark. We'll add a font size. That's one [80:39] word. We'll say go ahead and say 70. [80:45] We'll add a color of white. [80:48] And then the actual size of it. I'll go [80:50] ahead and say clip size. Now, this is [80:53] not the size of the text itself, but [80:56] rather like the box that the text lives [80:59] in. Okay? So, just keep that in mind. [81:01] So, what this is going to do is going to [81:02] make it right in the center. Now, for [81:04] some reason, if you wanted to change the [81:06] size of the clip, you totally could. [81:08] Just remember that the height and width [81:11] of the original clip is just clip [81:15] size. [81:17] And I'm using clip. I meant to say video [81:19] clip size. Okay. So, this is like as if [81:24] we were drawing a box. The size the same [81:27] size of this video clip. And now we're [81:30] just going to have just a little bit of [81:31] text in it and it's only going to show [81:32] the text. It's not going to show [81:33] anything else. Well, by default, the [81:35] background will be black. So, we don't [81:37] have to really change anything other [81:39] than the color of the text to make sure [81:41] that it can be seen. Okay. So now that [81:44] we've got that, let's go ahead and set [81:46] this duration. So we'll do intro_ext [81:49] set duration. In this case, I can just [81:53] say the intro duration here. I can set [81:56] the duration like the number of seconds [81:59] that I actually want to use. And like [82:01] any other method in movie pie, it [82:03] actually returns back the instance that [82:04] we need. So we grab that. And then we [82:08] can also set the frames per second. It's [82:11] actually a good idea to set the frames [82:12] per second to the frames per second we [82:15] want to use on our final video, which in [82:18] this case is just going to be our video [82:19] clip. So, I'll go ahead and set that [82:22] right here. Okay. [82:26] So, set frames per second. And then [82:28] finally, we're going to go ahead and set [82:30] the position for it. And this is going [82:33] to be the position not for the text [82:36] exactly, but rather for the entire frame [82:39] that's being put on top of the video [82:42] itself. And we want to say this is in [82:44] the center. Uh we'll see what I mean by [82:47] this when we actually make a little [82:49] watermark in the right hand corner or [82:51] like a text watermark in the right hand [82:53] corner a little bit later. Okay. So now [82:55] that we've got that, let's go ahead and [82:56] do intro text. [83:00] Video file. and it's going to go out to [83:04] our we'll just use it our final video [83:06] path so we can see what this looks like. [83:09] Okay, so open up the terminal here and [83:12] Python 5 overlay_ext.py [83:17] and creating a video or it actually [83:20] created the original audio which we [83:22] probably don't need to outport or output [83:25] any longer uh at this point. We could [83:28] probably use the one that's already [83:29] outputed, but in case you changed your [83:31] video or something like that, just just [83:33] get in the habit of output outputting it [83:35] when you need to. Okay, so now let's go [83:37] ahead and take a look at this this video [83:38] itself. I'm going to open up my finder. [83:42] Here's my overlay video. [83:44] And if I open it up, there it is. So [83:47] cool. And uh it's only 5 seconds long, [83:49] like I said, with that duration. It does [83:51] the frames per second, like I said. Uh [83:54] so that's uh that's nice. We have text [83:56] now. Um, so what I want to do is [83:58] actually combine this with my original [84:00] video file. And there's a method that we [84:03] imported by default with this call right [84:05] here called concatenate video clips. So [84:09] what we want to say is our final clip is [84:12] equal to concatenate [84:17] video clips and then the items that I [84:22] want to concatenate. I first want to [84:23] start with the intro text and then I [84:26] want to start with the video clip, the [84:28] original one. So, this is going to add [84:31] them to each other. First, the intro [84:34] text and then the video clip. So, the [84:36] order that you put the list of items in [84:38] there, it just extends the video that [84:40] much longer essentially. So, I could put [84:42] these back and and forth, back to each [84:44] other, back and forth, back and forth. [84:46] uh that is a way to concatenate them [84:48] versus the actual composite video clip [84:51] which we still will do where it stacks [84:53] them. This won't stack them. Okay, so [84:56] now we've got this final video clip. [84:57] Let's actually go ahead and create it [85:01] and we're going to write it out and [85:02] we'll just take a look at what the [85:04] result is. Um so again, I'll run the [85:08] Python overlay text [85:11] and we'll take a look in the finder. [85:14] might take a moment for that to finish. [85:17] So, I actually anticipate that this is [85:19] going to have the same issue that we had [85:20] with the audio mixing the audio. So, [85:24] whenever you want to export your video, [85:26] just make sure you're using the right [85:29] encoding or codeex for both the video [85:32] and the audio. Uh, so in this case, I [85:34] I'm going to reset it. I mean, I might [85:37] still have that audio, but I'm going to [85:38] have to do some stuff to it in a moment. [85:40] Uh, and so now what we have I do not [85:43] have audio. It's not showing up, but I [85:45] do have a much longer clip. It's longer [85:47] by 5 seconds. That's because I have that [85:50] intro. So, concatenate video clips [85:52] works. And so, now I'm going to have [85:53] that go. And I'll run it again. [85:56] But what I want to do is actually add [85:58] some audio to this intro text, right? [86:01] And that audio I want to use is that [86:03] background music, that sub clip there. [86:06] So, I'm actually going to come down here [86:09] and underneath this intro text, I'm [86:11] going to call this intro music now. And [86:15] it's going to still take that background [86:16] audio clip. This time, it's going to go [86:19] to the duration of the intro duration. [86:22] So, I can make sure I put that there. [86:26] And so, I want to actually set the [86:29] actual intro [86:31] as this music, right? So to do this, I'm [86:36] going to go ahead and say intro text [86:39] equals to intro text set audio. And now [86:45] I've got my intro text music. Okay, so [86:48] let's save it. I'm going to verify that [86:50] the audio came through this time. [86:53] And sure enough, it did. So I'm going to [86:55] run it again. Now I'll have audio on my [86:59] intro. While that's exporting, we want [87:02] to think about the next portion of this, [87:05] which is adding text over my video clip [87:08] video. Video clip video. Yes. Um, okay. [87:12] So, this now is going to be, let's call [87:14] it our watermark [87:16] text. And this is going to be again a [87:18] text clip. In this case, I'll just go [87:21] ahead and write CF. The font size, I'll [87:25] go ahead and just say 30. color being [87:29] white again just as my overlay. This [87:32] time I'm gonna say something like align [87:36] equals to east and the size well now the [87:40] size is going to be w as in the width of [87:44] the original video [87:47] and then the height that I designate. [87:49] I'm going to give it the same height as [87:51] my font size. Okay. So now I have the [87:56] watermark text that I want. There's [87:58] still a few more things I need to set. [87:59] So watermark text set frames per second [88:03] being the original clip. So video clip [88:06] or I think I already designated the FPS [88:10] right here. [88:13] Okay. And then the duration. So the [88:16] duration being set duration. And this [88:19] duration should probably be the same as [88:21] the video clip. [88:26] You could also use videoclipip.duration. [88:29] Both of those should work just fine. And [88:31] then now what I want to do is actually [88:33] set the position somewhere different. So [88:37] I'm going to go ahead and say watermark [88:38] text equals to watermark text set [88:42] position. And this time I'm gonna pass [88:45] in a tpple for bottom. [88:49] Okay. [88:51] So this means that it's going to go to [88:53] the bottom of the page. Top and center [88:55] are other options. Um but right now I've [88:59] got this aligned to the east versus the [89:02] west. I don't know why it's not left and [89:04] right, but it's east and west. So we're [89:06] going to align it to the east, which [89:08] should be on the right hand side. And [89:10] then we're setting it equal to this [89:11] size. So, it's going to be the full [89:13] width of the entire, you know, clip, the [89:16] video clip itself, the original video [89:18] clip, but then the height is not going [89:20] to be nearly as high. And hopefully what [89:22] this does is bring it right down to the [89:25] bottom. Okay. So, now that we've got [89:27] this text, let's actually just verify [89:30] the output of our last part, which [89:32] should give us audio to the beginning. [89:35] And sure enough, it does. And it's only [89:37] for 5 seconds, hopefully. And there it [89:40] is. Okay, great. So, that part's [89:42] working. So, back in here, we're going [89:44] to go ahead and now make a composite [89:47] video clip. So, the composite video clip [89:50] stacks the the videos in the way that I [89:52] want them to. Right? So, I want the [89:54] intro text on top of the actual video [89:58] itself. Right? So, I'm going to make a [90:01] composite video clip from the original [90:04] and the watermark text. Okay? So, let's [90:09] go ahead and do this. And I'll say [90:12] CVC as in composite video clip equals to [90:17] composite video clip. And we're going to [90:20] pass in the watermark text. And then our [90:24] video clip. [90:27] And I'm going to set the size on here to [90:29] our video clip size. The original size [90:33] for this one. The duration is going to [90:36] be the same as the video clip as we [90:39] might expect. Set duration. So video uh [90:42] video clip duration [90:45] video clip reader dot duration [90:49] and CVC [90:53] equals to CVC set frames per second [90:57] being yes. Okay. So now I've got my new [91:02] clip here. The problem with this clip is [91:04] it doesn't actually have audio. Right? [91:07] So, this is where you also might run [91:10] into another issue. So, what I'm going [91:12] to say here is I'll say CVC equals to [91:15] CVC's set audio being none. [91:22] Okay. So, now I've got no audio here. [91:26] But let's go ahead and use this instead [91:28] of our final video clip. I'll use this [91:31] composite video clip. And let's take a [91:32] look at what happens here. [91:36] Back in the terminal, I'll press up and [91:38] run it. [91:41] And I used the wrong variable somewhere [91:44] in here. I used CVS right there or CSV [91:48] rather. So used to writing CSV. Okay. So [91:52] now with it running, I'll just give that [91:54] a minute to finish. [91:57] Okay, with that finished, I hear music [92:00] here. [92:02] And then I don't hear music and I [92:04] actually don't see my composite video. [92:06] Okay, so obviously I set audio to being [92:10] none. So that makes sense. The problem [92:12] here is that my watermark text didn't [92:15] actually hold up. So this is one of [92:19] those challenges that comes up from time [92:20] to time. So, we're going to go ahead and [92:22] get rid of my original video clip and [92:25] create a composite video clip from this [92:28] text and add another one. And we'll call [92:32] this the overlay clip. And we're going [92:34] to make another composite video clip. [92:38] And this time, I'm going to take my [92:39] original video. And then I'm going to [92:42] add in my composite video clip. And [92:45] again, size being equal to my video clip [92:49] size. And then my overlay clip, I will [92:53] add in a lot of these same things. So, [92:56] let's go ahead and just copy them. [93:02] Okay. And change all of the CBC's to [93:08] overlay clip. [93:12] And then after that, what I want is to [93:15] actually change the audio again. And [93:17] I'll say set audio. [93:19] This time I'm going to use the audio [93:22] file clip. And I want to grab the [93:25] original music that I had or the [93:27] original audio that I had, which was the [93:29] OG source path or the OG audio path. So [93:33] we bring that down here. Let's go ahead [93:36] and make this a little bit wider so we [93:38] can see it. [93:42] Okay. So, I can just OG audio [93:48] like that. So, now we've got our overlay [93:50] clip here [93:53] and [93:55] let's bring that in. Okay. So, we save [93:57] it and let's run it again. [94:00] So, it's not quite done yet, but one of [94:02] the things that is important here is [94:04] actually the ordering of the composite [94:06] video clip. If you put your top level [94:11] thing first, as in my text, if I put [94:15] that before the main video, it's [94:18] actually going to be gone. Like, we're [94:19] not going to see it. So, the ordering of [94:21] this actually matters. The watermark [94:23] text should be last because it's putting [94:26] the first thing inside of this composite [94:28] clip at the bottom. Um, and then it's [94:31] stacking each every everything after [94:32] that, which kind of makes sense. I mean, [94:34] if you think about concatenate, it [94:36] starts with the intro and then adds to [94:38] it after that. So, this is the base. [94:42] This is like the foundation. This is [94:44] level one, level two, and so on. So, our [94:47] composite video clip in this case is [94:49] correct or it should be. So, if we check [94:51] in our overlay video and just click over [94:54] to where our item is, what I actually [94:57] see here, um, I'm going to go ahead and [95:01] just pause this. I see that there's my [95:03] text off in the corner there. Um, so of [95:06] course I can make it bigger and change [95:08] how that fits and all that. So I [95:10] actually don't need those dual composite [95:12] video clips there. I just needed to [95:15] change the ordering. Um, so that's a [95:17] fairly simple thing, but it's important [95:19] to see that yeah, you can combine [95:21] composite video clips for sure. No big [95:23] deal there. That's one thing. And number [95:25] two, to see the actual ramifications of [95:28] not having these in the correct order. [95:31] Um, so now that we've got that, we [95:32] actually have completed everything that [95:35] I wanted to do here, which was adding a [95:37] watermark to that video. And of course, [95:40] if you wanted to change the size, let's [95:41] go ahead and do that. I'll go ahead and [95:43] just say watermark [95:47] size. And we'll use let's try 60 this [95:50] time. [95:52] And I'll put that instead. [95:56] And then I want to align it on the west [95:58] side or the other side of the video. [96:00] I'll go ahead and run it again. And this [96:03] time, now we only have one composite [96:05] video clip with our primary video clip [96:08] at the base and then all of our text. So [96:11] text by default has an opacity to it. So [96:14] it actually you can see through the text [96:16] to what's underneath it. Uh which is [96:18] really nice because of text. Um [96:21] sometimes you want to overlay text, you [96:24] don't want to have like a black bar [96:25] around it or something like that. So [96:27] that's where coming in with this is [96:29] pretty useful. [96:32] So now with it done, I have my like [96:35] little text over here with CF and it's [96:38] now on the left hand side. Um you can [96:40] also add something called margin around [96:42] any one of these clips. So margin just [96:45] gives it a little bit of extra padding. [96:48] So we could say this margin [96:52] and you could do left or right or left [96:55] equals to two, right equals to two, [96:58] bottom equals to two, top, you know, [97:01] opacity equals to zero. That's another [97:04] thing that you can add in there as well. [97:06] Or you could do even bigger numbers if [97:08] you'd like. So that's pretty cool that [97:11] we have that ability now. Um so as far [97:15] as the overlays are concerned um they [97:18] are definitely useful but just remember [97:20] that the main video that you want to be [97:22] overlaid on needs to be at the bottom of [97:25] this composite video clip. Okay, so that [97:29] actually does it for us with Movie Pie [97:32] and Day 15. Now I actually think this is [97:35] a good time for you to go and check out [97:37] some of their example scripts. They have [97:40] all sorts of really cool things that [97:42] they can do that are a lot more advanced [97:44] than what we've done here. But now that [97:46] you have a basis of it, you can actually [97:48] go in and use some of these scripts and [97:50] have a better understanding of even [97:52] doing it. Now, personally, I think just [97:54] creating thumbnails is enough uh for [97:58] what the purpose of using Movie Pie, [98:01] just thumbnails itself or even doing [98:03] machine learning analysis. Um, and but [98:05] those things kind of go hand in hand. So [98:07] the idea here is that we now have a way [98:10] to generate videos, images, and text all [98:14] through Python. So it's up to you now to [98:16] make this as creative as possible, [98:19] right? So like how do you actually go [98:21] one step further and make just some [98:23] really cool videos automatically without [98:25] really writing all of this code? Like [98:27] can you make that? I think you can. Um, [98:30] so I'm really interested in seeing what [98:32] you guys come up with. Please upload [98:33] them to YouTube and send a link in the [98:35] comments below. I would definitely love [98:37] to check out any cool Python program [98:41] generated videos that you might come up [98:43] with. Thanks so much for watching and [98:45] we'll see you in the next day.