- Java 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful
|
||
| .mvn | ||
| kodik-api-core | ||
| kodik-api-retrofit | ||
| .gitignore | ||
| .woodpecker.yml | ||
| LICENSE | ||
| mvnw | ||
| mvnw.cmd | ||
| pom.xml | ||
| README.md | ||
Kodik API
Typed Java client for the Kodik search, list and player links APIs.
kodik-api is a small, transport-agnostic library for the Kodik media API. It
covers the /search and /list endpoints plus the parsing of the embedded
player page: media metadata and the direct video links behind a player link.
Request and response models are immutable records with fluent, generated
builders, so every searchable media type, MPAA rating and anime kind is
compile-time checked instead of being a magic string.
Installing / Getting started
The project is a Maven multi-module reactor. The core module contains the request/response models and the transport SPI; the optional Retrofit2 module provides a ready-made implementation of that SPI.
Add the core dependency to your pom.xml:
<dependency>
<groupId>dev.bivashy</groupId>
<artifactId>kodik-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
If you want the Retrofit2 transport, add it as well:
<dependency>
<groupId>dev.bivashy</groupId>
<artifactId>kodik-api-retrofit</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
A minimal "hello world" looks like this:
KodikHttpClient transport = RetrofitKodikHttpClientWrapper.create("https://kodik-api.com");
KodikAPI api = new KodikAPI(transport, "your-token");
Result<SearchResponse> found = api.search(
api.search().byTitle("Naruto").build());
if (found instanceof Result.Success<?> success) {
SearchResponse response = (SearchResponse) success.value();
response.results().forEach(media -> System.out.println(media.title()));
}
Executing this sends a search request to Kodik, parses the JSON response into
a typed SearchResponse, and prints the matching titles.
When you run inside Quarkus with the EasyRetrofit extension, do not pass the
URL manually: inject the RetrofitKodikHttpClient that EasyRetrofit produces
and configure the base URL in application.properties instead:
kodik.baseUrl=https://kodik-api.com
Initial Configuration
- A Kodik API token. Kodik issues tokens per domain; you need one to authenticate.
- Java 25 (the build targets
maven.compiler.release25).
Developing
Clone and build the reactor:
git clone git@git.bivashy.dev:anyame/kodik-api.git
cd kodik-api
mvn clean install
This compiles both modules, runs the annotation processor that generates the
record builders (SearchRequestBuilder, ListRequestBuilder,
SearchResponseBuilder, ListResponseBuilder), and installs the artifacts into
your local Maven repository so dependent projects (like your Quarkus
application) can resolve them.
Building
After code changes, re-run from the root:
mvn clean install
Deploying / Publishing
Publishing is handled by a Woodpecker CI workflow
(.woodpecker.yml) that runs ./mvnw deploy -DskipTests on every push to
main (and manually) and uploads the artifacts to the Forgejo Maven package
repository. The gitea_username and gitea_token CI secrets must be set. For
local consumption, running mvn install from the root is enough.
Features
- Request and response models are immutable records with builders and immutable
with...withers generated by record-builder — no hand-written builder boilerplate. - Full coverage of the documented Kodik filter parameters on
SearchRequestandListRequest, including the additionalwith_*response params. - Typed value sets for the stringly-typed parameters:
MediaType(media types, withMediaType.join(...)for the comma-joinedtypesfilter)MpaaRating(g,pg,pg-13,r,rx)AnimeKind(tv,tv13,tv24,tv48,movie,special,ova,ona,music)Quality(NHD/SD/HD→360p/480p/720p) for video preferences
- Search validation that requires at least one identifier
(
title,titleOrig,shikimoriId,kinopoiskId,imdbId,id,mdlId,worldartAnimationId,worldartCinemaId,worldartLink,playerLink). - Transport-agnostic
KodikHttpClientSPI returning a sealedResult<T>(Success/Failure). - Optional Retrofit2 transport in a separate module, free of Quarkus/CDI code.
- Player-page parsing: fluent
metadataQuery(String)/videoLinksQuery(String)onKodikAPIandvideoLinks()/metadata()on every search/list result, with optional translation, quality and episode preferences. Direct file links are decoded and normalized automatically.
Configuration
A KodikAPI instance is created from a KodikHttpClient implementation and a
token:
new KodikAPI(KodikHttpClient transport, String token)
You supply the transport; the library ships one for Retrofit2.
search()
Type: SearchStep
Pick exactly one identifier, then chain any number of filters on the returned
SearchRequestBuilder and finish with build():
SearchRequest request = api.search()
.byTitle("One Piece")
.withTypes(MediaType.join(MediaType.ANIME, MediaType.ANIME_SERIAL))
.withYear(1999)
.withRatingMpaa(MpaaRating.PG_13.apiValue())
.withMaterialData(true)
.build();
api.search(request);
All documented filters are available: titleOrig, strict, fullMatch, the
external ids, types, year, camrip, lgbt, translationId,
translationType, animeKind, animeStatus, mydramalistTags,
ratingMpaa, minimalAge, kinopoiskRating, imdbRating, shikimoriRating,
animeStudios, genres, animeGenres, duration, playerLink, hasField*,
prioritizeTranslations, blockTranslations, season, episode,
notBlockedIn, countries, actors, directors, producers, writers,
composers, editors, designers, operators, licensedBy, plus the
withSeasons, withEpisodes, withEpisodesData and withPageLinks extras.
limit defaults to 100.
list()
Type: ListRequestBuilder
The list endpoint needs no identifier, only optional filters plus the
list-specific sort and order:
ListRequest request = api.list()
.withAnimeKind(AnimeKind.TV.apiValue())
.withSort("year")
.withOrder("desc")
.build();
api.list(request);
Result<T>
Every call returns a sealed Result<T>:
switch (result) {
case Result.Success<SearchResponse> success -> handle(success.value());
case Result.Failure failure -> log(failure.error(), failure.statusCode());
}
Video links and metadata
The embedded player behind a media link can be parsed into media metadata and
the direct video file links. Two entry points exist: results obtained from
search() / list(), and arbitrary player/media links through KodikAPI.
Results from search() and list()
Responses returned by KodikAPI.search(...) / KodikAPI.list(...) bind a
VideoLinkExtractService to every result, so each MediaResult exposes the
queries directly:
Result<SearchResponse> found = api.search(api.search().byTitle("One Piece").build());
if (found instanceof Result.Success<SearchResponse> success) {
for (MediaResult media : success.value().results()) {
// metadata of the media's player page (no preferences)
Result<MediaMetadata> metadata = media.metadata();
// decoded direct file links, with optional preferences
Result<VideoLinks> links = media.videoLinks()
.preferTranslation(158) // optional
.preferQuality(Quality.HD) // optional, defaults to Quality.HD
.preferEpisode(1) // optional, defaults to 1
.query();
}
}
A response deserialized without going through KodikAPI is not bound, and
videoLinks() / metadata() return a Result.Failure explaining that the
response must be obtained through KodikAPI.
Raw links
KodikAPI exposes the same queries for an arbitrary media or player link:
Result<MediaMetadata> metadata = api.metadataQuery("https://.../player-link")
.preferTranslation(158)
.preferQuality(Quality.SD)
.preferEpisode(4)
.execute();
Result<VideoLinks> links = api.videoLinksQuery("https://.../player-link")
.preferQuality(Quality.HD)
.execute();
KodikAPI.metadata(link) is a shortcut that executes a metadata query without
preferences.
Preferences
When no translation is preferred, the player link is used as-is. When
translations are preferred, the page metadata is fetched and the player URL is
rebuilt from the first preferred translation available on the page as
baseUrl/{mediaType}/{mediaId}/{mediaHash}/{quality}. Quality defaults to
Quality.HD (720p) and the episode to 1.
Result models
MediaMetadata—getTitle(),getTranslations()(available translations asKodikTranslationwithid,mediaId,mediaHash,mediaType, ...), andgetEpisodes().VideoLinks—getLinks()returns the extracted links asMap<String, List<VideoLinks.Link>>; eachLinkhasgetSrc()(decoded, normalized direct URL) andgetType().
Contributing
If you'd like to contribute, please fork the repository and use a feature branch. Pull requests are warmly welcome.
Any value documented by the Kodik API that is missing
from the request models or enums is a good first contribution: add it, keep the
builders generated (no manual builder code), and run mvn clean install.
Links
- Project homepage: https://git.bivashy.dev/anyame/kodik-api
- Repository:
git@git.bivashy.dev:anyame/kodik-api.git - Kodik API: https://kodik-api.com
- Related projects:
- record-builder: https://github.com/randgalt/record-builder
- EasyRetrofit (Quarkus): https://github.com/quarkiverse/quarkus-easy-retrofit
Licensing
The code in this project is licensed under the GNU Lesser General Public License, version 3 (or, at your option, any later version). See the LICENSE file for the full license text.
The LGPL lets applications and libraries link against this project without requiring the whole combined work to be released under the same copyleft terms — good fit for a library. Modifications to the library itself must stay LGPL. The library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU LGPL for more details.