Repository navigation
RU RU 2 Managing links
Note
Настраивается: platforms.allowMatureContent, searchLimit, searchCleanupInterval, biliBiliCookie и
player.mrlCleanupInterval. Смотрите их комментарии в config/watermedia.toml.
MRL (Multimedia Resource Location) превращает любую ссылку в медиа, которое она содержит: один или несколько источников, у каждого свои варианты качества, аудиодорожки и метаданные. Он разрешается в фоне и остаётся в кэше.
MRL mrl = PlayerAPI.mrl("https://example.com/movie.mp4"); // A WEB LINK, A FILE UNDER THE WORKING DIRECTORY OR A water:// LINK
MRL[] queue = PlayerAPI.preload(firstUri, secondUri); // START RESOLVING SEVERAL AT ONCE
// FROM A GAME TICK OR A UI TIMER
switch (mrl.status()) {
case FETCHING -> { } // STILL RESOLVING
case LOADED -> play(mrl); // READY TO PLAY
case BLOCKED -> showMatureWarning(); // MATURE CONTENT WHILE IT'S NOT ALLOWED
case ERROR -> showError(mrl.exception()); // WHY IT FAILED; A MALFORMED LINK STARTS HERE
case EXPIRED, FORGOTTEN -> mrl = mrl.reload(); // STALE OR EVICTED: KEEP THE HANDLE reload() RETURNS
}
mrl.subscribe(done -> refreshUi()); // OR GET CALLED ONCE WHEN IT FINISHES OR FAILS
boolean finished = mrl.await(5_000); // OR WAIT, BUT NEVER ON THE GAME OR RENDER THREAD
// ONE LINK CAN HOLD SEVERAL SOURCES, SUCH AS A GALLERY WITH AN IMAGE AT 0 AND A VIDEO AT 1
MRL.Source first = mrl.source(0); // null FOR A WRONG INDEX
MRL.Source video = mrl.sourceByType(MediaType.VIDEO); // THE FIRST VIDEO, OR null
List<MRL.Source> all = mrl.sources();
MediaType type = video.type(); // IMAGE, VIDEO, AUDIO...
Metadata info = video.metadata(); // TITLE, DESCRIPTION, AUTHOR, DURATION...
URI thumbnail = video.thumbnail();
URI link = video.uri(MediaQuality.HIGH); // THE CLOSEST QUALITY AVAILABLE
List<Track> dubs = video.audioTracks(); // SEPARATE AUDIO TRACKS, THE ORIGINAL FIRST
MediaPlayer player = PlayerAPI.player(mrl, 1, gfx, sfx); // PLAY THE SOURCE AT INDEX 1 (SEE PLAYING MEDIA)
Прямая ссылка завершается ошибкой, если сервер отвечает статусом ошибки или ответом без медиа; плейлисты HLS открываются как одно видео, а плейлисты IPTV как один источник на канал.
Ссылки с этих платформ разрешаются из коробки:
- 📺 YouTube, Twitch, Kick, BiliBili, Odysee, D.tube, VidLii
- 🐦 Twitter (X), Bluesky, TikTok, Facebook, Instagram
- 🎵 SoundCloud, Newgrounds (фильмы, музыка и арт)
- 🖼️ Imgur, Lightshot, Medal, Streamable, Sendvid
- 📁 Google Drive, Dropbox, MediaFire
- 🔞 PornHub (только при включённом
platforms.allowMatureContent) - 🔗 Прямые ссылки на любое видео, музыку, изображение или плейлист, локальные файлы и
ссылки
water://
YouTube, SoundCloud, Facebook и Instagram воспроизводятся через yt-dlp, которому нужен Binaries. BiliBili
воспроизводится до 480p или до 1080p (8K с VIP) с cookie вашей сессии в platforms.biliBiliCookie.
Platform.Resolution resolved = PlatformAPI.fetch(uri); // THE RESOLUTION WITHOUT AN MRL, FOR TOOLS; null WHEN NO PLATFORM HANDLES THE LINK
// YOUR OWN SITE: CHECKED BEFORE THE BUILT-IN PLATFORMS, AND IT NEEDS NO STARTUP
PlatformAPI.register(new Platform() {
@Override
public String name() { return "MySite"; }
@Override
public Platform.Resolution resolve(final URI uri) throws PlatformException {
if (!"mysite.com".equals(uri.getHost())) return null; // NOT YOURS: THE NEXT PLATFORM TRIES
if (isProfile(uri)) throw PlatformException.unsupported(this.name(), uri); // YOURS BUT NOTHING TO PLAY: MRL GOES ERROR
if (isMature(uri)) throw new MatureContentException(this.name(), "Mature video"); // MRL GOES BLOCKED
final Platform.Quality quality = new Platform.Quality(findVideo(uri), 1920, 1080); // REAL SIZE, 0 WHEN UNKNOWN
final Platform.Source source = new Platform.Source(MediaType.VIDEO, null, null, null, List.of(quality), null, null);
return new Platform.Resolution(null, source); // null EXPIRY: THE LINKS NEVER EXPIRE
}
});
Любая другая ошибка вашей платформы тоже оформляется как PlatformException, а альбом или плейлист пропускает плохой
элемент, а не падает целиком.
YouTube (с Binaries), Twitch, Kick и Imgur поддерживают поиск по ключевым словам, а ваша платформа присоединяется к ним,
если переопределит search(query, limit).
List<PlatformResult> results = PlatformAPI.search("lofi"); // RETURNS AT ONCE; THE LIST FILLS AS PLATFORMS ANSWER
boolean searching = PlatformAPI.searching("lofi"); // false ONCE EVERY PLATFORM ANSWERED; POLL BOTH FROM THE UI
for (final PlatformResult hit: results) {
show(hit.title(), hit.thumbnail(), hit.platform().name()); // WHEN PICKED, PlayerAPI.mrl(hit.uri()) OPENS IT LIKE ANY LINK
}
List<PlatformResult> fresh = PlatformAPI.search("lofi", true); // ASK THE PLATFORMS AGAIN INSTEAD OF THE CACHE
Новый запрос заменяет предыдущий, а пока пользователь продолжает печатать, запрос немного ждёт следующего нажатия клавиши. Twitch и Kick показывают только каналы в прямом эфире.
PlayerAPI.mrl("water://local/config/song.mp3"); // ./config/song.mp3, RESOLVED AGAINST THE WORKING DIRECTORY
PlayerAPI.mrl("water://remote/xyz12345"); // A FILE ON YOUR FILE SERVER (SEE UPLOADING AND DOWNLOADING FILES)
PlayerAPI.mrl("water://global/abcd6789"); // A FILE ON OUR GLOBAL SERVER
Ссылки remote и global содержат ID файла, выданный сервером, а не путь к папке.
HTTPS работает из коробки с сертификатами, которым Java уже доверяет, и эта проверка никогда не отключается.
Используете самоподписанный сертификат или собственный центр сертификации? Импортируйте его в truststore и укажите этот
truststore Java до запуска WATERMeDIA (в Minecraft добавьте те же опции -D в аргументы JVM лаунчера):
keytool -importcert -alias my-server -file my-server.crt -keystore my-truststore.jks -storepass changeit -noprompt
java -Djavax.net.ssl.trustStore=my-truststore.jks -Djavax.net.ssl.trustStorePassword=changeit -jar my-app.jar
- Пустой или повреждённый truststore не даёт FFmpeg запуститься, поэтому видео остаётся выключенным, пока его не исправят.
- Собственной сборке FFmpeg нужна такая же поддержка TLS, как у встроенной.
- Заголовки входа и cookie (
Authorization,Cookie,X-WaterMedia-Token) никогда не уходят вслед за редиректом на другой сервер, а редиректы ведут только на ссылки http или https.