Automate Video Thumbnails with Python
45sShows a practical, time-saving automation for content creators, which is highly shareable.
▶ Play ClipThis tutorial covers automated video processing using the MoviePy library in Python. It demonstrates how to create thumbnails, generate videos from images, create GIFs, mix audio, and overlay text on videos.
MoviePy requires FFmpeg and ImageMagick. Installation instructions for Mac (Homebrew) and Windows are provided.
Use VideoFileClip to load a video, then iterate over frames using clip.iter_frames() or get_frame() to extract images. Save frames as JPEG using Pillow.
Use ImageSequenceClip to create a video from a folder of images. Sort file paths correctly using os.walk and dictionary sorting to maintain order.
Use subclip to extract a portion of a video, resize if needed, then write_gif(). Specify fps and program (ffmpeg recommended for smaller file size).
Extract original audio from video clip, load background music as AudioFileClip, adjust volume, combine using CompositeAudioClip, then set audio on video clip.
Create TextClip with desired text, font size, color, and size. Set duration and fps. Use CompositeVideoClip to overlay text on video. Order matters: base video first, then text.
MoviePy provides a powerful Python interface for automating video editing tasks. With the basics covered, you can create custom video processing pipelines for thumbnails, GIFs, audio mixing, and text overlays.
"Title accurately describes the tutorial content; it's a genuine Python tutorial on video processing with MoviePy."
What two external programs does MoviePy require?
FFmpeg and ImageMagick.
00:50
How do you extract a frame at a specific second from a VideoFileClip?
Use clip.get_frame(seconds) where seconds is an integer.
16:46
What method is used to create a video from a list of image file paths?
ImageSequenceClip.
37:27
How do you combine two audio clips into one?
Use CompositeAudioClip([clip1, clip2]).
71:42
What codec should be used for MP4 video output in MoviePy?
libx264 for video and aac for audio.
76:10
How do you overlay text on a video using MoviePy?
Create a TextClip, then use CompositeVideoClip with the video as base and text on top.
89:44
What is the difference between concatenate_videoclips and CompositeVideoClip?
concatenate_videoclips joins clips end-to-end; CompositeVideoClip stacks them on top of each other.
84:09
MoviePy Dependencies
Explains that MoviePy relies on FFmpeg and ImageMagick for full functionality.
00:50Numpy Array from Frame
Shows that each frame is a numpy array of pixel values, useful for machine learning.
18:35Video from Image Sequence
Demonstrates how to reverse the thumbnail process and create a video from images.
35:48Composite Audio Clip
Key technique for mixing multiple audio tracks into one.
71:42Composite Video Clip Ordering
Highlights that the base video must be first in the list for proper overlay.
89:44[00:00] Hey there. Welcome to day 15. In this
[00:02] one, we're going to be processing video,
[00:05] audio, and images using Python in a
[00:08] package called Movie Pie.
[00:11] [Music]
[00:13] Now, we're going to start off by
[00:15] creating thumbnails from any given
[00:17] image, which is actually really useful
[00:19] because you can automate the process of
[00:21] creating thumbnails for all of your
[00:23] videos, which is also really awesome if
[00:26] you work on, let's say, for instance,
[00:28] YouTube and you want to, you know, use
[00:31] one of the screenshots from a video and
[00:33] you don't want to have to scrub through
[00:35] the the video itself and do a
[00:36] screenshot. That's one of many examples
[00:39] of why you'd want to use Movie Pie. So
[00:42] before we actually get started, what
[00:43] we're going to do is we have to set up
[00:45] our system a little bit more. And the
[00:48] reason for that is because Movie Pi is
[00:50] really supercharged with two other
[00:52] packages. One is called Image Magic and
[00:55] the other one is called FFmpeg. So to
[00:58] actually follow along with our guide on
[01:01] getting this going, you can go to our
[01:04] GitHub, which of course is
[01:05] github.com/coding forrepreneurs. go to
[01:08] the 30 days of Python repo and into day
[01:11] 15 and setup.mmd.
[01:14] This will show you some of the things
[01:15] that we're doing and then the
[01:16] installation requirements ffmpeg and
[01:19] image magic. Now, if you're on Windows,
[01:21] you can go to this timestamp right here.
[01:23] That's going to actually show you how to
[01:25] do all of it. Install ffmpeg and image
[01:27] magic. And then we'll actually all come
[01:29] back and work together. If you're on
[01:31] Linux, well, you know that Linux has a
[01:33] lot of different distributions, so we
[01:35] can't cover installing on every Linux
[01:37] distribution. So, just check out the
[01:39] links that we have there. Really, it's
[01:42] just ffmpeg.org or imagemagic.org. You
[01:45] can go to both of those places and just
[01:46] download the process that you need.
[01:49] Okay, so for us Mac users, we're going
[01:51] to be using Homebrew. Now, if you don't
[01:53] have Homebrew installed, go to brew.sh
[01:55] sh and then copy and paste this command
[01:58] into your terminal so that you can do
[02:00] something like this brew. Now, if you
[02:03] see this, that's a good sign. And what
[02:06] you're just going to want to do is
[02:07] upgrade brew. So, just go ahead and do
[02:10] brew update and maybe even brew upgrade.
[02:13] So, after that, we're just going to run
[02:15] brew install ffmpeg
[02:19] and
[02:21] image magic with a K at the end. We hit
[02:25] enter. And of course, this installation
[02:27] process might take a while depending on
[02:29] your system. But after you do it, we can
[02:31] just type out ffmpeg. And now your
[02:34] entire system can use ffmpeg. We're
[02:36] going to talk about this more in just a
[02:37] moment. Uh but now that we have that,
[02:39] it's really simple. Our movie pie is now
[02:42] ready to actually use it because once
[02:44] it's on your system, movie pie can
[02:46] actually run with it. And now we can
[02:48] finish the rest of the setup process.
[02:50] So, if you're skipping the Windows part,
[02:52] which you probably are if you're on Mac,
[02:54] go ahead and jump to this timestamp.
[03:01] To get the most out of Movie Pie on
[03:03] Windows or really any system, we're
[03:06] going to want to use FFmpeg and Image
[03:08] Magic. Now, in this one, I'm going to
[03:10] show you how to install FFmpeg directly
[03:12] from that or you can use image magic.
[03:15] Just go ahead and download its
[03:16] installation and make sure FFmpeg is
[03:18] checked. I'll show you to do both
[03:20] things, but I want to start with
[03:22] ffmpeg.org
[03:24] and then going into download, clicking
[03:26] on the Windows icon and Windows builds.
[03:29] Notice this is provided by another
[03:31] service, but it is a reliable one as
[03:33] ffmpeg.org references it. So, we're
[03:36] going to go ahead and download this zip
[03:38] file. Once it downloads, you'll see
[03:40] something like this in your downloads
[03:42] folder. I'm going to go ahead and
[03:44] actually rename this to just ffmpeg.
[03:48] And I'm going to go ahead and bring this
[03:49] over into my C drive. So into my C drive
[03:54] and I'll go ahead and hit continue.
[03:58] Now that it's in my C drive, I'm going
[04:00] to go ahead and extract it here. So with
[04:03] sevenzip, I'm going to hit extract here.
[04:07] And the reason for this is this is now
[04:08] where my location for ffmpeg will be. Uh
[04:13] as you see that this directory has now
[04:14] been created. And so I can actually
[04:17] reference this in my environment
[04:19] variables. So all I need to do is add
[04:22] this to my systems path for the bin. So
[04:25] I can actually call the application of
[04:28] ffmpeg. I could certainly move that
[04:31] binary right there anywhere I want to on
[04:33] my system and be able to run it, but
[04:35] it's probably best to just leave it
[04:36] inside of this ffmpeg folder. Um, so I'm
[04:40] actually going to delete this other one
[04:42] that I have in here. If yours was
[04:44] renamed this way, like if you still have
[04:46] this naming convention al together, just
[04:49] rename it to ffmpeg. It's going to save
[04:51] us some time in just a moment. So now
[04:54] what I'm going to do is add this to my
[04:55] path. I'll hit the start menu and type
[04:58] out environment
[05:00] variables. And you're going to want to
[05:01] edit the system environment variables.
[05:05] And then we're going to go ahead and
[05:06] click on environment variables. This
[05:08] should pop up right here. Click on that
[05:10] link. And then with that, we will see
[05:14] this window icon and we're just going to
[05:18] want to edit our system environment
[05:20] variables for path right there and then
[05:25] click on edit.
[05:27] Okay, so in here you just want to make
[05:29] sure that you add this right here. So c
[05:33] ffmpeg/ben
[05:35] and with that you will be able to
[05:37] actually use ffmpeg in command prompt
[05:41] and powershell. Uh so let's go ahead and
[05:43] verify that by just opening up
[05:46] powershell and ffmpeg. I should get
[05:49] something like this. Now the reason of
[05:51] course I renamed it was so that my
[05:54] environment variables was really easy to
[05:56] work with. Right? If I didn't rename it
[05:58] then I would have to have a longer item
[06:00] here for the path. once I added it into
[06:03] those environment variables. Now, that's
[06:05] the manual way of adding FFmpeg. A
[06:07] automated way is using image magic. And
[06:10] of course, we want Image Magic anyway.
[06:12] So, I'm going to go ahead and go to
[06:13] imagemagic.org.
[06:14] Click on download. And then I'm going to
[06:16] scroll down to the Windows binary
[06:19] release. And I want to grab the first
[06:22] one that it has here with the DLL.exe.
[06:26] You can try some of the other ones, but
[06:28] this one I found is the easiest one to
[06:30] work with. So, I hit download on the
[06:32] HTTP.
[06:34] And of course, in my case, I actually
[06:36] already downloaded it. Uh, it's right
[06:38] here. So, I'll go ahead and double click
[06:39] and open that. And we're going to run
[06:41] this. So, uh, naturally, we're going to
[06:44] accept to the agreement. Hit next. We
[06:47] can install it in the default location.
[06:49] That's fine. You can leave it as the
[06:51] default name or you can change it. It's
[06:53] up to you. Notice that in here, this is
[06:56] the option to install FFmpeg. So, we
[06:58] could have saved like two minutes or
[07:00] three um installing ffmpeg by just using
[07:03] image magic. But I wanted to show you
[07:05] that there is the manual way of doing it
[07:07] and that is a valid way. So, as it's
[07:11] installing, I'm actually going to open
[07:12] up my program files. So, back into my C
[07:16] drive into program files and I'm going
[07:19] to look for image magic. I'm assuming
[07:20] it's already done. It looks like it is.
[07:22] And I'm not going to view the
[07:23] index.html.
[07:25] So going into image magic here, if I
[07:28] scroll down a little bit, I will see
[07:30] that binary of FFmpeg. I believe it's
[07:32] the same size as the one we just
[07:34] downloaded. So let's go ahead and just
[07:36] check that out by opening up a file
[07:39] explorer, going into there into our bin,
[07:43] and we've got FFmpeg right here.
[07:48] So there the one that we downloaded
[07:50] directly from ffmpeg is just a little
[07:52] bit smaller than the one that's inside
[07:54] of image magic. Perhaps they add some
[07:56] additional things to it. Uh but now we
[07:59] have ffmpeg installed two times. So we
[08:02] can definitely use ffmpeg on our command
[08:05] line which also means that movie pi will
[08:08] be able to use it as well.
[08:13] Now that we have FFmpeg installed, we
[08:15] can open up terminal or PowerShell and
[08:18] actually type out the command and see
[08:20] all of these different options. Now
[08:22] FFmpeg is powerful in of itself. You
[08:24] don't actually need a whole lot of
[08:26] things beyond it. But customizing it and
[08:28] actually using it, I would argue is not
[08:30] that friendly for a new developer, let
[08:31] alone a Python developer. So we use
[08:34] Movie Pi to do all sorts of things that
[08:36] FFmpeg can do out of the box. One of
[08:38] those things being like changing from an
[08:40] AVI file to an MP4 file. So video format
[08:44] changing. That's not that big of a deal
[08:46] with it. Just FFmpeg or extracting audio
[08:49] from a video. Also not that big of a
[08:52] deal. But we're not going to actually
[08:53] spend a whole lot of time on this. I
[08:55] just wanted to mention it because it is
[08:56] a really cool program and if you want to
[08:58] learn more about it, I recommend that
[09:00] you do. Okay. So with that out of the
[09:02] way, let's go ahead and start our
[09:04] project. I'm going to create a directory
[09:06] called day 15. Now, in my case, I
[09:08] actually have already created this
[09:10] because there's a number of things I
[09:11] want to do prior to actually getting
[09:13] here. So, I made a folder called day 15
[09:15] and I made a pipv, which we'll do in
[09:17] just a moment as well, but I also
[09:19] downloaded a couple files here. I
[09:21] downloaded an audio sample as well as
[09:23] sample.mpp4.
[09:25] So, if you want to actually do exactly
[09:27] what I did, you're going to go back into
[09:29] that repo and you can scroll towards the
[09:32] bottom and look at the base project
[09:34] start. Right? So, we're going to make
[09:36] these directories here. These are where
[09:38] we're going to store some of those
[09:39] files. And then we're going to create
[09:41] conf.py, which I'll do in a moment. And
[09:43] then you can download these audio and
[09:45] video samples. These are things that you
[09:47] can use if you want. They're not that
[09:49] big of a deal. It's just more of um we
[09:51] need an MP3 file and an MP4 file. also a
[09:54] video file and an audio file that we can
[09:56] actually work with. In my case, my video
[09: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
[1:00:00] calling that function itself and then
[1:00:02] calling subclip on it and then the
[1:00:06] width. So this is going to crop it on
[1:00:09] how we want. So I'm going to go ahead
[1:00:10] and say 320 and the height being 320 and
[1:00:15] then the x center being the width
[1:00:19] divided by two and then the y center
[1:00:23] being the height divided by two.
[1:00:25] So this is basically saying, hey, how do
[1:00:27] we center this image out itself? So this
[1:00:30] is going to take the exact center of the
[1:00:32] original clip. It's going to go right in
[1:00:34] the middle of that, which is what this
[1:00:35] divided by two is. The Y center is going
[1:00:38] to take right in the middle of of that
[1:00:39] one as well. So even if it's widescreen
[1:00:41] or it's not already a square, this will
[1:00:43] actually turn it into a square. So you
[1:00:46] can think of this as a square clip
[1:00:50] or square cropped clip, right? And now
[1:00:55] we can do square cropped clip right GIF.
[1:01:00] And this time I'm going to go ahead and
[1:01:02] give a new output path. And we'll give
[1:01:04] this two sample two.
[1:01:09] So right GIF and
[1:01:12] output path frames per second. This time
[1:01:15] I'm going to go ahead and use the
[1:01:16] original frames per second. And then the
[1:01:19] program again being ffmpeg.
[1:01:23] We save that. The other one, the other
[1:01:26] GIF that I create, I'm going to go ahead
[1:01:27] and just comment that one out. Let's
[1:01:30] open the terminal back up. And I
[1:01:32] probably need to reactivate
[1:01:34] my virtual environment. So, pipv shell.
[1:01:38] And then I'll go ahead and run python
[1:01:42] 3 create.py.
[1:01:46] We hit enter. And it's just hanging. So,
[1:01:51] C to cancel it out. Uh, if I scroll
[1:01:53] down, I actually did a spelling error on
[1:01:57] ffmpeg. That must have been the issue
[1:01:59] before as well. So, let's go ahead and
[1:02:02] run this. Now, it's actually running.
[1:02:04] Okay, cool. Um, so let's go ahead and
[1:02:07] take a look at our results. So, in the
[1:02:09] finder window, sample 2.gif,
[1:02:12] um, it is a square image now, and it's
[1:02:15] off of the time that I designated
[1:02:18] before, which of course we could compare
[1:02:20] that to that other sample. uh to to just
[1:02:23] see what that is. And that sample is
[1:02:24] still way too large. So I'm going to go
[1:02:27] ahead and use the resized version.
[1:02:32] And I'm also going to use the other
[1:02:35] frames per second.
[1:02:38] Try that again with sample one.
[1:02:41] And this one, of course, still takes a
[1:02:43] while because I made it still fairly
[1:02:45] large image. Um so let's see what's
[1:02:49] going on with this one.
[1:02:52] There we go. Um, so it should be 500
[1:02:55] pixels wide,
[1:02:57] which we can verify inside of the
[1:03:01] Finder. It shows me that it's 500 pixels
[1:03:03] wide. And this is giving me, you know,
[1:03:05] exactly the same thing that's going on
[1:03:07] here. It's just it's cropped in a
[1:03:09] different way. Okay, so that's how you
[1:03:12] make gifts. Now, how useful is this in
[1:03:15] particular? It's hard to say. I think
[1:03:18] that if you have a crop size that you're
[1:03:20] actually looking for, um, it's probably
[1:03:22] going to be a lot easier of actually
[1:03:25] cropping a part of a video and then
[1:03:28] saving that in particular. All right, so
[1:03:31] I don't know how often you're going to
[1:03:33] be making gifts necessarily, but the
[1:03:35] nice thing to know is that Movie Pie has
[1:03:38] the ability to write gifts on any clip.
[1:03:40] So you just have to write underscore gif
[1:03:42] just like that on any given video clip
[1:03:45] and it will create a GIF for you. And I
[1:03:48] would recommend that you use ffmpeg
[1:03:49] because the size of the gif is going to
[1:03:51] be a lot smaller. Um so that's pretty
[1:03:54] cool. And then the other part of this is
[1:03:56] that if you remember creating a video
[1:03:59] file is just write underscore video file
[1:04:02] on any given video clip as well or an
[1:04:04] image sequence clip. In fact, you can
[1:04:07] try out write GIF on a whole variety of
[1:04:11] different places if you need to. Uh, so
[1:04:14] I'm going to leave these write
[1:04:15] statements out and I'll let you use it
[1:04:17] uh how you see fit. Uh, but that's
[1:04:19] writing gifts.
[1:04:25] So now what we're going to do is add a
[1:04:27] background soundtrack to our video that
[1:04:31] just has spoken narration basically. So,
[1:04:35] what we want to do is verify that our
[1:04:38] inputs have audio and sample. Now, if
[1:04:42] your video doesn't actually have audio
[1:04:44] yet, a lot of the methods here will
[1:04:46] still work. You just skip a step, which
[1:04:48] I'll show you in just a moment. But,
[1:04:50] let's go ahead and start off by creating
[1:04:52] a new file inside of day 15. And this is
[1:04:56] going to be four. And I'll call it mix
[1:04:58] audio.py.
[1:05:00] And I'm going to go ahead and go to
[1:05:02] thumbs.py Pi and just grab some of the
[1:05:05] defaults there. Going to grab the sample
[1:05:08] related items here. And then I'm also
[1:05:11] going to grab the output dur or at least
[1:05:14] some of the base of it. And this one I'm
[1:05:16] going to call this mix audio dur. And
[1:05:20] I'll call this mixed
[1:05:22] audio and ospath dot or rather os.maked
[1:05:29] and mix audio dur exists.
[1:05:33] Okay, being true. Okay, cool.
[1:05:36] So, like we've seen before, we're going
[1:05:38] to go ahead and grab our original clip.
[1:05:40] So, the clip itself, of course, is video
[1:05:44] file clip, and it's going to be the
[1:05:46] source path. I want to go ahead and grab
[1:05:50] the original audio here. So, original
[1:05:54] audio,
[1:05:55] and that's going to be clip.audio.
[1:05:59] And this original audio I'm going to go
[1:06:01] ahead and write an audio file.
[1:06:07] And this of course I need to actually
[1:06:08] set to a a path of its own. Uh and that
[1:06:12] path is going to be similar to this mix
[1:06:14] audio dur but it's actually going to use
[1:06:15] that one. So I'll go ahead and say og
[1:06:18] audio path equals ospath.join
[1:06:22] and this mixed audio directory with
[1:06:26] og.mpp3. MP3.
[1:06:30] Okay. Uh, and this I'm going to just go
[1:06:32] ahead and write that audio file out. So,
[1:06:35] I'm going to go ahead and run this. Now,
[1:06:37] let's just make sure that I can actually
[1:06:39] run it and I can get the correct audio
[1:06:42] coming out. So, mix audio.py
[1:06:46] and I hit enter. It's makes og.mpp3.
[1:06:50] So, in outputs, we got mix audio here.
[1:06:54] Ogmpp3. Let's go ahead and reveal this
[1:06:56] in our Finder or of course if you're on
[1:06:59] Windows, it would be in your file f
[1:07:02] explorer. And in this case, I actually
[1:07:04] have it working. I don't know if you
[1:07:05] could hear it or not, but um I have that
[1:07:08] audio working. I definitely just
[1:07:09] verified that that is the original audio
[1:07:12] clip. Now, the reason I did it this way
[1:07:15] has to do with just the finicky nature
[1:07:19] of Movie Pie. um and what we're trying
[1:07:22] to do here, which is mixing two
[1:07:24] different audio clips into one and then
[1:07:27] bringing it right back to that original
[1:07:28] video clip. Um so that was that was
[1:07:31] something that we have to consider with
[1:07:32] that. Okay, so we have our source video
[1:07:35] path. Now I'm going to add my source
[1:07:38] audio path which I believe I just called
[1:07:42] and here we called it audio.mpp3.
[1:07:46] So this is going to be audio.mpp3.
[1:07:50] Okay. And you could open that up and
[1:07:52] hear what it sounds like. And it's
[1:07:54] essentially just some free music that we
[1:07:57] can use. Um, so what I want to do then
[1:07:59] is grab that as a clip itself. So we're
[1:08:02] going to go ahead and say background
[1:08:04] audio
[1:08:06] clip equals to audio
[1:08:09] file clip and then our audio source
[1:08:13] path.
[1:08:15] So audio file clip is automatically
[1:08:17] imported with this as well. So I can
[1:08:22] actually use that one which is nice. And
[1:08:24] now what I actually want to have is a
[1:08:27] subclip of this. Right? So I want it to
[1:08:29] be the same length that this video is.
[1:08:33] So I'm going to go ahead and just call
[1:08:34] this BG music equals to this background
[1:08:38] audio clip. And we'll say subclip.
[1:08:42] And I'm going to go zero and
[1:08:44] clip.duration.
[1:08:47] See, I've been using clip a lot here.
[1:08:49] So, why don't I call this the actual
[1:08:50] video clip so I don't actually get too
[1:08:53] confused as to what's going on here with
[1:08:55] the various clips and sub clips and all
[1:08:58] that. So, now I've got my original video
[1:09:00] clip. It creates an audio path for me.
[1:09:04] And then I have a new audio file, the
[1:09:06] sample, and then we just get a clip of
[1:09:09] that or a sub clip of that. Now, it's I
[1:09:11] think it's safe to assume that the video
[1:09:14] file clip, our source video file, its
[1:09:17] audio clip is going to be the same
[1:09:19] length as our original video clip. So, I
[1:09:22] don't actually have to subclip that one
[1:09:24] either. But, of course, you could if you
[1:09:26] wanted to clip out pieces of it. Um, so
[1:09:29] now that we've got this, what I want to
[1:09:30] do is actually change the volume of this
[1:09:33] background music. Now you can inspect it
[1:09:36] on your own, but the idea is that that
[1:09:38] actual audio is kind of loud. Now maybe
[1:09:41] you hear this or not, but it is kind of
[1:09:43] loud in comparison to the source audio
[1:09:46] or the original audio uh that is coming
[1:09:48] through. So I want to change that
[1:09:50] volume. Now there's a few different ways
[1:09:52] on how to go about doing this. One of
[1:09:55] them is just doing it directly here. So
[1:09:58] I'll say bgusic equals to bgmusic.
[1:10:01] volume
[1:10:02] x and then whatever I want to times the
[1:10:07] original volume or whatever the actual
[1:10:10] volume of that source is times whatever
[1:10:13] this value is. So if it's at 100 then
[1:10:16] this would be 10% of that. Right? So
[1:10:18] this is 10% of that original volume. And
[1:10:21] that actually might be still kind of
[1:10:23] loud. It really just depends on on the
[1:10:25] volume itself. So, one method to inspect
[1:10:28] this is you can actually write this as
[1:10:31] an audio file, too. I'm not going to do
[1:10:33] that. I'm not going to verify it here.
[1:10:35] Uh, but you totally could and
[1:10:37] potentially should to verify that the
[1:10:39] volume is actually changing. Of course,
[1:10:42] there's another method we can use to
[1:10:44] actually change the volume. So, the
[1:10:47] method that we would use is from movie
[1:10:50] pi.audio.fx.all
[1:10:54] all going to import the
[1:10:57] volume
[1:10:59] x method and then instead of calling it
[1:11:03] like this we can use another method
[1:11:06] which is FX and then passing volume in
[1:11:10] here or volume X rather in here and that
[1:11:13] will do the exact same thing or at least
[1:11:15] it should do the same thing. So try
[1:11:18] either one of those methods to see how
[1:11:19] the volume is. Now, keep in mind if you
[1:11:22] have both of these going, it's going to
[1:11:23] change the volume here and then it's
[1:11:25] going to change it again right there,
[1:11:27] which, you know, may or may not be what
[1:11:29] you want. Okay, so now we have two
[1:11:33] different audio clips and then our
[1:11:35] original video clip. So, what I want to
[1:11:37] do is actually combine those two audio
[1:11:39] clips into being my final audio. And
[1:11:42] this is called a composite
[1:11:46] audio clip. Now, it's a composite audio
[1:11:49] clip because it's actually stacking them
[1:11:52] on top of each other. There's another
[1:11:54] one called a concatenated audio or
[1:11:56] concatenated video where it brings them
[1:11:59] together like from end to end. Composite
[1:12:02] layers them on top of one another in the
[1:12:05] order that you specify. So, if I list
[1:12:07] put a pass a list through here, I'm
[1:12:10] going to pass the original audio and
[1:12:12] then the BG music, right? So again, that
[1:12:15] original audio is from this file here.
[1:12:18] So we may or may not want to reload the
[1:12:20] original audio in here. Or we may or may
[1:12:23] want to use like the original audio
[1:12:26] file, actually grab that audio file and
[1:12:29] reload it in as a clip itself to do this
[1:12:33] actual final audio clip. That is a
[1:12:35] method that you might consider trying
[1:12:37] because again, this movie pie is really
[1:12:39] good, but there's some little bugs that
[1:12:41] sometimes just don't work. And I found
[1:12:43] that audio is one of those things that
[1:12:45] has that potential issue. So with this,
[1:12:48] I'm going to go ahead and actually
[1:12:49] create a final audio output. So the OG
[1:12:53] audio path, I'm going to put the final
[1:12:57] audio path. And I'll call this final-
[1:13:00] audio.mpp3.
[1:13:03] This final audio I will write out. So
[1:13:05] final audio
[1:13:10] write audio file just like that. And yet
[1:13:13] another thing that we could verify here
[1:13:15] that the original audio and the
[1:13:17] background music are now combined prior
[1:13:19] to ever even adding it it to a video. Um
[1:13:23] so now that we've got that, all we need
[1:13:25] to do is actually set the new audio to
[1:13:29] our video. Okay. Okay, so we can take
[1:13:31] the original video clip from up here and
[1:13:34] we can set audio or change the audio
[1:13:36] based off of this final audio. So it's
[1:13:40] real simple. We say final
[1:13:42] clip equals to and this is going to be
[1:13:45] video clip set audio
[1:13:49] and in this case I did the final audio
[1:13:52] itself and then we're going to go ahead
[1:13:54] and say final clip.right
[1:13:58] write
[1:14:01] video file. And now we need an output
[1:14:04] video file. So I'm going to right next
[1:14:07] to the final audio path, I'm going to
[1:14:08] call this the final video path
[1:14:13] and final video
[1:14:16] mp4.
[1:14:19] Going down to final video. And there we
[1:14:21] go.
[1:14:23] Okay. So now I'm going to go ahead and
[1:14:27] leave it like this. and we're going to
[1:14:28] run it. I think that there might be an
[1:14:31] error, but let's go ahead and try it
[1:14:33] out. We'll do Python 4_mix_audio.py.
[1:14:37] We hit enter. Okay, so it's going to
[1:14:40] output a number of files. Oh, and I get
[1:14:43] a my first error. Composite audio clip
[1:14:46] has no attribute frames per second. So
[1:14:50] the frames per second in this case is
[1:14:52] going to be related to the original
[1:14:54] audio. So this write audio file um or or
[1:14:58] rather the actual time that we export
[1:15:01] it. We want to do FPS equals to well we
[1:15:05] want to get the original audio. Whatever
[1:15:06] that speed is, that's what we're going
[1:15:08] to want to use. So original audio.fps.
[1:15:11] Not the same as frame per second for a
[1:15:14] video. Uh but that's what we need to
[1:15:15] pass. Just like that. Let's try that
[1:15:18] again.
[1:15:21] Okay, it seems to be out outputting all
[1:15:23] of my audio. Now, let's go into my
[1:15:26] outputs, mixed audio. So, I should see
[1:15:29] final audio here.
[1:15:31] And I actually can tell that the
[1:15:33] background music that I created is now
[1:15:35] working with that new background music.
[1:15:38] Uh, so the original files here, just the
[1:15:41] audio, which is kind of cool to see,
[1:15:42] too. We can parse out the audio from
[1:15:44] original file just like that. Uh, and it
[1:15:47] looks like my video is ready. So, let's
[1:15:48] go ahead and try it.
[1:15:52] And
[1:15:53] I don't have any audio.
[1:15:56] Okay. So, this actually didn't write the
[1:15:59] file correctly. So, I could potentially
[1:16:02] have the correct audio, but I think it's
[1:16:05] because of my encoding. Actually, I know
[1:16:07] that. So, I'll go ahead and say codec
[1:16:10] equals to libx264.
[1:16:14] Now, this should be something that
[1:16:15] FFmpeg has for you. Uh, if it doesn't,
[1:16:18] you might have to install these
[1:16:19] additionally. And of course, let me know
[1:16:21] if that is your case because again using
[1:16:25] audio and video is not all does not
[1:16:28] always play that well together. Then the
[1:16:30] audio codec is going to be AAC.
[1:16:34] So this should actually solve the MP4
[1:16:36] video file. It might not, but let's go
[1:16:39] ahead and try it out. Of course, it's
[1:16:41] going to make all of those files all
[1:16:43] over again. Um, which you didn't
[1:16:45] necessarily have to do here. But if you
[1:16:48] run into any errors with these codecs,
[1:16:49] then obviously this part would have
[1:16:52] stopped and stopped you in the tracks.
[1:16:53] And please let me know in the comments
[1:16:55] because I definitely want to help you
[1:16:56] solve this problem. And hopefully other
[1:16:58] people can chime in as well if you know
[1:17:00] the answer to that. Uh because these
[1:17:02] codec things, it's just it's just
[1:17:04] another nightmare to worry about. Okay,
[1:17:06] so now that I changed the codecs on the
[1:17:08] output,
[1:17:09] it's all working. So the audio and video
[1:17:11] in my case is definitely working. Um, so
[1:17:14] a couple things to try if you do run
[1:17:17] into errors. One of them being that we
[1:17:19] take this final audio
[1:17:22] and do a new audio set. So I'll say new
[1:17:25] audio equals to the audio
[1:17:29] file clip and then actually grabbing in
[1:17:32] what that final audio path is. And then
[1:17:35] your final clip instead of being based
[1:17:38] off of the instance that we created
[1:17:40] there, but rather being based off of a
[1:17:43] newly loaded in audio. That is one thing
[1:17:47] that you could try for this. Of course,
[1:17:50] there's different codecs if you need.
[1:17:52] Um, but more than likely, if this
[1:17:54] worked, then this should as well with
[1:17:56] the correct codec. And it's all going to
[1:17:58] be dependent on what video file you end
[1:18:01] up using. So MP4 needs these two codecs.
[1:18:04] So, we should be good for you for you to
[1:18:08] be able to do this on your machine as
[1:18:09] well as long as you have FFmpeg
[1:18:12] installed. So, that's mixing audio. Um,
[1:18:15] I realize that it's
[1:18:18] probably just a lot of like writing
[1:18:20] things and not fully understanding
[1:18:22] exactly what's going on, but just keep
[1:18:24] in mind that the composite audio clip,
[1:18:26] that's the thing that brought it
[1:18:28] together. Everything else was stuff that
[1:18:30] we pretty much already covered to some
[1:18:32] degree, except instead of using video,
[1:18:34] now we're using audio.
[1:18:40] Now, what we're going to do is overlay
[1:18:41] text on our video. We're even going to
[1:18:43] make a little text intro with some music
[1:18:46] and then add it to our original input
[1:18:48] file and see what the result is. Now,
[1:18:52] you could use a lot of these methods to
[1:18:55] also put an image somewhere in there
[1:18:58] where it's overlaid of an image, but uh
[1:19:01] we're just going to cover the text
[1:19:02] portion of this. If you need the image
[1:19:04] part, just check the docs for that cuz I
[1:19:07] think once you know how to do the text,
[1:19:08] it's really easy to do the image. Okay,
[1:19:11] so let's go ahead and make a new file
[1:19:12] here. And I'm going to call this five
[1:19:14] overlay
[1:19:17] text
[1:19:19] pie.
[1:19:20] So, in mix audio, I'm actually going to
[1:19:22] copy a lot of these original things all
[1:19:25] the way down to the background audio
[1:19:28] clip.
[1:19:30] Okay.
[1:19:32] So, let's close this down. So, the
[1:19:34] background audio clip, um, I actually am
[1:19:37] not going to leave it that duration. Any
[1:19:39] like the BG music is not actually going
[1:19:41] to be that duration anymore. I'll change
[1:19:44] it to something different. And then this
[1:19:46] final video will be the overlay.
[1:19:50] video and perhaps the overlay audio. I'm
[1:19:54] not actually poss sure if we'll have the
[1:19:56] entire final audio video clip in there
[1:19:59] if we need. All right, first things
[1:20:01] first. Let's go ahead and actually
[1:20:03] create a text video and that's it.
[1:20:07] Nothing else, just a text video. And I'm
[1:20:10] going to go ahead and say duration or
[1:20:12] rather intro
[1:20:15] duration and we'll say 5 seconds. So go
[1:20:19] intro text and this is going to be equal
[1:20:21] to a text clip. Now the text clip itself
[1:20:26] is going to be imported from movie pie
[1:20:28] editor. Make sure you import that if you
[1:20:31] need it. So text clip and I'll just go
[1:20:34] ahead and say hello world exclamation
[1:20:36] mark. We'll add a font size. That's one
[1:20:39] word. We'll say go ahead and say 70.
[1:20:45] We'll add a color of white.
[1:20:48] And then the actual size of it. I'll go
[1:20:50] ahead and say clip size. Now, this is
[1:20:53] not the size of the text itself, but
[1:20:56] rather like the box that the text lives
[1:20:59] in. Okay? So, just keep that in mind.
[1:21:01] So, what this is going to do is going to
[1:21:02] make it right in the center. Now, for
[1:21:04] some reason, if you wanted to change the
[1:21:06] size of the clip, you totally could.
[1:21:08] Just remember that the height and width
[1:21:11] of the original clip is just clip
[1:21:15] size.
[1:21:17] And I'm using clip. I meant to say video
[1:21:19] clip size. Okay. So, this is like as if
[1:21:24] we were drawing a box. The size the same
[1:21:27] size of this video clip. And now we're
[1:21:30] just going to have just a little bit of
[1:21:31] text in it and it's only going to show
[1:21:32] the text. It's not going to show
[1:21:33] anything else. Well, by default, the
[1:21:35] background will be black. So, we don't
[1:21:37] have to really change anything other
[1:21:39] than the color of the text to make sure
[1:21:41] that it can be seen. Okay. So now that
[1:21:44] we've got that, let's go ahead and set
[1:21:46] this duration. So we'll do intro_ext
[1:21:49] set duration. In this case, I can just
[1:21:53] say the intro duration here. I can set
[1:21:56] the duration like the number of seconds
[1:21:59] that I actually want to use. And like
[1:22:01] any other method in movie pie, it
[1:22:03] actually returns back the instance that
[1:22:04] we need. So we grab that. And then we
[1:22:08] can also set the frames per second. It's
[1:22:11] actually a good idea to set the frames
[1:22:12] per second to the frames per second we
[1:22:15] want to use on our final video, which in
[1:22:18] this case is just going to be our video
[1:22:19] clip. So, I'll go ahead and set that
[1:22:22] right here. Okay.
[1:22:26] So, set frames per second. And then
[1:22:28] finally, we're going to go ahead and set
[1:22:30] the position for it. And this is going
[1:22:33] to be the position not for the text
[1:22:36] exactly, but rather for the entire frame
[1:22:39] that's being put on top of the video
[1:22:42] itself. And we want to say this is in
[1:22:44] the center. Uh we'll see what I mean by
[1:22:47] this when we actually make a little
[1:22:49] watermark in the right hand corner or
[1:22:51] like a text watermark in the right hand
[1:22:53] corner a little bit later. Okay. So now
[1:22:55] that we've got that, let's go ahead and
[1:22:56] do intro text.
[1:23:00] Video file. and it's going to go out to
[1:23:04] our we'll just use it our final video
[1:23:06] path so we can see what this looks like.
[1:23:09] Okay, so open up the terminal here and
[1:23:12] Python 5 overlay_ext.py
[1:23:17] and creating a video or it actually
[1:23:20] created the original audio which we
[1:23:22] probably don't need to outport or output
[1:23:25] any longer uh at this point. We could
[1:23:28] probably use the one that's already
[1:23:29] outputed, but in case you changed your
[1:23:31] video or something like that, just just
[1:23:33] get in the habit of output outputting it
[1:23:35] when you need to. Okay, so now let's go
[1:23:37] ahead and take a look at this this video
[1:23:38] itself. I'm going to open up my finder.
[1:23:42] Here's my overlay video.
[1:23:44] And if I open it up, there it is. So
[1:23:47] cool. And uh it's only 5 seconds long,
[1:23:49] like I said, with that duration. It does
[1:23:51] the frames per second, like I said. Uh
[1:23:54] so that's uh that's nice. We have text
[1:23:56] now. Um, so what I want to do is
[1:23:58] actually combine this with my original
[1:24:00] video file. And there's a method that we
[1:24:03] imported by default with this call right
[1:24:05] here called concatenate video clips. So
[1:24:09] what we want to say is our final clip is
[1:24:12] equal to concatenate
[1:24:17] video clips and then the items that I
[1:24:22] want to concatenate. I first want to
[1:24:23] start with the intro text and then I
[1:24:26] want to start with the video clip, the
[1:24:28] original one. So, this is going to add
[1:24:31] them to each other. First, the intro
[1:24:34] text and then the video clip. So, the
[1:24:36] order that you put the list of items in
[1:24:38] there, it just extends the video that
[1:24:40] much longer essentially. So, I could put
[1:24:42] these back and and forth, back to each
[1:24:44] other, back and forth, back and forth.
[1:24:46] uh that is a way to concatenate them
[1:24:48] versus the actual composite video clip
[1:24:51] which we still will do where it stacks
[1:24:53] them. This won't stack them. Okay, so
[1:24:56] now we've got this final video clip.
[1:24:57] Let's actually go ahead and create it
[1:25:01] and we're going to write it out and
[1:25:02] we'll just take a look at what the
[1:25:04] result is. Um so again, I'll run the
[1:25:08] Python overlay text
[1:25:11] and we'll take a look in the finder.
[1:25:14] might take a moment for that to finish.
[1:25:17] So, I actually anticipate that this is
[1:25:19] going to have the same issue that we had
[1:25:20] with the audio mixing the audio. So,
[1:25:24] whenever you want to export your video,
[1:25:26] just make sure you're using the right
[1:25:29] encoding or codeex for both the video
[1:25:32] and the audio. Uh, so in this case, I
[1:25:34] I'm going to reset it. I mean, I might
[1:25:37] still have that audio, but I'm going to
[1:25:38] have to do some stuff to it in a moment.
[1:25:40] Uh, and so now what we have I do not
[1:25:43] have audio. It's not showing up, but I
[1:25:45] do have a much longer clip. It's longer
[1:25:47] by 5 seconds. That's because I have that
[1:25:50] intro. So, concatenate video clips
[1:25:52] works. And so, now I'm going to have
[1:25:53] that go. And I'll run it again.
[1:25:56] But what I want to do is actually add
[1:25:58] some audio to this intro text, right?
[1:26:01] And that audio I want to use is that
[1:26:03] background music, that sub clip there.
[1:26:06] So, I'm actually going to come down here
[1:26:09] and underneath this intro text, I'm
[1:26:11] going to call this intro music now. And
[1:26:15] it's going to still take that background
[1:26:16] audio clip. This time, it's going to go
[1:26:19] to the duration of the intro duration.
[1:26:22] So, I can make sure I put that there.
[1:26:26] And so, I want to actually set the
[1:26:29] actual intro
[1:26:31] as this music, right? So to do this, I'm
[1:26:36] going to go ahead and say intro text
[1:26:39] equals to intro text set audio. And now
[1:26:45] I've got my intro text music. Okay, so
[1:26:48] let's save it. I'm going to verify that
[1:26:50] the audio came through this time.
[1:26:53] And sure enough, it did. So I'm going to
[1:26:55] run it again. Now I'll have audio on my
[1:26:59] intro. While that's exporting, we want
[1:27:02] to think about the next portion of this,
[1:27:05] which is adding text over my video clip
[1:27:08] video. Video clip video. Yes. Um, okay.
[1:27:12] So, this now is going to be, let's call
[1:27:14] it our watermark
[1:27:16] text. And this is going to be again a
[1:27:18] text clip. In this case, I'll just go
[1:27:21] ahead and write CF. The font size, I'll
[1:27:25] go ahead and just say 30. color being
[1:27:29] white again just as my overlay. This
[1:27:32] time I'm gonna say something like align
[1:27:36] equals to east and the size well now the
[1:27:40] size is going to be w as in the width of
[1:27:44] the original video
[1:27:47] and then the height that I designate.
[1:27:49] I'm going to give it the same height as
[1:27:51] my font size. Okay. So now I have the
[1:27:56] watermark text that I want. There's
[1:27:58] still a few more things I need to set.
[1:27:59] So watermark text set frames per second
[1:28:03] being the original clip. So video clip
[1:28:06] or I think I already designated the FPS
[1:28:10] right here.
[1:28:13] Okay. And then the duration. So the
[1:28:16] duration being set duration. And this
[1:28:19] duration should probably be the same as
[1:28:21] the video clip.
[1:28:26] You could also use videoclipip.duration.
[1:28:29] Both of those should work just fine. And
[1:28:31] then now what I want to do is actually
[1:28:33] set the position somewhere different. So
[1:28:37] I'm going to go ahead and say watermark
[1:28:38] text equals to watermark text set
[1:28:42] position. And this time I'm gonna pass
[1:28:45] in a tpple for bottom.
[1:28:49] Okay.
[1:28:51] So this means that it's going to go to
[1:28:53] the bottom of the page. Top and center
[1:28:55] are other options. Um but right now I've
[1:28:59] got this aligned to the east versus the
[1:29:02] west. I don't know why it's not left and
[1:29:04] right, but it's east and west. So we're
[1:29:06] going to align it to the east, which
[1:29:08] should be on the right hand side. And
[1:29:10] then we're setting it equal to this
[1:29:11] size. So, it's going to be the full
[1:29:13] width of the entire, you know, clip, the
[1:29:16] video clip itself, the original video
[1:29:18] clip, but then the height is not going
[1:29:20] to be nearly as high. And hopefully what
[1:29:22] this does is bring it right down to the
[1:29:25] bottom. Okay. So, now that we've got
[1:29:27] this text, let's actually just verify
[1:29:30] the output of our last part, which
[1:29:32] should give us audio to the beginning.
[1:29:35] And sure enough, it does. And it's only
[1:29:37] for 5 seconds, hopefully. And there it
[1:29:40] is. Okay, great. So, that part's
[1:29:42] working. So, back in here, we're going
[1:29:44] to go ahead and now make a composite
[1:29:47] video clip. So, the composite video clip
[1:29:50] stacks the the videos in the way that I
[1:29:52] want them to. Right? So, I want the
[1:29:54] intro text on top of the actual video
[1:29:58] itself. Right? So, I'm going to make a
[1:30:01] composite video clip from the original
[1:30:04] and the watermark text. Okay? So, let's
[1:30:09] go ahead and do this. And I'll say
[1:30:12] CVC as in composite video clip equals to
[1:30:17] composite video clip. And we're going to
[1:30:20] pass in the watermark text. And then our
[1:30:24] video clip.
[1:30:27] And I'm going to set the size on here to
[1:30:29] our video clip size. The original size
[1:30:33] for this one. The duration is going to
[1:30:36] be the same as the video clip as we
[1:30:39] might expect. Set duration. So video uh
[1:30:42] video clip duration
[1:30:45] video clip reader dot duration
[1:30:49] and CVC
[1:30:53] equals to CVC set frames per second
[1:30:57] being yes. Okay. So now I've got my new
[1:31:02] clip here. The problem with this clip is
[1:31:04] it doesn't actually have audio. Right?
[1:31:07] So, this is where you also might run
[1:31:10] into another issue. So, what I'm going
[1:31:12] to say here is I'll say CVC equals to
[1:31:15] CVC's set audio being none.
[1:31:22] Okay. So, now I've got no audio here.
[1:31:26] But let's go ahead and use this instead
[1:31:28] of our final video clip. I'll use this
[1:31:31] composite video clip. And let's take a
[1:31:32] look at what happens here.
[1:31:36] Back in the terminal, I'll press up and
[1:31:38] run it.
[1:31:41] And I used the wrong variable somewhere
[1:31:44] in here. I used CVS right there or CSV
[1:31:48] rather. So used to writing CSV. Okay. So
[1:31:52] now with it running, I'll just give that
[1:31:54] a minute to finish.
[1:31:57] Okay, with that finished, I hear music
[1:32:00] here.
[1:32:02] And then I don't hear music and I
[1:32:04] actually don't see my composite video.
[1:32:06] Okay, so obviously I set audio to being
[1:32:10] none. So that makes sense. The problem
[1:32:12] here is that my watermark text didn't
[1:32:15] actually hold up. So this is one of
[1:32:19] those challenges that comes up from time
[1:32:20] to time. So, we're going to go ahead and
[1:32:22] get rid of my original video clip and
[1:32:25] create a composite video clip from this
[1:32:28] text and add another one. And we'll call
[1:32:32] this the overlay clip. And we're going
[1:32:34] to make another composite video clip.
[1:32:38] And this time, I'm going to take my
[1:32:39] original video. And then I'm going to
[1:32:42] add in my composite video clip. And
[1:32:45] again, size being equal to my video clip
[1:32:49] size. And then my overlay clip, I will
[1:32:53] add in a lot of these same things. So,
[1:32:56] let's go ahead and just copy them.
[1:33:02] Okay. And change all of the CBC's to
[1:33:08] overlay clip.
[1:33:12] And then after that, what I want is to
[1:33:15] actually change the audio again. And
[1:33:17] I'll say set audio.
[1:33:19] This time I'm going to use the audio
[1:33:22] file clip. And I want to grab the
[1:33:25] original music that I had or the
[1:33:27] original audio that I had, which was the
[1:33:29] OG source path or the OG audio path. So
[1:33:33] we bring that down here. Let's go ahead
[1:33:36] and make this a little bit wider so we
[1:33:38] can see it.
[1:33:42] Okay. So, I can just OG audio
[1:33:48] like that. So, now we've got our overlay
[1:33:50] clip here
[1:33:53] and
[1:33:55] let's bring that in. Okay. So, we save
[1:33:57] it and let's run it again.
[1:34:00] So, it's not quite done yet, but one of
[1:34:02] the things that is important here is
[1:34:04] actually the ordering of the composite
[1:34:06] video clip. If you put your top level
[1:34:11] thing first, as in my text, if I put
[1:34:15] that before the main video, it's
[1:34:18] actually going to be gone. Like, we're
[1:34:19] not going to see it. So, the ordering of
[1:34:21] this actually matters. The watermark
[1:34:23] text should be last because it's putting
[1:34:26] the first thing inside of this composite
[1:34:28] clip at the bottom. Um, and then it's
[1:34:31] stacking each every everything after
[1:34:32] that, which kind of makes sense. I mean,
[1:34:34] if you think about concatenate, it
[1:34:36] starts with the intro and then adds to
[1:34:38] it after that. So, this is the base.
[1:34:42] This is like the foundation. This is
[1:34:44] level one, level two, and so on. So, our
[1:34:47] composite video clip in this case is
[1:34:49] correct or it should be. So, if we check
[1:34:51] in our overlay video and just click over
[1:34:54] to where our item is, what I actually
[1:34:57] see here, um, I'm going to go ahead and
[1:35:01] just pause this. I see that there's my
[1:35:03] text off in the corner there. Um, so of
[1:35:06] course I can make it bigger and change
[1:35:08] how that fits and all that. So I
[1:35:10] actually don't need those dual composite
[1:35:12] video clips there. I just needed to
[1:35:15] change the ordering. Um, so that's a
[1:35:17] fairly simple thing, but it's important
[1:35:19] to see that yeah, you can combine
[1:35:21] composite video clips for sure. No big
[1:35:23] deal there. That's one thing. And number
[1:35:25] two, to see the actual ramifications of
[1:35:28] not having these in the correct order.
[1:35:31] Um, so now that we've got that, we
[1:35:32] actually have completed everything that
[1:35:35] I wanted to do here, which was adding a
[1:35:37] watermark to that video. And of course,
[1:35:40] if you wanted to change the size, let's
[1:35:41] go ahead and do that. I'll go ahead and
[1:35:43] just say watermark
[1:35:47] size. And we'll use let's try 60 this
[1:35:50] time.
[1:35:52] And I'll put that instead.
[1:35:56] And then I want to align it on the west
[1:35:58] side or the other side of the video.
[1:36:00] I'll go ahead and run it again. And this
[1:36:03] time, now we only have one composite
[1:36:05] video clip with our primary video clip
[1:36:08] at the base and then all of our text. So
[1:36:11] text by default has an opacity to it. So
[1:36:14] it actually you can see through the text
[1:36:16] to what's underneath it. Uh which is
[1:36:18] really nice because of text. Um
[1:36:21] sometimes you want to overlay text, you
[1:36:24] don't want to have like a black bar
[1:36:25] around it or something like that. So
[1:36:27] that's where coming in with this is
[1:36:29] pretty useful.
[1:36:32] So now with it done, I have my like
[1:36:35] little text over here with CF and it's
[1:36:38] now on the left hand side. Um you can
[1:36:40] also add something called margin around
[1:36:42] any one of these clips. So margin just
[1:36:45] gives it a little bit of extra padding.
[1:36:48] So we could say this margin
[1:36:52] and you could do left or right or left
[1:36:55] equals to two, right equals to two,
[1:36:58] bottom equals to two, top, you know,
[1:37:01] opacity equals to zero. That's another
[1:37:04] thing that you can add in there as well.
[1:37:06] Or you could do even bigger numbers if
[1:37:08] you'd like. So that's pretty cool that
[1:37:11] we have that ability now. Um so as far
[1:37:15] as the overlays are concerned um they
[1:37:18] are definitely useful but just remember
[1:37:20] that the main video that you want to be
[1:37:22] overlaid on needs to be at the bottom of
[1:37:25] this composite video clip. Okay, so that
[1:37:29] actually does it for us with Movie Pie
[1:37:32] and Day 15. Now I actually think this is
[1:37:35] a good time for you to go and check out
[1:37:37] some of their example scripts. They have
[1:37:40] all sorts of really cool things that
[1:37:42] they can do that are a lot more advanced
[1:37:44] than what we've done here. But now that
[1:37:46] you have a basis of it, you can actually
[1:37:48] go in and use some of these scripts and
[1:37:50] have a better understanding of even
[1:37:52] doing it. Now, personally, I think just
[1:37:54] creating thumbnails is enough uh for
[1:37:58] what the purpose of using Movie Pie,
[1:38:01] just thumbnails itself or even doing
[1:38:03] machine learning analysis. Um, and but
[1:38:05] those things kind of go hand in hand. So
[1:38:07] the idea here is that we now have a way
[1:38:10] to generate videos, images, and text all
[1:38:14] through Python. So it's up to you now to
[1:38:16] make this as creative as possible,
[1:38:19] right? So like how do you actually go
[1:38:21] one step further and make just some
[1:38:23] really cool videos automatically without
[1:38:25] really writing all of this code? Like
[1:38:27] can you make that? I think you can. Um,
[1:38:30] so I'm really interested in seeing what
[1:38:32] you guys come up with. Please upload
[1:38:33] them to YouTube and send a link in the
[1:38:35] comments below. I would definitely love
[1:38:37] to check out any cool Python program
[1:38:41] generated videos that you might come up
[1:38:43] with. Thanks so much for watching and
[1:38:45] we'll see you in the next day.
⚡ Saved you time reading this? Transcribe any YouTube video for free — no signup needed.