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:
1
2
3
4
5
|
< 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:
1
2
3
4
5
6
7
8
9
10
|
public static void main(String[] args) { try { 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.
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
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.
1
2
3
4
5
6
7
8
9
10
11
12
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
public static void main(String[] args) { 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!