Java library wrapping yt-dlp via GraalVM Polyglot (GraalPy), with an optional Spring Boot starter and an embedded React 19 web UI.
GroupId: dev.leimuhue
Package: dev.leimuhue.ytdlp
Java: 17+
- Java 17+ — Required
- Maven 3.8+ — Required
- ffmpeg — Required (yt-dlp uses it for video/audio processing and muxing)
- yt-dlp — Required for CLI fallback mode; for GraalPy mode, see below
- Node.js 22+ — Optional (only needed for frontend development)
The engine uses a hybrid approach:
- GraalPy Python module (preferred) — calls
yt_dlpdirectly via the GraalPy polyglot context - CLI fallback (automatic) — invokes
yt-dlpviaProcessBuilderif the Python module is unavailable
For GraalPy mode, run the plugin manually to install yt-dlp in the bundled Python venv:
mvn graalpy:process-graalpy-resources -pl ytdlp-engineytdlp-java/
├── pom.xml Root multi-module POM
├── ytdlp-engine/ Core library (53 tests)
│ └── src/main/java/dev/leimuhue/ytdlp/
│ ├── YtdlpEngine.java Central entry point
│ ├── YtdlpOptions.java Fluent options builder
│ ├── model/ Typed response models
│ │ ├── VideoInfo.java
│ │ ├── FormatInfo.java
│ │ ├── DownloadResult.java
│ │ └── PlaylistInfo.java
│ ├── exception/ Exception hierarchy
│ │ ├── YtdlpException.java
│ │ ├── YtdlpExecutionException.java
│ │ ├── YtdlpTimeoutException.java
│ │ └── YtdlpEnvironmentException.java
│ └── internal/
│ ├── PythonBridge.java GraalPy integration
│ └── CliFallback.java CLI via ProcessBuilder
├── ytdlp-engine-spring-boot-starter/ Spring Boot auto-config (4 tests)
│ └── src/main/java/dev/leimuhue/ytdlp/spring/
│ ├── YtdlpProperties.java @ConfigurationProperties
│ └── YtdlpAutoConfiguration.java @AutoConfiguration
└── ytdlp-engine-web/ Web UI + REST API
├── frontend/ React 19 source (Vite)
└── src/main/java/dev/leimuhue/ytdlp/web/
└── YtdlpController.java REST endpoints
mvn clean installTo build without running tests:
mvn clean install -DskipTestsYtdlpEngine engine = YtdlpEngine.builder()
.defaultOptions(YtdlpOptions.create().output("/tmp/%(title)s.%(ext)s"))
.build();
// Extract metadata
VideoInfo info = engine.extractInfo("https://youtube.com/watch?v=xxx");
// Download with per-call overrides
DownloadResult result = engine.download("https://youtube.com/watch?v=xxx",
YtdlpOptions.create().format("bestvideo[height<=1080]+bestaudio"));
// List formats
List<FormatInfo> formats = engine.listFormats("https://youtube.com/watch?v=xxx");
// Search
List<VideoInfo> results = engine.search("query", 10);
engine.close();mvn spring-boot:run -pl ytdlp-engine-webOpen http://localhost:8080 — paste a video URL, click Extract to see metadata and formats, select a format and click Download.
cd ytdlp-engine-web/frontend
npm run devOpen http://localhost:3000 with hot reload. API calls proxy to localhost:8080.
ytdlp:
enabled: true
default-output: /downloads/%(title)s.%(ext)s
timeout: 300
allow-create-process: true
allow-io: true- Install dependencies: Java 17+, Maven 3.8+, ffmpeg
- Build:
mvn clean install - Run:
mvn spring-boot:run -pl ytdlp-engine-web - Open
http://localhost:8080