How would you like to download a YouTube video within a Java application? If so, then read on.

Fortunately, it’s not that complicated. You’ll need the aid of a library, though.

Also, there are some caveats. For starters, it looks like this code downloads video and audio separately on older YouTube videos.

Additionally, you might have trouble downloading really long videos. That could be due to network issues, though.

Still, if you’re looking to snag a quick video so that you can edit it and include a snippet of it in your own brilliant production, this solution should fit the bill.

As always, you can check out the complete source code on GitHub.

 

Get VGet

First things first: you need to get VGet.

What is VGet? It’s a YouTube download library based on WGet.

What is WGet? It’s a Java download library.

VGet will enable you to rip a YouTube video with nothing but the URL. Even better: it allows you to do that with just a few lines of code.

To get started, create a new Maven project in Eclipse (or your IDE of choice) and add the following stanza to your POM file:

      <dependency>
	       <groupId>com.github.axet</groupId>
	       <artifactId>vget</artifactId>
	       <version>1.2.6</version>
      </dependency>

 

Once you’ve got VGet, you’re ready to start coding.

 

Direct Download

There are a couple of options when it comes to downloading YouTube videos with VGet. You can use the direct download method or the managed download method.

Let’s take a look at the direct download method first. It’s the simplest.

Here are the few lines of code you need for the direct download method:

    public static void main(String[] args) {
        try {
            String url = "https://www.youtube.com/watch?v=5G5fzf7dyhU";
            String path = "/test/";
            VGet v = new VGet(new URL(url), new File(path));
            v.download();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

 

Not too complicated at all is it?

As you can see, the first line of code in the try block simply specifies a YouTube URL. In this case, it’s a brief news report. Feel free to use the same URL or specify your own video link.

The next line specifies the path where you want to save the downloaded file. Here, it’s going to put the file in the /test directory. You can change that as well.

Next, the code instantiates VGet. That’s the workhorse class that will handle the download process.

Finally, the code invokes the download() method on the VGet object.

Because this is a direct download, you’ll have to wait while the download finishes with no status updates along the way. Code execution is also blocked during the download process unless you run it in a separate thread.

When it’s all over, though, you should see the MP4 file on your hard drive.

savedmp4

Run that code to make sure the download works on your system. Then, come on back and take a look at the managed download option.

 

Managed Download

Sometimes, you need a little more than a direct download. You need a process that tracks the download and gives you a running status of what’s happening.

Enter the managed download solution. It accomplishes the same thing as the direct download code but with more information along the way.

In a nutshell, it uses the console to tell you “where you’re at” while the download as it’s happening. Think of it as a low-tech progress bar.

You won’t need to import any additional libraries to implement a managed download solution. VGet has you covered.

Start by creating an inner class that runs in a separate thread.

    static class VGetStatus implements Runnable {
        VideoInfo videoinfo;
        long last;

        Map<VideoFileInfo, SpeedInfo> map = new HashMap<VideoFileInfo, SpeedInfo>();

        public VGetStatus(VideoInfo i) {
            this.videoinfo = i;
        }

        public SpeedInfo getSpeedInfo(VideoFileInfo dinfo) {
            SpeedInfo speedInfo = map.get(dinfo);
            if (speedInfo == null) {
                speedInfo = new SpeedInfo();
                speedInfo.start(dinfo.getCount());
                map.put(dinfo, speedInfo);
            }
            return speedInfo;
        }

        
        @Override
        public void run() {
            List<VideoFileInfo> dinfoList = videoinfo.getInfo();

            switch (videoinfo.getState()) {
                case DONE:
                    if (videoinfo instanceof YouTubeInfo) {
                        YouTubeInfo i = (YouTubeInfo) videoinfo;
                        System.out.println(videoinfo.getState() + " " + i.getVideoQuality());
                    } else {
                        System.out.println("unknown video info");
                    }
                    
                    for (VideoFileInfo d : videoinfo.getInfo()) {
                        SpeedInfo speedInfo = getSpeedInfo(d);
                        speedInfo.end(d.getCount());
                        System.out.println(String.format("file:%d - %s (%s)", dinfoList.indexOf(d), d.targetFile,
                                formatSpeed(speedInfo.getAverageSpeed())));
                    }
                    
                    break;
                case ERROR:
                    System.out.println(videoinfo.getState() + " " + videoinfo.getDelay());
    
                    if (dinfoList != null) {
                        for (DownloadInfo dinfo : dinfoList) {
                            System.out.println("file:" + dinfoList.indexOf(dinfo) + " - " + dinfo.getException() + " delay:"
                                    + dinfo.getDelay());
                        }
                    }
                    
                    break;
                case RETRYING:
                    System.out.println(videoinfo.getState() + " " + videoinfo.getDelay());
    
                    if (dinfoList != null) {
                        for (DownloadInfo dinfo : dinfoList) {
                            System.out.println("file:" + dinfoList.indexOf(dinfo) + " - " + dinfo.getState() + " "
                                    + dinfo.getException() + " delay:" + dinfo.getDelay());
                        }
                    }
                    
                    break;
                case DOWNLOADING:
                    long now = System.currentTimeMillis();
                    if (now - 1000 > last) {
                        last = now;
    
                        String parts = "";
    
                        for (VideoFileInfo dinfo : dinfoList) {
                            SpeedInfo speedInfo = getSpeedInfo(dinfo);
                            speedInfo.step(dinfo.getCount());
    
                            List<Part> pp = dinfo.getParts();
                            if (pp != null) {
                                for (Part p : pp) {
                                    if (p.getState().equals(States.DOWNLOADING)) {
                                        parts += String.format("part#%d(%.2f) ", p.getNumber(),
                                                p.getCount() / (float) p.getLength());
                                    }
                                }
                            }
                            System.out.println(String.format("file:%d - %s %.2f %s (%s)", dinfoList.indexOf(dinfo),
                                    videoinfo.getState(), dinfo.getCount() / (float) dinfo.getLength(), parts,
                                    formatSpeed(speedInfo.getCurrentSpeed())));
                        }
                    }
                    
                    break;
                default:
                    break;
            }
        }
    }

 

First of all, full disclosure: this class is cribbed from the GitHub site (linked above).

Second, note that the code above is little more than one large switch block. Each case within the block represents a status during the download process.

The code also relies on a static method to determine download speed. You can see that below.

    public static String formatSpeed(long s) {
        if (s > 0.1 * 1024 * 1024 * 1024) {
            float f = s / 1024f / 1024f / 1024f;
            return String.format("%.1f GB/s", f);
        } else if (s > 0.1 * 1024 * 1024) {
            float f = s / 1024f / 1024f;
            return String.format("%.1f MB/s", f);
        } else {
            float f = s / 1024f;
            return String.format("%.1f kb/s", f);
        }
    }

 

Here’s what the main() method looks like:

    public static void main(String[] args) {
        String url = "https://www.youtube.com/watch?v=5G5fzf7dyhU";
        File path = new File("/test/");

        try {
            final AtomicBoolean stop = new AtomicBoolean(false);

            //create the URL object
            URL web = new URL(url);
            
            //instantiate the parser
            VGetParser user = VGet.parser(web);

            //instantiate VideoInfo object
            VideoInfo videoinfo = user.info(web);

            //instantiate VGet
            VGet v = new VGet(videoinfo, path);

            //create the inner class to track the status of the download
            VGetStatus notify = new VGetStatus(videoinfo);

            //call to get info like the title and download link
            v.extract(user, stop, notify);

            //print out the title
            System.out.println("Title: " + videoinfo.getTitle());
            
            //print out the download URL
            List<VideoFileInfo> list = videoinfo.getInfo();
            if (list != null) {
                for (VideoFileInfo d : list) {
                    System.out.println("Download URL: " + d.getSource());
                }
            }

            //start the download
            v.download(user, stop, notify);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

As you can see, the code kicks off by instantiating an AtomicBoolean object with the value of false. That object can be used to stop the download in its tracks (but isn’t used for that purpose here).

AtomicBoolean is used instead of Boolean because the code is running in a multi-threaded environment. The whole point of that class is to enable multiple threads to check and change a boolean value.

Following the AtomicBoolean declaration, the next few lines of code instantiate the URL object, the VGet parser, and the VideoInfo objects, respectively.

After that, VGet itself instantiated with a constructor that includes the VideoInfo object and the path where the downloaded MP4 file will be saved.

Then the aforementioned inner class, VGetStatus, is instantiated. Again, that’s the object that will track the status of the download and display helpful output along the way.

The next line of code extracts important info about the video, such as its title and download links. The following few lines display that info to the user running the application.

Finally, the code invokes the download() method on the VGet object.

To test out the managed download solution, just delete the MP4 file that you downloaded in the previous step and run the application. Once again, you should see the file appear in the directory you specified.

 

Wrapping It Up

And there you have it: a very simple way to download YouTube videos within a Java application. Try it out with different videos of varying sizes to see what the application can “handle.”

Challenge: You can also use VGet to download Vimeo videos. Why not write some code to handle that?

Again, be sure to check out the code on GitHub. Feel free to download the whole project into your own Eclipse environment and run it.

Have fun!