Links
Tags
Creators
Details
All versions
⚡ [NEW] PlatformsAPI (
3.0.0.17
Beta
WM/3.0.0.172 months ago 20.6K
Compatibility
Minecraft: Java Edition
1.21.11
1.21.1
1.20.1
1.19.2
1.18.2
Platforms
Fabric
Forge
NeoForge
Supported environments
Client-side
Required content
Changes
📦 UPDATE 3.0.0.17 (BETA)
⚡ Core / lifecycle
- ⚙️ Added:
org.watermedia.api.WaterMediaAPIabstract base class — every top-level API now inheritsname(),load(WaterMedia),start(WaterMedia),release(WaterMedia)plus boot-progress fieldsstep/steps/stepNamefor loading screens - ⚙️ Added:
WaterMedia#steps(),step(),currentAPI(),totalWorkSteps(),completedWorkSteps()to surface boot progress - ⚙️ Changed: APIs are now registered as
WaterMediaAPIinstances (CodecsAPI→PlatformAPI→MediaAPI→NetworkAPI), each driven throughload()+start()with per-API try/catch - ⚙️ Changed:
WaterMedia#start(name, ...)now rejects blank names (IllegalArgumentException) - ⚙️ Changed:
WaterMediaConfig.Decoders#defaultQualityretyped fromMRL.Qualitytoorg.watermedia.api.util.MediaQuality
⚡ CodecsAPI
- ⚙️ Added:
ImageReader— new abstract pull-based per-frame decoder (Closeable) withwidth/height/pixelFormat/planeCount/plane/planeStride/scan/loopCount/frameCount/duration/delays/averageFps/variableFrameRate/metadata/readAll/hasNext/next - ⚙️ Added:
ImageWriter— streaming frame encoder skeleton (writeFrame(ByteBuffer),writeFrame(ByteBuffer, long)) - ⚙️ Added:
ImageMetadata— normalized metadata bag with typed accessors (title,description,authors,copyright,comments,creationTime,software,source) and free-form map - ⚙️ Added:
PNG_METAKEY_*,GIF_METAKEY_*,WEBP_METAKEY_*metadata key constants onCodecsAPI - ⚙️ Added:
ImageData.Scanrecord (frameCount,delays,duration,loopCount) withScan.EMPTYconstant - ⚙️ Added:
UnsupportedFormatException extends XCodecException - ⚙️ Added:
CodecsAPI#getMediaType(InputStream)— byte-signature sniffer returning aMediaType(resolves ambiguousapplication/octet-streamresponses) - ⚙️ Changed:
CodecsAPI#decodeImage(byte[]|ByteBuffer)now declaresthrows IOExceptionand returns anImageReader. New overloadsdecodeImage(ByteBuffer|byte[], PixelFormat requestedFormat). Unknown magic throwsUnsupportedFormatException(was returningnull) - ⚙️ Removed: pluggable decoder registry —
CodecsAPI#register(ImageCodec), theIMAGE_CODECSlist and theorg.watermedia.api.codecs.ImageCodecabstract base (superseded byImageReader)
⚡ MediaAPI
- ⚙️ Added:
MediaAPI#getMRL(URI),MediaAPI#createPlayer(MRL, [int sourceIndex,] Supplier<GFXEngine>, Supplier<SFXEngine>)— player factory now lives onMediaAPI(was onMRL) - ⚙️ Added:
MRL#reloadAll(),MRL#subscribe(Consumer<MRL>)(fires once and drops),MRL#hasError(),MRL#exception(),MRL#forgotten(),MRL#blocked()(true when gated by mature-content) - ⚙️ Added:
MRL.Source#qualityOf(URI)and a newMRL.SlaveEntry(name, lang, uri)record - ⚙️ Added:
MediaPlayer.NO_SOURCEconstant - ⚙️ Changed:
MRL#get(URI)renamed toMRL#getMRL(URI);MRL#preload(URI...)now returnsMRL[] - ⚙️ Changed:
MRL#sources()/MRL#sourcesByType(MediaType)returnList<Source>(wasSource[]) - ⚙️ Changed:
MRL.Sourcerewritten as record(MediaType type, URI thumbnail, Metadata metadata, RequestHeaders headers, EnumMap<MediaQuality,URI> qualities, List<SlaveEntry> audioSlaves, List<SlaveEntry> subSlaves); non-emptyqualitiesinvariant enforced - ⚙️ Changed:
MediaPlayerconstructor now takes(MRL, int sourceIndex, GFXEngine, SFXEngine)instead of a resolvedSource - ⚙️ Changed:
MediaPlayer#quality(MediaQuality)/quality()retyped fromMRL.QualitytoMediaQuality - ⚙️ Changed:
MediaPlayer#audioSource()returnsNO_SOURCE(wasNO_TEXTURE) - ⚙️ Removed (from
MRL):invalidate(URI),clearCache(),cacheSize(),error(),busy(),createPlayer(...),createThumbnailPlayer(...),sourceBuilder(...),SourceBuilder,Source.withQuality/reassignQuality/withSlave/withMetadata/slaveByLanguage - ⚙️ Removed (from
MediaAPI):registerPlatform(...)overloads and the internalPLATFORMSlist (moved to newPlatformAPI) - ⚙️ Relocated:
MRL.MediaType→api.util.MediaType;MRL.Quality→api.util.MediaQuality;MRL.Metadata→api.util.Metadata - ⚙️ Added: optional fast-path on
GFXEngine—supportsFrameTextures(),uploadFrameTextures(ByteBuffer[] frames, int stride),useFrameTexture(int) - ⚙️ Added:
GLEngine.Builderaccepts four extra GL function consumers —activeTexture,bindVertexArray,bindFrameBuffer,bindBuffer— so a host can intercept GL state - ⚙️ Removed:
GFXEngine.ColorSpaceinner enum — replaced by top-levelorg.watermedia.api.util.PixelFormat - ⚙️ Changed:
colorSpaceparameters/fields renamedpixelFormatacrossGFXEngine/GLEngine/FFMediaPlayer - ⚙️ Fixed typo:
MediaPlayer#foward()→forward()(also inServerMediaPlayer,FFMediaPlayer,TxMediaPlayer)
⚡ [NEW] PlatformsAPI (api/platform/ package)
- ⚙️ Added:
org.watermedia.api.platform.PlatformAPI extends WaterMediaAPI— registry withstatic PlatformData fetch(URI),static void register(IPlatform); iteration is reverse-registration so apps can override built-ins - ⚙️ Added:
IPlatform(new contract —name(),validate(URI),PlatformData getData(URI)),PlatformData(Instant expires, DataSource... entries),DataSource(MediaType, URI thumbnail, Metadata, RequestHeaders, DataQuality[], List<DataSlave> audioSlaves, List<DataSlave> subSlaves),DataQuality(URI, int width, int height),DataSlave(name, lang, uri) - ⚙️ Added:
internal.WaterPlatform— handleswater://local/remote/globalURIs (constantsHOST_LOCAL/REMOTE/GLOBAL,GLOBAL_SERVER,toHttpURL(URI)) - ⚙️ Relocated:
BiliBiliPlatform,ImgurPlatform,KickPlatform,PornHubPlatform,TwitterPlatformmoved fromapi/media/platform/toapi/platform/web/and migrated to the newPlatformData/DataSource/DataQualityshape - ⚙️ Removed:
api/media/platform/DefaultPlatform, the oldapi/media/platform/IPlatform, and the oldapi/media/platform/{Lightshot,Streamable,Twitch,Youtube}Platform - ⚙️ Removed:
YoutubePlatform(no replacement in this beta)
⚡ NetworkAPI
- ⚙️ Added:
org.watermedia.api.util.NetRequest— builder-style HTTP/FTP/file:// client (create(URI|String),Builder.method/accept/contentType/referer/userAgent/header/addHeader/body/connectTimeout/readTimeout/maxRedirects/headers/send;uri(),statusCode(),contentType(),contentLength(),header(),requestHeaders(),responseHeaders(),getInputStream(),getInputStream(Function),readAllAsString(),json(),json(Class),UserAgentenum,installExtraMimeTypes()) - ⚙️ Added:
org.watermedia.api.util.RequestHeaders— insertion-ordered case-insensitive multi-value header bag (set/add/get/getAll/has/removeAll/entries/iterator/toRawString,defaults(URI)factory; FFmpeg-ready blob viatoRawString()) - ⚙️ Added:
org.watermedia.api.util.MediaQualityenum (UNKNOWN,Q144P/LOWEST…Q8K) withof(int),of(int,int),higher(),lower(),closest(Set, MediaQuality) - ⚙️ Added:
org.watermedia.api.util.MediaTypeenum (IMAGE/VIDEO/AUDIO/SUBTITLES/UNKNOWN) withof(String mimeType)andofExtension(String) - ⚙️ Added:
org.watermedia.api.util.Metadatarecord(title, desc, Instant postedAt, long duration, author) - ⚙️ Added:
HlsTool#fetch(URI, String userAgent)overload (internals migrated fromHttpClienttoNetRequest) - ⚙️ Changed:
NetworkAPInowextends WaterMediaAPI;start(WaterMedia)becomes an instance override; reports two boot steps (MIME registry,FileServer); also installs MIME mappings (webp, apng, mkv, opus, m3u8, mpd, vtt, NETPBM, …) intoURLConnection.getFileNameMap() - ⚙️ Changed:
NetworkAPI#upload(...)return type is nowNetworkServer.UploadStatus(was top-levelUploadStatus) - ⚙️ Removed (from
NetworkAPI):parseQuery(String),waterURL(String),parseWaterURL(URI), the staticWATER_HANDLERfield —water://URL handling is gone from the public API - ⚙️ Renamed:
NetServer→NetworkServer(UploadStatusmoved inside as nested class) - ⚙️ Removed:
WaterStreamHandler, top-levelUploadStatus,NetTool(replaced byNetRequest)
⚡ GENERAL
- ✨ Added TikTok platform support (with full Metadata and Multi-variant qualities)
- ✨ Added D.Tube platform support (with full Metadata and Multi-variant qualities)
- ✨ Added Bluesky platform support (with full Metadata, Gallery support and Multi-variant qualities)
- ✨ Added Odysee platform support (with full Metadata and Multi-variant qualities)
- ✨ Added VidLii platform support
- ✨ Added Sendvid platform support (with half metadata and status-poll wait)
- ✨ Added back Google Drive, Dropbox and MediaFire platform support (limited support due to platform restrictions)
- ✨ Rewrote and Enhanced Twitch platform: now covers VOD, live, clips and better codecs
- ✨ Rewrote and Enhanced Kick platform: now covers clips
- ✨ Rewrote and Enhanced Streamable platform: both
mp4andmp4-mobilequalities exposed as quality variants - ✨ Rewrote Lightshot platform on the new platform API (produces
DataSource(MediaType.IMAGE, ...)) - ✨ Added on-disk media
NetworkCache— two-tier cache (NETWORK live; CODEC reserved for upcoming BC7/DDS) with atomic writes, lock striping, expiry index (WMICv3 format). Wired intoMediaAPI.start()underinstance.tmp/cache. Honored byFFMediaPlayerHTTP body fetches (config-gated, skips HLS/DASH manifests) and byTxMediaPlayer - ✨ Rewrote
TxMediaPlayerwith three playback modes: (1) static one-shot upload, (2) pre-uploaded per-frame textures via the newGFXEngine#uploadFrameTexturesfast-path when frame count ≤txMultiTextureFrameThreshold, (3) streamingImageReaderdecode driven by the playback clock with a bounded prefetch queue (64 MB budget) and direct-ByteBufferpool. Supports seek/loop/step-backwards, falls intoBUFFERINGon under-run - ✨ Added IPTV channel support —
m3u/m3u8channel playlists are expanded into individual channel sources (title, group and logo per channel), with a bundled channel catalog - ✨ Added a house-made JPEG decoder (pure Java, no
ImageIOdependency; baseline + progressive, all common chroma subsamplings) - ✨ Optimized PNG/APNG decoder (~40% faster on animated PNG) and added rich image metadata reporting (text, gamma, chromaticities, sRGB/ICC color profile, physical dimensions, timestamps, and ancillary chunks)
- ✨ Optimized GIF decoder (~12–24% faster); Netscape loop count now surfaced in metadata
- ✨ Optimized WEBP lossless decoder (~40% faster)
- ✨ Optimized WEBP lossy decoder (~2–14% faster); static lossy VP8 without alpha now decodes to native
YUV420Pplanes (no RGB conversion) - ✨ Added new config options:
decoders.maxImageSourceBytesMB(128),media.mrlManagerCleanupInterval(60 min),media.txMultiTextureFrameThreshold(5),media.txNetworkCache,media.ffmpegNetworkCache,media.ffmpegNetworkCacheMaxBytesMB(10),media.ffmpegAnalyzeDurationMs(7000),media.ffmpegProbeSizeMB(10),media.platforms.allowMatureContent,network.requestTimeoutMs(15000),network.maxRedirects(10),network.maxTextBytes(16 MiB) - ✨ Added JOML 1.10.8 as a library dependency
- ✨ [WaterMediaApp] Redesigned the entire standalone app on a new backend-agnostic 2D
RenderEngine/RenderSystem(OpenGL 3.2 core backend; architected so Vulkan can drop in without touching widgets), vertex batching, rounded rects / circles / arcs / gradients / glow / shadow primitives - ✨ [WaterMediaApp] Added more test cases, thumbnail previews, status badges.
- ✨ [WaterMediaApp] Enhanced Mouse experience on controls and buttons
- ✨ [WaterMediaApp] Added a
FrameLimiterwith monitor-aware pacing for drivers ignoring swap interval - ✨ [WaterMediaApp] New
LoadingScreenwith animated boot splash and 8-frame duck animation, eased progress driven byWaterMedia.completedWorkSteps()/totalWorkSteps() - ✨ [WaterMediaApp] Exit-confirmation dialog with
ENTER/ESCbindings - 🛠️ Changed:
MRLreadystate flips true on success or failure; callers must now checkhasError()/exception()separately - 🛠️ Changed:
FFMediaPlayerHW-decoder priority —D3D11VAnow precedesD3D12VA - 🛠️ Changed:
FFMediaPlayerquality auto-aligns — when initial quality isUNKNOWN, it is corrected once real video stream dimensions are known - 🛠️ Changed:
FFMediaPlayeropen-failure logs now include decodedav_strerrortext instead of swallowing the return code - 🛠️ Changed: HTTP requests across
FFMediaPlayer,TxMediaPlayer, and audio slaves now build headers from a unifiedRequestHeaders.defaults(uri)(no more hand-bakedUser-Agent/Accept/Referer) - 🛠️ Changed:
MRLsource resolution delegated toPlatformAPI.fetch(URI); loader threadpool switched toThreadTool.createRecomendedThreadPool(...) - 🛠️ Changed: ambiguous/wrong
Content-Type(e.g.application/octet-stream) is now resolved by sniffing the leading bytes with a URL-extension fallback, so mislabeled media from CDNs no longer fails to open; non-HTTP requests force a real connection instead of letting the URL handler guess the MIME type and existence - 🛠️ Changed: the MRL manager periodically forgets expired or errored MRLs to free memory (interval configurable via
media.mrlManagerCleanupInterval) - 🛠️ Changed: BiliBili CDN cookie/UA/Referer now flow through
RequestHeaderson everyDataSource(including live);WaterMediaConfig.media.platforms.biliBiliCookiehonored at request time - 🛠️ Changed: Mature content is now gated behind
media.platforms.allowMatureContent(disabled by default) — Twitch (streams, VODs, clips) and PornHub throwMatureContentExceptionbefore any data fetch;MRL#blocked()reports the gated state - 🛠️ Changed:
NetworkServer.maxUploadSizeMB <= 0now disables the size cap (was always enforced);NetworkAPI.uploadhonorsWaterMediaConfig.network.requestTimeoutMs - 🛠️ Changed: [WaterMediaApp]
AppBootstrapquick-scan fast-path skips the GUI when all jars are cached; otherwise a console window with progress and 5-second auto-launch is shown; renamedSideloadable→Extension,BOOTSTRAPPED_FLAG→APP_FLAG - 🛠️ Changed: [WaterMediaApp]
HomeScreen,PlayerScreen,MRLSelectorScreen,OpenMultimediaScreenrebuilt (ConsoleScreenandSourceSelectorScreenremoved;GridandSelectorremoved in favor ofListView/StackContainer) - 🐛 Fixed: GIF decoder writing
0x00000000at the transparent index even on frames whose GCE had no transparent-color flag (non-transparent GIFs were getting holes) - 🐛 Fixed: NETPBM
PAMRGB_ALPHAdecode was emitting an extra0xFFbyte between RGB and A, shifting every subsequent pixel - 🐛 Fixed:
NetworkServer#handleUploadno longer NPEs / throwsNumberFormatExceptionon missing or malformedContent-Length(returns 400) - 🐛 Fixed:
NetworkServerpartial/aborted uploads are cleaned up — half-written file and its ID directory are deleted on mid-transferIOException - 🐛 Fixed:
TxMediaPlayerreset/release no longer leaks lifecycle threads or theImageReader - 🐛 Fixed: implausible frame rates reported by some HLS streams (e.g. the 90 kHz clock) no longer break frame pacing — values outside a sane range are now rejected
- 🐛 Fixed: JVM crash on HLS streams whose audio/video parameters fail to probe (sample rate 0) — the stream is now torn down cleanly instead of dividing by zero
Supplementary resources
| File | Type | Size | |
|---|---|---|---|
| watermedia-3.0.0.17-sources.jar | Unknown | 1.64 MiB |
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:G922NeHS:PliJVHhd"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:G922NeHS:PliJVHhd"
}


