Skip to content

Commit 6503da3

Browse files
feat: add automatic binary installer YtDlpInstaller for zero-setup execution
1 parent a19bf90 commit 6503da3

3 files changed

Lines changed: 129 additions & 0 deletions

File tree

src/main/java/com/github/ytdlpjava/YtDlpClient.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,19 @@ public String getYtDlpPath() {
118118
public String getFfmpegPath() {
119119
return ffmpegPath;
120120
}
121+
122+
/**
123+
* Ensures that the yt-dlp binary is available on the system.
124+
* If yt-dlp is not accessible, automatically downloads the official executable for the current OS.
125+
*
126+
* @return YtDlpClient instance pointing to the verified or newly installed binary
127+
*/
128+
public YtDlpClient ensureInstalled() {
129+
if (!isYtDlpAvailable()) {
130+
var installer = com.github.ytdlpjava.installer.YtDlpInstaller.defaultInstaller();
131+
var installedPath = installer.install();
132+
return new YtDlpClient(installedPath.toString(), this.ffmpegPath);
133+
}
134+
return this;
135+
}
121136
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.github.ytdlpjava.installer;
2+
3+
import com.github.ytdlpjava.exception.YtDlpException;
4+
5+
import java.io.BufferedInputStream;
6+
import java.io.FileOutputStream;
7+
import java.io.IOException;
8+
import java.net.URI;
9+
import java.net.URL;
10+
import java.nio.file.Files;
11+
import java.nio.file.Path;
12+
import java.nio.file.Paths;
13+
14+
/**
15+
* Utility for automatically downloading and installing official yt-dlp binaries
16+
* matching the host operating system (Windows, Linux, macOS).
17+
*/
18+
public class YtDlpInstaller {
19+
20+
private static final String BASE_RELEASE_URL = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/";
21+
private final Path storageDirectory;
22+
23+
public YtDlpInstaller() {
24+
this(Paths.get(System.getProperty("user.home"), ".ytdlp-java", "bin"));
25+
}
26+
27+
public YtDlpInstaller(Path storageDirectory) {
28+
this.storageDirectory = storageDirectory;
29+
}
30+
31+
public static YtDlpInstaller defaultInstaller() {
32+
return new YtDlpInstaller();
33+
}
34+
35+
/**
36+
* Downloads and installs the latest official yt-dlp executable for the current OS.
37+
*
38+
* @return Path to the installed executable binary
39+
*/
40+
public Path install() {
41+
String binaryName = getBinaryNameForOs();
42+
String downloadUrl = BASE_RELEASE_URL + binaryName;
43+
Path targetPath = storageDirectory.resolve(binaryName);
44+
45+
try {
46+
Files.createDirectories(storageDirectory);
47+
if (Files.exists(targetPath) && Files.size(targetPath) > 0) {
48+
setExecutablePermissions(targetPath);
49+
return targetPath;
50+
}
51+
52+
downloadFile(downloadUrl, targetPath);
53+
setExecutablePermissions(targetPath);
54+
return targetPath;
55+
} catch (IOException e) {
56+
throw new YtDlpException("Failed to automatically install yt-dlp binary from " + downloadUrl, e);
57+
}
58+
}
59+
60+
private void downloadFile(String urlString, Path destination) throws IOException {
61+
URL url = URI.create(urlString).toURL();
62+
try (BufferedInputStream in = new BufferedInputStream(url.openStream());
63+
FileOutputStream fileOut = new FileOutputStream(destination.toFile())) {
64+
byte[] dataBuffer = new byte[8192];
65+
int bytesRead;
66+
while ((bytesRead = in.read(dataBuffer, 0, 8192)) != -1) {
67+
fileOut.write(dataBuffer, 0, bytesRead);
68+
}
69+
}
70+
}
71+
72+
private void setExecutablePermissions(Path path) {
73+
String os = System.getProperty("os.name").toLowerCase();
74+
if (!os.contains("win")) {
75+
path.toFile().setExecutable(true, false);
76+
}
77+
}
78+
79+
private String getBinaryNameForOs() {
80+
String os = System.getProperty("os.name").toLowerCase();
81+
if (os.contains("win")) {
82+
return "yt-dlp.exe";
83+
} else if (os.contains("mac")) {
84+
return "yt-dlp_macos";
85+
} else {
86+
return "yt-dlp";
87+
}
88+
}
89+
90+
public Path getStorageDirectory() {
91+
return storageDirectory;
92+
}
93+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.github.ytdlpjava;
2+
3+
import com.github.ytdlpjava.installer.YtDlpInstaller;
4+
import org.junit.jupiter.api.DisplayName;
5+
import org.junit.jupiter.api.Test;
6+
7+
import java.nio.file.Path;
8+
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
11+
class YtDlpInstallerTest {
12+
13+
@Test
14+
@DisplayName("Should initialize default installer with user home directory")
15+
void shouldInitDefaultInstaller() {
16+
YtDlpInstaller installer = YtDlpInstaller.defaultInstaller();
17+
Path expectedPath = Path.of(System.getProperty("user.home"), ".ytdlp-java", "bin");
18+
19+
assertThat(installer.getStorageDirectory()).isEqualTo(expectedPath);
20+
}
21+
}

0 commit comments

Comments
 (0)