From 9f97d88f813c35903a9711049918eefca1d91baf Mon Sep 17 00:00:00 2001 From: Geoff Bourne Date: Sun, 13 Oct 2024 13:13:14 -0500 Subject: [PATCH] curseforge: cache get mod info and file API calls to disk --- .../curseforge/CurseForgeApiClient.java | 69 +- .../curseforge/CurseForgeFilesCommand.java | 18 +- .../curseforge/CurseForgeInstaller.java | 23 +- .../curseforge/InstallCurseForgeCommand.java | 6 +- .../me/itzg/helpers/files/ApiCaching.java | 11 + .../me/itzg/helpers/files/ApiCachingImpl.java | 152 ++ .../me/itzg/helpers/files/CacheIndex.java | 17 + .../helpers/files/DisabledApiCaching.java | 17 + .../curseforge/CurseForgeApiClientTest.java | 3 +- .../curseforge/ModFileRefResolverTest.java | 3 +- .../curseforge/mappings/v1_mods_238222.json | 1592 +++++++++++++++++ .../curseforge/mappings/v1_mods_31054.json | 470 +++++ .../curseforge/mappings/v1_mods_667389.json | 253 +++ 13 files changed, 2587 insertions(+), 47 deletions(-) create mode 100644 src/main/java/me/itzg/helpers/files/ApiCaching.java create mode 100644 src/main/java/me/itzg/helpers/files/ApiCachingImpl.java create mode 100644 src/main/java/me/itzg/helpers/files/CacheIndex.java create mode 100644 src/main/java/me/itzg/helpers/files/DisabledApiCaching.java create mode 100644 src/test/resources/curseforge/mappings/v1_mods_238222.json create mode 100644 src/test/resources/curseforge/mappings/v1_mods_31054.json create mode 100644 src/test/resources/curseforge/mappings/v1_mods_667389.json diff --git a/src/main/java/me/itzg/helpers/curseforge/CurseForgeApiClient.java b/src/main/java/me/itzg/helpers/curseforge/CurseForgeApiClient.java index 622fc5d3..e7233427 100644 --- a/src/main/java/me/itzg/helpers/curseforge/CurseForgeApiClient.java +++ b/src/main/java/me/itzg/helpers/curseforge/CurseForgeApiClient.java @@ -9,7 +9,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import lombok.extern.slf4j.Slf4j; import me.itzg.helpers.curseforge.model.Category; import me.itzg.helpers.curseforge.model.CurseForgeFile; @@ -22,6 +21,7 @@ import me.itzg.helpers.curseforge.model.ModsSearchResponse; import me.itzg.helpers.errors.GenericException; import me.itzg.helpers.errors.InvalidParameterException; +import me.itzg.helpers.files.ApiCaching; import me.itzg.helpers.http.FailedRequestException; import me.itzg.helpers.http.Fetch; import me.itzg.helpers.http.FileDownloadStatusHandler; @@ -35,6 +35,8 @@ @Slf4j public class CurseForgeApiClient implements AutoCloseable { + public static final String CACHING_NAMESPACE = "curseforge"; + public static final String CATEGORY_MODPACKS = "modpacks"; public static final String CATEGORY_MC_MODS = "mc-mods"; public static final String CATEGORY_BUKKIT_PLUGINS = "bukkit-plugins"; @@ -48,10 +50,12 @@ public class CurseForgeApiClient implements AutoCloseable { private final UriBuilder downloadFallbackUriBuilder; private final String gameId; - private final ConcurrentHashMap cachedMods = new ConcurrentHashMap<>(); + private final ApiCaching apiCaching; - public CurseForgeApiClient(String apiBaseUrl, String apiKey, SharedFetch.Options sharedFetchOptions, String gameId + public CurseForgeApiClient(String apiBaseUrl, String apiKey, SharedFetch.Options sharedFetchOptions, String gameId, + ApiCaching apiCaching ) { + this.apiCaching = apiCaching; if (apiKey == null || apiKey.trim().isEmpty()) { throw new InvalidParameterException("CurseForge API key is required"); } @@ -128,8 +132,7 @@ else if (searchResponse.getData().size() > 1) { else { return Mono.just(searchResponse.getData().get(0)); } - }) - .doOnNext(curseForgeMod -> cachedMods.put(curseForgeMod.getId(), curseForgeMod)); + }); } /** @@ -184,19 +187,16 @@ public Mono getModInfo( ) { log.debug("Getting mod metadata for {}", projectID); - final CurseForgeMod cached = cachedMods.get(projectID); - if (cached != null) { - return Mono.just(cached); - } - - return preparedFetch.fetch( - uriBuilder.resolve("/v1/mods/{modId}", projectID) - ) - .toObject(GetModResponse.class) - .assemble() - .checkpoint("Getting mod info for " + projectID) - .map(GetModResponse::getData) - .doOnNext(curseForgeMod -> cachedMods.put(curseForgeMod.getId(), curseForgeMod)); + return apiCaching.cache("getModInfo", CurseForgeMod.class, + preparedFetch.fetch( + uriBuilder.resolve("/v1/mods/{modId}", projectID) + ) + .toObject(GetModResponse.class) + .assemble() + .checkpoint("Getting mod info for " + projectID) + .map(GetModResponse::getData), + projectID + ); } public Mono getModFileInfo( @@ -204,22 +204,25 @@ public Mono getModFileInfo( ) { log.debug("Getting mod file metadata for {}:{}", projectID, fileID); - return preparedFetch.fetch( - uriBuilder.resolve("/v1/mods/{modId}/files/{fileId}", projectID, fileID) - ) - .toObject(GetModFileResponse.class) - .assemble() - .onErrorMap(FailedRequestException.class::isInstance, e -> { - final FailedRequestException fre = (FailedRequestException) e; - if (fre.getStatusCode() == 400) { - if (isNotFoundResponse(fre.getBody())) { - return new InvalidParameterException("Requested file not found for modpack", e); + return apiCaching.cache("getModFileInfo", CurseForgeFile.class, + preparedFetch.fetch( + uriBuilder.resolve("/v1/mods/{modId}/files/{fileId}", projectID, fileID) + ) + .toObject(GetModFileResponse.class) + .assemble() + .onErrorMap(FailedRequestException.class::isInstance, e -> { + final FailedRequestException fre = (FailedRequestException) e; + if (fre.getStatusCode() == 400) { + if (isNotFoundResponse(fre.getBody())) { + return new InvalidParameterException("Requested file not found for modpack", e); + } } - } - return e; - }) - .map(GetModFileResponse::getData) - .checkpoint(); + return e; + }) + .map(GetModFileResponse::getData) + .checkpoint(), + projectID, fileID + ); } public Mono download(CurseForgeFile cfFile, Path outputFile, FileDownloadStatusHandler handler) { diff --git a/src/main/java/me/itzg/helpers/curseforge/CurseForgeFilesCommand.java b/src/main/java/me/itzg/helpers/curseforge/CurseForgeFilesCommand.java index 4d1fdd40..0e120559 100644 --- a/src/main/java/me/itzg/helpers/curseforge/CurseForgeFilesCommand.java +++ b/src/main/java/me/itzg/helpers/curseforge/CurseForgeFilesCommand.java @@ -24,6 +24,9 @@ import me.itzg.helpers.curseforge.model.ModLoaderType; import me.itzg.helpers.errors.GenericException; import me.itzg.helpers.errors.InvalidParameterException; +import me.itzg.helpers.files.ApiCaching; +import me.itzg.helpers.files.ApiCachingImpl; +import me.itzg.helpers.files.DisabledApiCaching; import me.itzg.helpers.files.Manifests; import me.itzg.helpers.http.SharedFetchArgs; import org.jetbrains.annotations.NotNull; @@ -93,6 +96,9 @@ public void setSlugCategory(String defaultCategory) { ) ModLoaderType modLoaderType; + @Option(names = "--disable-api-caching", defaultValue = "${env:CF_DISABLE_API_CACHING:-false}") + boolean disableApiCaching; + @ArgGroup(exclusive = false) SharedFetchArgs sharedFetchArgs = new SharedFetchArgs(); @@ -116,10 +122,14 @@ public Integer call() throws Exception { final CurseForgeFilesManifest newManifest; if (modFileRefs != null && !modFileRefs.isEmpty()) { - try (CurseForgeApiClient apiClient = new CurseForgeApiClient( - apiBaseUrl, apiKey, sharedFetchArgs.options(), - CurseForgeApiClient.MINECRAFT_GAME_ID - )) { + try ( + final ApiCaching apiCaching = disableApiCaching ? new DisabledApiCaching() : new ApiCachingImpl(outputDir, CACHING_NAMESPACE); + final CurseForgeApiClient apiClient = new CurseForgeApiClient( + apiBaseUrl, apiKey, sharedFetchArgs.options(), + CurseForgeApiClient.MINECRAFT_GAME_ID, + apiCaching + ) + ) { newManifest = apiClient.loadCategoryInfo(Arrays.asList(CATEGORY_MC_MODS, CATEGORY_BUKKIT_PLUGINS)) .flatMap(categoryInfo -> processModFileRefs(categoryInfo, previousFiles, apiClient) diff --git a/src/main/java/me/itzg/helpers/curseforge/CurseForgeInstaller.java b/src/main/java/me/itzg/helpers/curseforge/CurseForgeInstaller.java index c981e0ca..ba8a396b 100644 --- a/src/main/java/me/itzg/helpers/curseforge/CurseForgeInstaller.java +++ b/src/main/java/me/itzg/helpers/curseforge/CurseForgeInstaller.java @@ -3,6 +3,7 @@ import static java.util.Collections.emptySet; import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; +import static me.itzg.helpers.curseforge.CurseForgeApiClient.CACHING_NAMESPACE; import static me.itzg.helpers.curseforge.CurseForgeApiClient.modFileDownloadStatusHandler; import static me.itzg.helpers.singles.MoreCollections.safeStreamFrom; @@ -44,6 +45,9 @@ import me.itzg.helpers.errors.InvalidParameterException; import me.itzg.helpers.errors.RateLimitException; import me.itzg.helpers.fabric.FabricLauncherInstaller; +import me.itzg.helpers.files.ApiCaching; +import me.itzg.helpers.files.ApiCachingImpl; +import me.itzg.helpers.files.DisabledApiCaching; import me.itzg.helpers.files.Manifests; import me.itzg.helpers.files.ResultsFileWriter; import me.itzg.helpers.forge.ForgeInstaller; @@ -127,10 +131,12 @@ public class CurseForgeInstaller { @Getter @Setter private List ignoreMissingFiles; + @Getter @Setter + private boolean disableApiCaching; + /** - * @throws MissingModsException if any mods need to be manually downloaded */ - public void installFromModpackZip(Path modpackZip, String slug) throws IOException { + public void installFromModpackZip(Path modpackZip, String slug) { requireNonNull(modpackZip, "modpackZip is required"); install(slug, context -> { @@ -147,9 +153,8 @@ public void installFromModpackZip(Path modpackZip, String slug) throws IOExcepti } /** - * @throws MissingModsException if any mods need to be manually downloaded */ - public void installFromModpackManifest(String modpackManifestLoc, String slug) throws IOException { + public void installFromModpackManifest(String modpackManifestLoc, String slug) { requireNonNull(modpackManifestLoc, "modpackManifest is required"); install(slug, context -> { @@ -184,7 +189,7 @@ public void install(String slug, String fileMatcher, Integer fileId) throws IOEx ); } - void install(String slug, InstallationEntryPoint entryPoint) throws IOException { + void install(String slug, InstallationEntryPoint entryPoint) { requireNonNull(outputDir, "outputDir is required"); requireNonNull(slug); requireNonNull(entryPoint); @@ -209,9 +214,11 @@ void install(String slug, InstallationEntryPoint entryPoint) throws IOException } try ( - CurseForgeApiClient cfApi = new CurseForgeApiClient( + final ApiCaching apiCaching = disableApiCaching ? new DisabledApiCaching() : new ApiCachingImpl(outputDir, CACHING_NAMESPACE); + final CurseForgeApiClient cfApi = new CurseForgeApiClient( apiBaseUrl, apiKey, sharedFetchOptions, - CurseForgeApiClient.MINECRAFT_GAME_ID + CurseForgeApiClient.MINECRAFT_GAME_ID, + apiCaching ) ) { final CategoryInfo categoryInfo = cfApi.loadCategoryInfo(applicableClassIdSlugs) @@ -238,6 +245,8 @@ void install(String slug, InstallationEntryPoint entryPoint) throws IOException else { throw e; } + } catch (IOException e) { + throw new GenericException("Failed to setup API caching", e); } } diff --git a/src/main/java/me/itzg/helpers/curseforge/InstallCurseForgeCommand.java b/src/main/java/me/itzg/helpers/curseforge/InstallCurseForgeCommand.java index 5d26aef9..0170b477 100644 --- a/src/main/java/me/itzg/helpers/curseforge/InstallCurseForgeCommand.java +++ b/src/main/java/me/itzg/helpers/curseforge/InstallCurseForgeCommand.java @@ -160,6 +160,9 @@ static class Listed { @Option(names = "--missing-mods-filename", defaultValue = "MODS_NEED_DOWNLOAD.txt") String missingModsFilename; + @Option(names = "--disable-api-caching", defaultValue = "${env:CF_DISABLE_API_CACHING:-false}") + boolean disableApiCaching; + @Override public Integer call() throws Exception { // https://www.curseforge.com/minecraft/modpacks/all-the-mods-8/files @@ -194,7 +197,8 @@ public Integer call() throws Exception { .setOverridesExclusions(overridesExclusions) .setSharedFetchOptions(sharedFetchArgs.options()) .setApiKey(apiKey) - .setDownloadsRepo(downloadsRepo); + .setDownloadsRepo(downloadsRepo) + .setDisableApiCaching(disableApiCaching); if (apiBaseUrl != null) { installer.setApiBaseUrl(apiBaseUrl); diff --git a/src/main/java/me/itzg/helpers/files/ApiCaching.java b/src/main/java/me/itzg/helpers/files/ApiCaching.java new file mode 100644 index 00000000..40a229ae --- /dev/null +++ b/src/main/java/me/itzg/helpers/files/ApiCaching.java @@ -0,0 +1,11 @@ +package me.itzg.helpers.files; + +import java.io.IOException; +import reactor.core.publisher.Mono; + +public interface ApiCaching extends AutoCloseable { + + Mono cache(String operation, Class returnType, Mono resolver, Object... keys); + + void close() throws IOException; +} diff --git a/src/main/java/me/itzg/helpers/files/ApiCachingImpl.java b/src/main/java/me/itzg/helpers/files/ApiCachingImpl.java new file mode 100644 index 00000000..7de82221 --- /dev/null +++ b/src/main/java/me/itzg/helpers/files/ApiCachingImpl.java @@ -0,0 +1,152 @@ +package me.itzg.helpers.files; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.time.Instant; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import me.itzg.helpers.files.CacheIndex.CacheEntry; +import me.itzg.helpers.json.ObjectMappers; +import org.jetbrains.annotations.Blocking; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +@Slf4j +public class ApiCachingImpl implements ApiCaching { + + private static final String CACHE_SUBIDR = ".cache"; + private static final String CACHE_INDEX_FILENAME = "cache-index.json"; + private final ObjectMapper objectMapper; + private final CacheIndex cacheIndex; + private final Path cacheNamespaceDir; + + @Setter + private Duration cacheDuration = Duration.ofHours(24); + + @Blocking + public ApiCachingImpl(Path outputDirectory, String namespace) throws IOException { + objectMapper = ObjectMappers.defaultMapper(); + cacheNamespaceDir = outputDirectory.resolve(CACHE_SUBIDR).resolve(namespace); + cacheIndex = loadCacheIndex(); + pruneExpiredEntries(); + } + + private void pruneExpiredEntries() { + final Instant now = Instant.now(); + + cacheIndex.getOperations().forEach((operation, entryMap) -> { + final Iterator> entries = entryMap.entrySet().iterator(); + while (entries.hasNext()) { + final CacheEntry entry = entries.next().getValue(); + if (entry.getExpiresAt().isBefore(now)) { + final Path contentFile = resolveContentFile(operation, entry.getFilename()); + try { + log.debug("Pruning cached content file {}", contentFile); + Files.delete(contentFile); + } catch (IOException e) { + log.warn("Failed to delete cached content file {}", contentFile, e); + } + entries.remove(); + } + } + }); + } + + private Path resolveContentFile(String operation, String filename) { + return cacheNamespaceDir.resolve(operation).resolve(filename); + } + + private CacheIndex loadCacheIndex() throws IOException { + final Path cacheIndexPath = cacheNamespaceDir.resolve(CACHE_INDEX_FILENAME); + if (Files.exists(cacheIndexPath)) { + log.debug("Loading cache index from {}", cacheIndexPath); + return objectMapper.readValue(cacheIndexPath.toFile(), CacheIndex.class); + } + else { + return new CacheIndex(); + } + } + + @Override + public Mono cache(String operation, Class returnType, Mono resolver, Object... keys) { + final String keysKey = Stream.of(keys) + .map(Object::toString) + .collect(Collectors.joining(",")); + + return Mono.fromCallable(() -> { + synchronized (cacheIndex) { + final Map entryMap = cacheIndex.getOperations().get(operation); + if (entryMap != null) { + return entryMap.get(keysKey); + } + return null; + } + }) + .flatMap(entry -> loadFromCache(operation, keysKey, entry, returnType)) + .switchIfEmpty( + resolver + .flatMap(r -> saveToCache(operation, keysKey, r)) + ); + } + + private Mono saveToCache(String operation, String keys, R value) { + + return Mono.fromCallable(() -> { + final String filename = UUID.randomUUID() + ".json"; + try { + final Path contentFile = Files.createDirectories(cacheNamespaceDir.resolve(operation)) + .resolve(filename); + + objectMapper.writeValue(contentFile.toFile(), value); + synchronized (cacheIndex) { + cacheIndex.getOperations().computeIfAbsent(operation, s -> new HashMap<>()) + .put(keys, new CacheEntry() + .setExpiresAt(Instant.now().plus(cacheDuration)) + .setFilename(filename) + ); + } + + log.debug("Saved cache content of {}({}) to {}", operation, keys, contentFile); + } catch (IOException e) { + log.warn("Failed to cache file for operation={} keys={}", operation, keys, e); + } + + return value; + + }) + .subscribeOn(Schedulers.boundedElastic()); + } + + private Mono loadFromCache(String operation, String keys, CacheEntry entry, Class returnType) { + + return Mono.fromCallable(() -> { + final Path contentFile = resolveContentFile(operation, entry.getFilename()); + if (Files.exists(contentFile)) { + log.debug("Loading cached content of {}({}) from {}", operation, keys, contentFile); + return objectMapper.readValue(contentFile.toFile(), returnType); + } + else { + return null; + } + }) + .subscribeOn(Schedulers.boundedElastic()); + } + + @Override + @Blocking + public void close() throws IOException { + Files.createDirectories(cacheNamespaceDir); + final Path cacheIndexFile = cacheNamespaceDir.resolve(CACHE_INDEX_FILENAME); + log.debug("Saving cache index to {}", cacheIndexFile); + objectMapper.writeValue(cacheIndexFile.toFile(), cacheIndex); + } +} diff --git a/src/main/java/me/itzg/helpers/files/CacheIndex.java b/src/main/java/me/itzg/helpers/files/CacheIndex.java new file mode 100644 index 00000000..4414cc2a --- /dev/null +++ b/src/main/java/me/itzg/helpers/files/CacheIndex.java @@ -0,0 +1,17 @@ +package me.itzg.helpers.files; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import lombok.Data; + +@Data +public class CacheIndex { + private Map> operations = new HashMap<>(); + + @Data + public static class CacheEntry { + private String filename; + private Instant expiresAt; + } +} diff --git a/src/main/java/me/itzg/helpers/files/DisabledApiCaching.java b/src/main/java/me/itzg/helpers/files/DisabledApiCaching.java new file mode 100644 index 00000000..ecc439d5 --- /dev/null +++ b/src/main/java/me/itzg/helpers/files/DisabledApiCaching.java @@ -0,0 +1,17 @@ +package me.itzg.helpers.files; + +import java.io.IOException; +import reactor.core.publisher.Mono; + +public class DisabledApiCaching implements ApiCaching { + + @Override + public Mono cache(String operation, Class returnType, Mono resolver, Object... keys) { + return resolver; + } + + @Override + public void close() throws IOException { + + } +} diff --git a/src/test/java/me/itzg/helpers/curseforge/CurseForgeApiClientTest.java b/src/test/java/me/itzg/helpers/curseforge/CurseForgeApiClientTest.java index 27f86420..8ac6b392 100644 --- a/src/test/java/me/itzg/helpers/curseforge/CurseForgeApiClientTest.java +++ b/src/test/java/me/itzg/helpers/curseforge/CurseForgeApiClientTest.java @@ -6,6 +6,7 @@ import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; import java.util.Collections; +import me.itzg.helpers.files.DisabledApiCaching; import me.itzg.helpers.http.SharedFetch.Options; import org.intellij.lang.annotations.Language; import org.junit.jupiter.api.Test; @@ -25,7 +26,7 @@ void apiKeyHeaderIsTrimmed(WireMockRuntimeInfo wmInfo) { final CategoryInfo result; try (CurseForgeApiClient client = new CurseForgeApiClient(wmInfo.getHttpBaseUrl(), "key\n", Options.builder().build(), - "test" + "test", new DisabledApiCaching() )) { result = client.loadCategoryInfo(Collections.singleton("mc-mods")) .block(); diff --git a/src/test/java/me/itzg/helpers/curseforge/ModFileRefResolverTest.java b/src/test/java/me/itzg/helpers/curseforge/ModFileRefResolverTest.java index 7d353c21..1a656109 100644 --- a/src/test/java/me/itzg/helpers/curseforge/ModFileRefResolverTest.java +++ b/src/test/java/me/itzg/helpers/curseforge/ModFileRefResolverTest.java @@ -12,6 +12,7 @@ import java.util.stream.Stream; import me.itzg.helpers.curseforge.model.CurseForgeFile; import me.itzg.helpers.curseforge.model.ModLoaderType; +import me.itzg.helpers.files.DisabledApiCaching; import me.itzg.helpers.http.SharedFetch.Options; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; @@ -52,7 +53,7 @@ public static Stream testMcModsRefsArgs() { void testMcModsRefs(String ref, String defaultCategory, String gameVersion, ModLoaderType modLoader, ModFileIds expected) { final CurseForgeApiClient apiClient = new CurseForgeApiClient(wm.baseUrl(), "testing", Options.builder().build(), - CurseForgeApiClient.MINECRAFT_GAME_ID + CurseForgeApiClient.MINECRAFT_GAME_ID, new DisabledApiCaching() ); final CategoryInfo categoryInfo = apiClient.loadCategoryInfo( diff --git a/src/test/resources/curseforge/mappings/v1_mods_238222.json b/src/test/resources/curseforge/mappings/v1_mods_238222.json new file mode 100644 index 00000000..9727d2cd --- /dev/null +++ b/src/test/resources/curseforge/mappings/v1_mods_238222.json @@ -0,0 +1,1592 @@ +{ + "request" : { + "url" : "/v1/mods/238222", + "method" : "GET" + }, + "response" : { + "status" : 200, + "jsonBody" : { + "data": { + "id": 238222, + "gameId": 432, + "name": "Just Enough Items (JEI)", + "slug": "jei", + "links": { + "websiteUrl": "https://www.curseforge.com/minecraft/mc-mods/jei", + "wikiUrl": "", + "issuesUrl": "https://github.com/mezz/JustEnoughItems/issues?q=is%3Aissue", + "sourceUrl": "https://github.com/mezz/JustEnoughItems" + }, + "summary": "View Items and Recipes", + "status": 4, + "downloadCount": 364546645, + "isFeatured": false, + "primaryCategoryId": 423, + "categories": [ + { + "id": 421, + "gameId": 432, + "name": "API and Library", + "slug": "library-api", + "url": "https://www.curseforge.com/minecraft/mc-mods/library-api", + "iconUrl": "https://media.forgecdn.net/avatars/6/36/635351496947765531.png", + "dateModified": "2014-05-23T03:21:44.06Z", + "isClass": false, + "classId": 6, + "parentCategoryId": 6 + }, + { + "id": 423, + "gameId": 432, + "name": "Map and Information", + "slug": "map-information", + "url": "https://www.curseforge.com/minecraft/mc-mods/map-information", + "iconUrl": "https://media.forgecdn.net/avatars/6/38/635351497437388438.png", + "dateModified": "2014-05-08T17:42:23.74Z", + "isClass": false, + "classId": 6, + "parentCategoryId": 6 + } + ], + "classId": 6, + "authors": [ + { + "id": 17072262, + "name": "mezz", + "url": "https://www.curseforge.com/members/mezz", + "avatarUrl": "https://static-cdn.jtvnw.net/jtv_user_pictures/ce4a8a5fe354959b-profile_image-150x150.png" + } + ], + "logo": { + "id": 29069, + "modId": 238222, + "title": "635838945588716414.jpeg", + "description": "", + "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/29/69/256/256/635838945588716414.jpeg", + "url": "https://media.forgecdn.net/avatars/29/69/635838945588716414.jpeg" + }, + "screenshots": [ + { + "id": 31417, + "modId": 238222, + "title": "Recipe Completion", + "description": "", + "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/31/417/310/172/thzzdin.png", + "url": "https://media.forgecdn.net/attachments/31/417/thzzdin.png" + }, + { + "id": 31419, + "modId": 238222, + "title": "Potions", + "description": "", + "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/31/419/310/172/t7f7jh6.png", + "url": "https://media.forgecdn.net/attachments/31/419/t7f7jh6.png" + }, + { + "id": 31420, + "modId": 238222, + "title": "Itemlist Edit Mode", + "description": "", + "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/31/420/310/172/tgafkma.png", + "url": "https://media.forgecdn.net/attachments/31/420/tgafkma.png" + }, + { + "id": 31418, + "modId": 238222, + "title": "Big Screen Support", + "description": "", + "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/31/418/310/172/9lngh5f.png", + "url": "https://media.forgecdn.net/attachments/31/418/9lngh5f.png" + } + ], + "mainFileId": 5626488, + "latestFiles": [ + { + "id": 3040523, + "gameId": 432, + "modId": 238222, + "isAvailable": true, + "displayName": "jei_1.12.2-4.16.1.301.jar", + "fileName": "jei_1.12.2-4.16.1.301.jar", + "releaseType": 1, + "fileStatus": 4, + "hashes": [ + { + "value": "3045e8440ea44071d8b83c4e7b3c190348fdc527", + "algo": 1 + }, + { + "value": "1dee4be93d666e2228039c551e927b35", + "algo": 2 + } + ], + "fileDate": "2020-08-24T01:01:39.123Z", + "fileLength": 653211, + "downloadCount": 26341234, + "downloadUrl": "https://edge.forgecdn.net/files/3040/523/jei_1.12.2-4.16.1.301.jar", + "gameVersions": [ + "1.12.2" + ], + "sortableGameVersions": [ + { + "gameVersionName": "1.12.2", + "gameVersionPadded": "0000000001.0000000012.0000000002", + "gameVersion": "1.12.2", + "gameVersionReleaseDate": "2017-09-18T05:00:00Z", + "gameVersionTypeId": 628 + } + ], + "dependencies": [], + "alternateFileId": 0, + "isServerPack": false, + "fileFingerprint": 3089143260, + "modules": [ + { + "name": "META-INF", + "fingerprint": 2236405288 + }, + { + "name": "mezz", + "fingerprint": 2222830911 + }, + { + "name": "pack.mcmeta", + "fingerprint": 1488642189 + }, + { + "name": "mcmod.info", + "fingerprint": 3528499262 + }, + { + "name": "assets", + "fingerprint": 9943101 + } + ] + }, + { + "id": 3272039, + "gameId": 432, + "modId": 238222, + "isAvailable": true, + "displayName": "jei-1.13.2-5.0.0.31.jar", + "fileName": "jei-1.13.2-5.0.0.31.jar", + "releaseType": 3, + "fileStatus": 4, + "hashes": [ + { + "value": "aa15cdea079db8b91d75e3c68216df80a70545d8", + "algo": 1 + }, + { + "value": "1ee1f4fb4c6e199c02c7d15cbd0d2c8a", + "algo": 2 + } + ], + "fileDate": "2021-04-11T03:49:47.687Z", + "fileLength": 690802, + "downloadCount": 28655, + "downloadUrl": "https://edge.forgecdn.net/files/3272/39/jei-1.13.2-5.0.0.31.jar", + "gameVersions": [ + "1.13.2" + ], + "sortableGameVersions": [ + { + "gameVersionName": "1.13.2", + "gameVersionPadded": "0000000001.0000000013.0000000002", + "gameVersion": "1.13.2", + "gameVersionReleaseDate": "2018-10-22T00:00:00Z", + "gameVersionTypeId": 55023 + } + ], + "dependencies": [], + "alternateFileId": 0, + "isServerPack": false, + "fileFingerprint": 2700304635, + "modules": [ + { + "name": "META-INF", + "fingerprint": 1102858494 + }, + { + "name": "mezz", + "fingerprint": 2811918946 + }, + { + "name": "pack.mcmeta", + "fingerprint": 3652707984 + }, + { + "name": "assets", + "fingerprint": 88833534 + } + ] + }, + { + "id": 4087656, + "gameId": 432, + "modId": 238222, + "isAvailable": true, + "displayName": "jei-1.19.2-fabric-11.4.0.286.jar", + "fileName": "jei-1.19.2-fabric-11.4.0.286.jar", + "releaseType": 3, + "fileStatus": 4, + "hashes": [ + { + "value": "f4c77ecd8b897a12c2c8a26350d93a93322a8bcd", + "algo": 1 + }, + { + "value": "cbf23483d172a38b71419100e227f017", + "algo": 2 + } + ], + "fileDate": "2022-11-15T02:14:24.907Z", + "fileLength": 1068892, + "downloadCount": 90125, + "downloadUrl": "https://edge.forgecdn.net/files/4087/656/jei-1.19.2-fabric-11.4.0.286.jar", + "gameVersions": [ + "Fabric", + "1.19.2" + ], + "sortableGameVersions": [ + { + "gameVersionName": "Fabric", + "gameVersionPadded": "0", + "gameVersion": "", + "gameVersionReleaseDate": "2022-09-01T00:00:00Z", + "gameVersionTypeId": 68441 + }, + { + "gameVersionName": "1.19.2", + "gameVersionPadded": "0000000001.0000000019.0000000002", + "gameVersion": "1.19.2", + "gameVersionReleaseDate": "2022-08-05T14:12:22.413Z", + "gameVersionTypeId": 73407 + } + ], + "dependencies": [], + "alternateFileId": 0, + "isServerPack": false, + "fileFingerprint": 1613607509, + "modules": [ + { + "name": "META-INF", + "fingerprint": 2822576509 + }, + { + "name": "jei.accesswidener", + "fingerprint": 3441454662 + }, + { + "name": "assets", + "fingerprint": 224894697 + }, + { + "name": "fabric.mod.json", + "fingerprint": 1230897332 + }, + { + "name": "pack.mcmeta", + "fingerprint": 1550930300 + }, + { + "name": "jei.mixins.json", + "fingerprint": 623960849 + }, + { + "name": "jei-icon.png", + "fingerprint": 2007185424 + }, + { + "name": "jei-1.19.2-fabric-refmap.json", + "fingerprint": 1412800118 + }, + { + "name": "mezz", + "fingerprint": 2346665333 + } + ] + }, + { + "id": 4087658, + "gameId": 432, + "modId": 238222, + "isAvailable": true, + "displayName": "jei-1.19.2-forge-11.4.0.286.jar", + "fileName": "jei-1.19.2-forge-11.4.0.286.jar", + "releaseType": 3, + "fileStatus": 4, + "hashes": [ + { + "value": "3bab715ae0f56e1b9a3e1ebfb5e9bb3f677e3711", + "algo": 1 + }, + { + "value": "0d458f02d611eafbf29ae36010d03130", + "algo": 2 + } + ], + "fileDate": "2022-11-15T02:14:49.103Z", + "fileLength": 1046401, + "downloadCount": 662161, + "downloadUrl": "https://edge.forgecdn.net/files/4087/658/jei-1.19.2-forge-11.4.0.286.jar", + "gameVersions": [ + "1.19.2", + "Forge" + ], + "sortableGameVersions": [ + { + "gameVersionName": "1.19.2", + "gameVersionPadded": "0000000001.0000000019.0000000002", + "gameVersion": "1.19.2", + "gameVersionReleaseDate": "2022-08-05T14:12:22.413Z", + "gameVersionTypeId": 73407 + }, + { + "gameVersionName": "Forge", + "gameVersionPadded": "0", + "gameVersion": "", + "gameVersionReleaseDate": "2022-10-01T00:00:00Z", + "gameVersionTypeId": 68441 + } + ], + "dependencies": [], + "alternateFileId": 0, + "isServerPack": false, + "fileFingerprint": 4135756105, + "modules": [ + { + "name": "META-INF", + "fingerprint": 2624845708 + }, + { + "name": "mezz", + "fingerprint": 1839722990 + }, + { + "name": "jei-icon.png", + "fingerprint": 2007185424 + }, + { + "name": "pack.mcmeta", + "fingerprint": 1550930300 + }, + { + "name": "assets", + "fingerprint": 224894697 + } + ] + }, + { + "id": 5060339, + "gameId": 432, + "modId": 238222, + "isAvailable": true, + "displayName": "jei_1.12.2-4.16.1.1004.jar", + "fileName": "jei_1.12.2-4.16.1.1004.jar", + "releaseType": 2, + "fileStatus": 4, + "hashes": [ + { + "value": "f64479688b83762482f8675b3a23223389e6c693", + "algo": 1 + }, + { + "value": "84c1540e5888f2af4c53709f6649ed2d", + "algo": 2 + } + ], + "fileDate": "2024-01-26T06:10:11.413Z", + "fileLength": 654181, + "downloadCount": 84051, + "downloadUrl": "https://edge.forgecdn.net/files/5060/339/jei_1.12.2-4.16.1.1004.jar", + "gameVersions": [ + "1.12.2" + ], + "sortableGameVersions": [ + { + "gameVersionName": "1.12.2", + "gameVersionPadded": "0000000001.0000000012.0000000002", + "gameVersion": "1.12.2", + "gameVersionReleaseDate": "2017-09-18T05:00:00Z", + "gameVersionTypeId": 628 + } + ], + "dependencies": [], + "alternateFileId": 0, + "isServerPack": false, + "fileFingerprint": 2559345639, + "modules": [ + { + "name": "META-INF", + "fingerprint": 3367829501 + }, + { + "name": "mezz", + "fingerprint": 2795317040 + }, + { + "name": "pack.mcmeta", + "fingerprint": 1488642189 + }, + { + "name": "assets", + "fingerprint": 2844848637 + }, + { + "name": "mcmod.info", + "fingerprint": 718309853 + } + ] + }, + { + "id": 5623550, + "gameId": 432, + "modId": 238222, + "isAvailable": true, + "displayName": "jei-1.21.1-neoforge-19.8.4.113.jar", + "fileName": "jei-1.21.1-neoforge-19.8.4.113.jar", + "releaseType": 1, + "fileStatus": 4, + "hashes": [ + { + "value": "adccdae2c69e715e113a92fb4173d2a5e816357f", + "algo": 1 + }, + { + "value": "6b1f4e58eeee0c800c696e55d117dcd7", + "algo": 2 + } + ], + "fileDate": "2024-08-13T05:58:36.98Z", + "fileLength": 1317845, + "downloadCount": 49971, + "fileSizeOnDisk": 3033047, + "downloadUrl": "https://edge.forgecdn.net/files/5623/550/jei-1.21.1-neoforge-19.8.4.113.jar", + "gameVersions": [ + "NeoForge", + "1.21.1" + ], + "sortableGameVersions": [ + { + "gameVersionName": "NeoForge", + "gameVersionPadded": "0", + "gameVersion": "", + "gameVersionReleaseDate": "2023-07-25T00:00:00Z", + "gameVersionTypeId": 68441 + }, + { + "gameVersionName": "1.21.1", + "gameVersionPadded": "0000000001.0000000021.0000000001", + "gameVersion": "1.21.1", + "gameVersionReleaseDate": "2024-08-08T15:17:53.787Z", + "gameVersionTypeId": 77784 + } + ], + "dependencies": [], + "alternateFileId": 0, + "isServerPack": false, + "fileFingerprint": 4239410058, + "modules": [ + { + "name": "META-INF", + "fingerprint": 2431960955 + }, + { + "name": "mezz", + "fingerprint": 27293395 + }, + { + "name": "jei-icon.png", + "fingerprint": 2007185424 + }, + { + "name": "pack.mcmeta", + "fingerprint": 2606738017 + }, + { + "name": "assets", + "fingerprint": 1130732814 + } + ] + }, + { + "id": 5626487, + "gameId": 432, + "modId": 238222, + "isAvailable": true, + "displayName": "jei-1.20.1-fabric-15.12.2.51.jar", + "fileName": "jei-1.20.1-fabric-15.12.2.51.jar", + "releaseType": 1, + "fileStatus": 4, + "hashes": [ + { + "value": "9027f0dd6be9c71ba4f1e5b7dab0564c3e8932b7", + "algo": 1 + }, + { + "value": "71a3effbcb74836cbe486126ee8b26a5", + "algo": 2 + } + ], + "fileDate": "2024-08-14T00:54:38.917Z", + "fileLength": 1372227, + "downloadCount": 76903, + "fileSizeOnDisk": 2973624, + "downloadUrl": "https://edge.forgecdn.net/files/5626/487/jei-1.20.1-fabric-15.12.2.51.jar", + "gameVersions": [ + "Fabric", + "1.20.1" + ], + "sortableGameVersions": [ + { + "gameVersionName": "Fabric", + "gameVersionPadded": "0", + "gameVersion": "", + "gameVersionReleaseDate": "2022-09-01T00:00:00Z", + "gameVersionTypeId": 68441 + }, + { + "gameVersionName": "1.20.1", + "gameVersionPadded": "0000000001.0000000020.0000000001", + "gameVersion": "1.20.1", + "gameVersionReleaseDate": "2023-06-12T14:26:38.477Z", + "gameVersionTypeId": 75125 + } + ], + "dependencies": [], + "alternateFileId": 0, + "isServerPack": false, + "fileFingerprint": 1138339429, + "modules": [ + { + "name": "META-INF", + "fingerprint": 103321005 + }, + { + "name": "assets", + "fingerprint": 670826692 + }, + { + "name": "fabric.mod.json", + "fingerprint": 1868345856 + }, + { + "name": "jei-1.20.1-fabric-refmap.json", + "fingerprint": 1056944492 + }, + { + "name": "jei-icon.png", + "fingerprint": 2007185424 + }, + { + "name": "jei.accesswidener", + "fingerprint": 2262576366 + }, + { + "name": "jei.mixins.json", + "fingerprint": 1640667786 + }, + { + "name": "mezz", + "fingerprint": 3096604870 + }, + { + "name": "pack.mcmeta", + "fingerprint": 2606738017 + } + ] + }, + { + "id": 5626488, + "gameId": 432, + "modId": 238222, + "isAvailable": true, + "displayName": "jei-1.20.1-forge-15.12.2.51.jar", + "fileName": "jei-1.20.1-forge-15.12.2.51.jar", + "releaseType": 1, + "fileStatus": 4, + "hashes": [ + { + "value": "c16c67d133702dddbe1f4d232389be9965838cfb", + "algo": 1 + }, + { + "value": "4500381fa9b095a7d41929fc443c31c1", + "algo": 2 + } + ], + "fileDate": "2024-08-14T00:54:46.853Z", + "fileLength": 1343395, + "downloadCount": 1610622, + "fileSizeOnDisk": 3024324, + "downloadUrl": "https://edge.forgecdn.net/files/5626/488/jei-1.20.1-forge-15.12.2.51.jar", + "gameVersions": [ + "1.20.1", + "Forge" + ], + "sortableGameVersions": [ + { + "gameVersionName": "1.20.1", + "gameVersionPadded": "0000000001.0000000020.0000000001", + "gameVersion": "1.20.1", + "gameVersionReleaseDate": "2023-06-12T14:26:38.477Z", + "gameVersionTypeId": 75125 + }, + { + "gameVersionName": "Forge", + "gameVersionPadded": "0", + "gameVersion": "", + "gameVersionReleaseDate": "2022-10-01T00:00:00Z", + "gameVersionTypeId": 68441 + } + ], + "dependencies": [], + "alternateFileId": 0, + "isServerPack": false, + "fileFingerprint": 1324877622, + "modules": [ + { + "name": "META-INF", + "fingerprint": 4251754191 + }, + { + "name": "assets", + "fingerprint": 670826692 + }, + { + "name": "jei-icon.png", + "fingerprint": 2007185424 + }, + { + "name": "mezz", + "fingerprint": 1095909132 + }, + { + "name": "pack.mcmeta", + "fingerprint": 2606738017 + } + ] + }, + { + "id": 5802635, + "gameId": 432, + "modId": 238222, + "isAvailable": true, + "displayName": "19.21.0.246 for Fabric 1.21.1", + "fileName": "jei-1.21.1-fabric-19.21.0.246.jar", + "releaseType": 2, + "fileStatus": 4, + "hashes": [ + { + "value": "92d943490e560b522762ae893bbc7e72b5c9f02a", + "algo": 1 + }, + { + "value": "89d78c5624cb50ab5b00060daba70715", + "algo": 2 + } + ], + "fileDate": "2024-10-11T12:07:39.537Z", + "fileLength": 1474032, + "downloadCount": 0, + "fileSizeOnDisk": 3341228, + "downloadUrl": "https://edge.forgecdn.net/files/5802/635/jei-1.21.1-fabric-19.21.0.246.jar", + "gameVersions": [ + "1.21", + "Fabric", + "1.21.1" + ], + "sortableGameVersions": [ + { + "gameVersionName": "1.21", + "gameVersionPadded": "0000000001.0000000021", + "gameVersion": "1.21", + "gameVersionReleaseDate": "2024-06-13T15:52:21.12Z", + "gameVersionTypeId": 77784 + }, + { + "gameVersionName": "Fabric", + "gameVersionPadded": "0", + "gameVersion": "", + "gameVersionReleaseDate": "2022-09-01T00:00:00Z", + "gameVersionTypeId": 68441 + }, + { + "gameVersionName": "1.21.1", + "gameVersionPadded": "0000000001.0000000021.0000000001", + "gameVersion": "1.21.1", + "gameVersionReleaseDate": "2024-08-08T15:17:53.787Z", + "gameVersionTypeId": 77784 + } + ], + "dependencies": [], + "alternateFileId": 0, + "isServerPack": false, + "fileFingerprint": 308495480, + "modules": [ + { + "name": "META-INF", + "fingerprint": 3982343629 + }, + { + "name": "assets", + "fingerprint": 2284906622 + }, + { + "name": "fabric.mod.json", + "fingerprint": 269931162 + }, + { + "name": "jei-1.21.1-fabric-refmap.json", + "fingerprint": 148796946 + }, + { + "name": "jei-icon.png", + "fingerprint": 2007185424 + }, + { + "name": "jei.accesswidener", + "fingerprint": 421432334 + }, + { + "name": "jei.mixins.json", + "fingerprint": 623332486 + }, + { + "name": "mezz", + "fingerprint": 2128323631 + }, + { + "name": "pack.mcmeta", + "fingerprint": 2606738017 + } + ] + }, + { + "id": 5802636, + "gameId": 432, + "modId": 238222, + "isAvailable": true, + "displayName": "19.21.0.246 for Forge 1.21.1", + "fileName": "jei-1.21.1-forge-19.21.0.246.jar", + "releaseType": 2, + "fileStatus": 4, + "hashes": [ + { + "value": "20a87115dc50d18d431bf1465367a6df4d56dcd9", + "algo": 1 + }, + { + "value": "46ccf4a896a4fe6654bd01151197f1a8", + "algo": 2 + } + ], + "fileDate": "2024-10-11T12:07:44.637Z", + "fileLength": 1442435, + "downloadCount": 0, + "fileSizeOnDisk": 3382384, + "downloadUrl": "https://edge.forgecdn.net/files/5802/636/jei-1.21.1-forge-19.21.0.246.jar", + "gameVersions": [ + "1.21", + "Forge", + "1.21.1" + ], + "sortableGameVersions": [ + { + "gameVersionName": "1.21", + "gameVersionPadded": "0000000001.0000000021", + "gameVersion": "1.21", + "gameVersionReleaseDate": "2024-06-13T15:52:21.12Z", + "gameVersionTypeId": 77784 + }, + { + "gameVersionName": "Forge", + "gameVersionPadded": "0", + "gameVersion": "", + "gameVersionReleaseDate": "2022-10-01T00:00:00Z", + "gameVersionTypeId": 68441 + }, + { + "gameVersionName": "1.21.1", + "gameVersionPadded": "0000000001.0000000021.0000000001", + "gameVersion": "1.21.1", + "gameVersionReleaseDate": "2024-08-08T15:17:53.787Z", + "gameVersionTypeId": 77784 + } + ], + "dependencies": [], + "alternateFileId": 0, + "isServerPack": false, + "fileFingerprint": 1922851540, + "modules": [ + { + "name": "META-INF", + "fingerprint": 886487487 + }, + { + "name": "jei-icon.png", + "fingerprint": 2007185424 + }, + { + "name": "pack.mcmeta", + "fingerprint": 2606738017 + }, + { + "name": "assets", + "fingerprint": 2284906622 + }, + { + "name": "mezz", + "fingerprint": 605380612 + } + ] + }, + { + "id": 5802637, + "gameId": 432, + "modId": 238222, + "isAvailable": true, + "displayName": "19.21.0.246 for NeoForge 1.21.1", + "fileName": "jei-1.21.1-neoforge-19.21.0.246.jar", + "releaseType": 2, + "fileStatus": 4, + "hashes": [ + { + "value": "e346313c458e2d64cfba6f06199127ae7b661616", + "algo": 1 + }, + { + "value": "d969b861931d717a1e0d7622f5117957", + "algo": 2 + } + ], + "fileDate": "2024-10-11T12:07:49.62Z", + "fileLength": 1442269, + "downloadCount": 0, + "fileSizeOnDisk": 3382628, + "downloadUrl": "https://edge.forgecdn.net/files/5802/637/jei-1.21.1-neoforge-19.21.0.246.jar", + "gameVersions": [ + "1.21", + "NeoForge", + "1.21.1" + ], + "sortableGameVersions": [ + { + "gameVersionName": "1.21", + "gameVersionPadded": "0000000001.0000000021", + "gameVersion": "1.21", + "gameVersionReleaseDate": "2024-06-13T15:52:21.12Z", + "gameVersionTypeId": 77784 + }, + { + "gameVersionName": "NeoForge", + "gameVersionPadded": "0", + "gameVersion": "", + "gameVersionReleaseDate": "2023-07-25T00:00:00Z", + "gameVersionTypeId": 68441 + }, + { + "gameVersionName": "1.21.1", + "gameVersionPadded": "0000000001.0000000021.0000000001", + "gameVersion": "1.21.1", + "gameVersionReleaseDate": "2024-08-08T15:17:53.787Z", + "gameVersionTypeId": 77784 + } + ], + "dependencies": [], + "alternateFileId": 0, + "isServerPack": false, + "fileFingerprint": 1125072613, + "modules": [ + { + "name": "META-INF", + "fingerprint": 3339863727 + }, + { + "name": "mezz", + "fingerprint": 2902661593 + }, + { + "name": "jei-icon.png", + "fingerprint": 2007185424 + }, + { + "name": "pack.mcmeta", + "fingerprint": 2606738017 + }, + { + "name": "assets", + "fingerprint": 2284906622 + } + ] + } + ], + "latestFilesIndexes": [ + { + "gameVersion": "1.21", + "fileId": 5802637, + "filename": "jei-1.21.1-neoforge-19.21.0.246.jar", + "releaseType": 2, + "gameVersionTypeId": 77784, + "modLoader": 6 + }, + { + "gameVersion": "1.21.1", + "fileId": 5802637, + "filename": "jei-1.21.1-neoforge-19.21.0.246.jar", + "releaseType": 2, + "gameVersionTypeId": 77784, + "modLoader": 6 + }, + { + "gameVersion": "1.21", + "fileId": 5802636, + "filename": "jei-1.21.1-forge-19.21.0.246.jar", + "releaseType": 2, + "gameVersionTypeId": 77784, + "modLoader": 1 + }, + { + "gameVersion": "1.21.1", + "fileId": 5802636, + "filename": "jei-1.21.1-forge-19.21.0.246.jar", + "releaseType": 2, + "gameVersionTypeId": 77784, + "modLoader": 1 + }, + { + "gameVersion": "1.21", + "fileId": 5802635, + "filename": "jei-1.21.1-fabric-19.21.0.246.jar", + "releaseType": 2, + "gameVersionTypeId": 77784, + "modLoader": 4 + }, + { + "gameVersion": "1.21.1", + "fileId": 5802635, + "filename": "jei-1.21.1-fabric-19.21.0.246.jar", + "releaseType": 2, + "gameVersionTypeId": 77784, + "modLoader": 4 + }, + { + "gameVersion": "1.19.2", + "fileId": 5793305, + "filename": "jei-1.19.2-forge-11.8.1.1033.jar", + "releaseType": 2, + "gameVersionTypeId": 73407, + "modLoader": 1 + }, + { + "gameVersion": "1.19.2", + "fileId": 5793304, + "filename": "jei-1.19.2-fabric-11.8.1.1033.jar", + "releaseType": 2, + "gameVersionTypeId": 73407, + "modLoader": 4 + }, + { + "gameVersion": "1.20.1", + "fileId": 5793297, + "filename": "jei-1.20.1-forge-15.20.0.104.jar", + "releaseType": 2, + "gameVersionTypeId": 75125, + "modLoader": 1 + }, + { + "gameVersion": "1.20.1", + "fileId": 5793295, + "filename": "jei-1.20.1-fabric-15.20.0.104.jar", + "releaseType": 2, + "gameVersionTypeId": 75125, + "modLoader": 4 + }, + { + "gameVersion": "1.20.4", + "fileId": 5680204, + "filename": "jei-1.20.4-neoforge-17.3.1.4.jar", + "releaseType": 2, + "gameVersionTypeId": 75125, + "modLoader": 6 + }, + { + "gameVersion": "1.20.4", + "fileId": 5680203, + "filename": "jei-1.20.4-forge-17.3.1.4.jar", + "releaseType": 2, + "gameVersionTypeId": 75125, + "modLoader": 1 + }, + { + "gameVersion": "1.20.4", + "fileId": 5680202, + "filename": "jei-1.20.4-fabric-17.3.1.4.jar", + "releaseType": 2, + "gameVersionTypeId": 75125, + "modLoader": 4 + }, + { + "gameVersion": "1.20.1", + "fileId": 5626488, + "filename": "jei-1.20.1-forge-15.12.2.51.jar", + "releaseType": 1, + "gameVersionTypeId": 75125, + "modLoader": 1 + }, + { + "gameVersion": "1.20.1", + "fileId": 5626487, + "filename": "jei-1.20.1-fabric-15.12.2.51.jar", + "releaseType": 1, + "gameVersionTypeId": 75125, + "modLoader": 4 + }, + { + "gameVersion": "1.21.1", + "fileId": 5623554, + "filename": "jei-1.21.1-forge-19.8.4.113.jar", + "releaseType": 1, + "gameVersionTypeId": 77784, + "modLoader": 1 + }, + { + "gameVersion": "1.21.1", + "fileId": 5623551, + "filename": "jei-1.21.1-fabric-19.8.4.113.jar", + "releaseType": 1, + "gameVersionTypeId": 77784, + "modLoader": 4 + }, + { + "gameVersion": "1.21.1", + "fileId": 5623550, + "filename": "jei-1.21.1-neoforge-19.8.4.113.jar", + "releaseType": 1, + "gameVersionTypeId": 77784, + "modLoader": 6 + }, + { + "gameVersion": "1.16.5", + "fileId": 5534622, + "filename": "jei-1.16.5-7.8.0.1012.jar", + "releaseType": 2, + "gameVersionTypeId": 70886, + "modLoader": 1 + }, + { + "gameVersion": "1.18.2", + "fileId": 5534399, + "filename": "jei-1.18.2-forge-10.2.1.1008.jar", + "releaseType": 2, + "gameVersionTypeId": 73250, + "modLoader": 1 + }, + { + "gameVersion": "1.18.2", + "fileId": 5534397, + "filename": "jei-1.18.2-fabric-10.2.1.1008.jar", + "releaseType": 2, + "gameVersionTypeId": 73250, + "modLoader": 4 + }, + { + "gameVersion": "1.19.4", + "fileId": 5533479, + "filename": "jei-1.19.4-forge-13.1.0.18.jar", + "releaseType": 2, + "gameVersionTypeId": 73407, + "modLoader": 1 + }, + { + "gameVersion": "1.19.4", + "fileId": 5533477, + "filename": "jei-1.19.4-fabric-13.1.0.18.jar", + "releaseType": 2, + "gameVersionTypeId": 73407, + "modLoader": 4 + }, + { + "gameVersion": "1.20.6", + "fileId": 5466528, + "filename": "jei-1.20.6-neoforge-18.0.0.65.jar", + "releaseType": 2, + "gameVersionTypeId": 75125, + "modLoader": 6 + }, + { + "gameVersion": "1.20.6", + "fileId": 5466527, + "filename": "jei-1.20.6-forge-18.0.0.65.jar", + "releaseType": 2, + "gameVersionTypeId": 75125, + "modLoader": 1 + }, + { + "gameVersion": "1.20.6", + "fileId": 5466526, + "filename": "jei-1.20.6-fabric-18.0.0.65.jar", + "releaseType": 2, + "gameVersionTypeId": 75125, + "modLoader": 4 + }, + { + "gameVersion": "1.19.2", + "fileId": 5106178, + "filename": "jei-1.19.2-forge-11.6.0.1019.jar", + "releaseType": 1, + "gameVersionTypeId": 73407, + "modLoader": 1 + }, + { + "gameVersion": "1.19.2", + "fileId": 5106177, + "filename": "jei-1.19.2-fabric-11.6.0.1019.jar", + "releaseType": 1, + "gameVersionTypeId": 73407, + "modLoader": 4 + }, + { + "gameVersion": "1.20.2", + "fileId": 5101374, + "filename": "jei-1.20.2-forge-16.0.0.2.jar", + "releaseType": 2, + "gameVersionTypeId": 75125, + "modLoader": 1 + }, + { + "gameVersion": "1.20.2", + "fileId": 5101373, + "filename": "jei-1.20.2-fabric-16.0.0.2.jar", + "releaseType": 2, + "gameVersionTypeId": 75125, + "modLoader": 4 + }, + { + "gameVersion": "1.12.2", + "fileId": 5101347, + "filename": "jei_1.12.2-4.16.1.1012.jar", + "releaseType": 2, + "gameVersionTypeId": 628, + "modLoader": 1 + }, + { + "gameVersion": "1.12.2", + "fileId": 5060339, + "filename": "jei_1.12.2-4.16.1.1004.jar", + "releaseType": 2, + "gameVersionTypeId": 628 + }, + { + "gameVersion": "1.18.2", + "fileId": 4593548, + "filename": "jei-1.18.2-forge-10.2.1.1005.jar", + "releaseType": 1, + "gameVersionTypeId": 73250, + "modLoader": 1 + }, + { + "gameVersion": "1.19.4", + "fileId": 4592504, + "filename": "jei-1.19.4-forge-13.1.0.15.jar", + "releaseType": 1, + "gameVersionTypeId": 73407, + "modLoader": 1 + }, + { + "gameVersion": "1.20", + "fileId": 4581323, + "filename": "jei-1.20-forge-14.0.0.11.jar", + "releaseType": 2, + "gameVersionTypeId": 75125, + "modLoader": 1 + }, + { + "gameVersion": "1.20", + "fileId": 4581322, + "filename": "jei-1.20-fabric-14.0.0.11.jar", + "releaseType": 2, + "gameVersionTypeId": 75125, + "modLoader": 4 + }, + { + "gameVersion": "1.19.3", + "fileId": 4455246, + "filename": "jei-1.19.3-forge-12.4.0.22.jar", + "releaseType": 2, + "gameVersionTypeId": 73407, + "modLoader": 1 + }, + { + "gameVersion": "1.19.3", + "fileId": 4455245, + "filename": "jei-1.19.3-fabric-12.4.0.22.jar", + "releaseType": 2, + "gameVersionTypeId": 73407, + "modLoader": 4 + }, + { + "gameVersion": "1.16.5", + "fileId": 4371666, + "filename": "jei-1.16.5-7.8.0.1009.jar", + "releaseType": 1, + "gameVersionTypeId": 70886, + "modLoader": 1 + }, + { + "gameVersion": "1.17.1", + "fileId": 4351306, + "filename": "jei-1.17.1-8.3.1.1002.jar", + "releaseType": 2, + "gameVersionTypeId": 73242, + "modLoader": 1 + }, + { + "gameVersion": "1.19.2", + "fileId": 4087658, + "filename": "jei-1.19.2-forge-11.4.0.286.jar", + "releaseType": 3, + "gameVersionTypeId": 73407, + "modLoader": 1 + }, + { + "gameVersion": "1.19.2", + "fileId": 4087656, + "filename": "jei-1.19.2-fabric-11.4.0.286.jar", + "releaseType": 3, + "gameVersionTypeId": 73407, + "modLoader": 4 + }, + { + "gameVersion": "1.18.1", + "fileId": 4060769, + "filename": "jei-1.18.1-9.4.1.276.jar", + "releaseType": 2, + "gameVersionTypeId": 73250, + "modLoader": 1 + }, + { + "gameVersion": "1.18.2", + "fileId": 4030311, + "filename": "jei-1.18.2-forge-10.1.5.272.jar", + "releaseType": 3, + "gameVersionTypeId": 73250, + "modLoader": 1 + }, + { + "gameVersion": "1.18.2", + "fileId": 4030310, + "filename": "jei-1.18.2-fabric-10.1.5.272.jar", + "releaseType": 3, + "gameVersionTypeId": 73250, + "modLoader": 4 + }, + { + "gameVersion": "1.19.1", + "fileId": 3922508, + "filename": "jei-1.19.1-forge-11.2.0.244.jar", + "releaseType": 3, + "gameVersionTypeId": 73407, + "modLoader": 1 + }, + { + "gameVersion": "1.19.1", + "fileId": 3922506, + "filename": "jei-1.19.1-fabric-11.2.0.244.jar", + "releaseType": 3, + "gameVersionTypeId": 73407, + "modLoader": 4 + }, + { + "gameVersion": "1.19", + "fileId": 3903068, + "filename": "jei-1.19-forge-11.1.1.239.jar", + "releaseType": 3, + "gameVersionTypeId": 73407, + "modLoader": 1 + }, + { + "gameVersion": "1.19", + "fileId": 3903066, + "filename": "jei-1.19-fabric-11.1.1.239.jar", + "releaseType": 3, + "gameVersionTypeId": 73407, + "modLoader": 4 + }, + { + "gameVersion": "1.18.1", + "fileId": 3723162, + "filename": "jei-1.18.1-9.4.1.172.jar", + "releaseType": 1, + "gameVersionTypeId": 73250, + "modLoader": 1 + }, + { + "gameVersion": "1.18", + "fileId": 3550020, + "filename": "jei-1.18-9.0.0.40.jar", + "releaseType": 2, + "gameVersionTypeId": 73250, + "modLoader": 1 + }, + { + "gameVersion": "1.13.2", + "fileId": 3272039, + "filename": "jei-1.13.2-5.0.0.31.jar", + "releaseType": 3, + "gameVersionTypeId": 55023 + }, + { + "gameVersion": "1.15.2", + "fileId": 3272032, + "filename": "jei-1.15.2-6.0.3.16.jar", + "releaseType": 3, + "gameVersionTypeId": 68722 + }, + { + "gameVersion": "1.16.4", + "fileId": 3245003, + "filename": "jei-1.16.4-7.6.1.74.jar", + "releaseType": 2, + "gameVersionTypeId": 70886, + "modLoader": 1 + }, + { + "gameVersion": "1.16.3", + "fileId": 3104018, + "filename": "jei-1.16.3-7.6.0.51.jar", + "releaseType": 2, + "gameVersionTypeId": 70886, + "modLoader": 1 + }, + { + "gameVersion": "1.16.3", + "fileId": 3071356, + "filename": "jei-1.16.3-7.4.0.40.jar", + "releaseType": 3, + "gameVersionTypeId": 70886, + "modLoader": 1 + }, + { + "gameVersion": "1.16.2", + "fileId": 3060935, + "filename": "jei-1.16.2-7.3.2.28.jar", + "releaseType": 3, + "gameVersionTypeId": 70886, + "modLoader": 1 + }, + { + "gameVersion": "1.12.2", + "fileId": 3040523, + "filename": "jei_1.12.2-4.16.1.301.jar", + "releaseType": 1, + "gameVersionTypeId": 628 + }, + { + "gameVersion": "1.14.4", + "fileId": 3039707, + "filename": "jei-1.14.4-6.0.1.30.jar", + "releaseType": 3, + "gameVersionTypeId": 64806 + }, + { + "gameVersion": "1.16.1", + "fileId": 3028697, + "filename": "jei-1.16.1-7.0.1.10.jar", + "releaseType": 3, + "gameVersionTypeId": 70886, + "modLoader": 1 + }, + { + "gameVersion": "1.15.1", + "fileId": 2855456, + "filename": "jei-1.15.1-6.0.0.1.jar", + "releaseType": 3, + "gameVersionTypeId": 68722 + }, + { + "gameVersion": "1.14.3", + "fileId": 2738328, + "filename": "jei-1.14.3-6.0.0.8.jar", + "releaseType": 3, + "gameVersionTypeId": 64806 + }, + { + "gameVersion": "1.14.2", + "fileId": 2733474, + "filename": "jei-1.14.2-6.0.0.3.jar", + "releaseType": 3, + "gameVersionTypeId": 64806 + }, + { + "gameVersion": "1.10.2", + "fileId": 2561516, + "filename": "jei_1.10.2-3.14.8.422.jar", + "releaseType": 2, + "gameVersionTypeId": 572 + }, + { + "gameVersion": "1.12", + "fileId": 2485363, + "filename": "jei_1.12.2-4.7.11.102.jar", + "releaseType": 2, + "gameVersionTypeId": 628 + }, + { + "gameVersion": "1.12.1", + "fileId": 2485363, + "filename": "jei_1.12.2-4.7.11.102.jar", + "releaseType": 2, + "gameVersionTypeId": 628 + }, + { + "gameVersion": "1.12", + "fileId": 2478647, + "filename": "jei_1.12.1-4.7.8.95.jar", + "releaseType": 1, + "gameVersionTypeId": 628 + }, + { + "gameVersion": "1.12.1", + "fileId": 2478647, + "filename": "jei_1.12.1-4.7.8.95.jar", + "releaseType": 1, + "gameVersionTypeId": 628 + }, + { + "gameVersion": "1.11.2", + "fileId": 2461378, + "filename": "jei_1.11.2-4.5.1.296.jar", + "releaseType": 2, + "gameVersionTypeId": 599 + }, + { + "gameVersion": "1.11.2", + "fileId": 2453428, + "filename": "jei_1.11.2-4.5.0.294.jar", + "releaseType": 1, + "gameVersionTypeId": 599 + }, + { + "gameVersion": "1.12", + "fileId": 2442204, + "filename": "jei_1.12-4.7.0.68.jar", + "releaseType": 3, + "gameVersionTypeId": 628 + }, + { + "gameVersion": "1.8.9", + "fileId": 2431977, + "filename": "jei_1.8.9-2.28.18.187.jar", + "releaseType": 1, + "gameVersionTypeId": 4 + }, + { + "gameVersion": "1.10.2", + "fileId": 2428966, + "filename": "jei_1.10.2-3.14.7.420.jar", + "releaseType": 1, + "gameVersionTypeId": 572 + }, + { + "gameVersion": "1.11", + "fileId": 2360492, + "filename": "jei_1.11-4.1.1.208.jar", + "releaseType": 2, + "gameVersionTypeId": 599 + }, + { + "gameVersion": "1.11", + "fileId": 2350616, + "filename": "jei_1.11-4.0.4.199.jar", + "releaseType": 3, + "gameVersionTypeId": 599 + }, + { + "gameVersion": "1.9.4", + "fileId": 2313650, + "filename": "jei_1.9.4-3.6.8.225.jar", + "releaseType": 1, + "gameVersionTypeId": 552 + }, + { + "gameVersion": "1.10", + "fileId": 2310912, + "filename": "jei_1.10-3.7.1.219.jar", + "releaseType": 1, + "gameVersionTypeId": 572 + }, + { + "gameVersion": "1.9.4", + "fileId": 2306298, + "filename": "jei_1.9.4-3.6.2.211.jar", + "releaseType": 2, + "gameVersionTypeId": 552 + }, + { + "gameVersion": "1.9", + "fileId": 2305823, + "filename": "jei_1.9.4-3.4.4.208.jar", + "releaseType": 2, + "gameVersionTypeId": 552 + }, + { + "gameVersion": "1.9", + "fileId": 2304545, + "filename": "jei_1.9.4-3.4.3.207.jar", + "releaseType": 1, + "gameVersionTypeId": 552 + }, + { + "gameVersion": "1.8.9", + "fileId": 2292565, + "filename": "jei_1.8.9-2.28.14.182.jar", + "releaseType": 2, + "gameVersionTypeId": 4 + }, + { + "gameVersion": "1.8.8", + "fileId": 2275072, + "filename": "jei_1.8.9-2.16.2.78.jar", + "releaseType": 1, + "gameVersionTypeId": 4 + }, + { + "gameVersion": "1.8", + "fileId": 2273901, + "filename": "jei_1.8-2.14.0.139.jar", + "releaseType": 1, + "gameVersionTypeId": 4 + }, + { + "gameVersion": "1.8.8", + "fileId": 2270928, + "filename": "jei_1.8.8-2.8.3.39.jar", + "releaseType": 2, + "gameVersionTypeId": 4 + }, + { + "gameVersion": "1.8", + "fileId": 2270927, + "filename": "jei_1.8-1.8.3.96.jar", + "releaseType": 2, + "gameVersionTypeId": 4 + } + ], + "latestEarlyAccessFilesIndexes": [], + "dateCreated": "2015-11-23T22:55:58.84Z", + "dateModified": "2024-10-11T12:11:48.843Z", + "dateReleased": "2024-10-11T12:07:49.62Z", + "allowModDistribution": true, + "gamePopularityRank": 1, + "isAvailable": true, + "hasCommentsEnabled": false, + "thumbsUpCount": 1 + } + }, + "headers" : { + "Content-Type" : "application/json; charset=utf-8" + } + } +} \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/v1_mods_31054.json b/src/test/resources/curseforge/mappings/v1_mods_31054.json new file mode 100644 index 00000000..593be379 --- /dev/null +++ b/src/test/resources/curseforge/mappings/v1_mods_31054.json @@ -0,0 +1,470 @@ +{ + "request" : { + "url" : "/v1/mods/31054", + "method" : "GET" + }, + "response" : { + "status" : 200, + "jsonBody" : { + "data": { + "id": 31054, + "gameId": 432, + "name": "WorldGuard", + "slug": "worldguard", + "links": { + "websiteUrl": "https://www.curseforge.com/minecraft/bukkit-plugins/worldguard", + "wikiUrl": "https://worldguard.enginehub.org/en/latest/", + "issuesUrl": "https://github.com/EngineHub/WorldGuard/issues", + "sourceUrl": "https://github.com/EngineHub/WorldGuard" + }, + "summary": "WorldGuard", + "status": 4, + "downloadCount": 8828759, + "isFeatured": false, + "primaryCategoryId": 116, + "categories": [ + { + "id": 125, + "gameId": 432, + "name": "Fixes", + "slug": "fixes", + "url": "https://www.curseforge.com/minecraft/bukkit-plugins/fixes", + "iconUrl": "https://media.forgecdn.net/avatars/65/454/636162917923103138.png", + "dateModified": "2016-12-02T22:09:52.31Z", + "isClass": false, + "classId": 5, + "parentCategoryId": 5 + }, + { + "id": 115, + "gameId": 432, + "name": "Admin Tools", + "slug": "admin-tools", + "url": "https://www.curseforge.com/minecraft/bukkit-plugins/admin-tools", + "iconUrl": "https://media.forgecdn.net/avatars/65/448/636162917918103026.png", + "dateModified": "2016-12-02T22:09:51.92Z", + "isClass": false, + "classId": 5, + "parentCategoryId": 5 + }, + { + "id": 124, + "gameId": 432, + "name": "World Editing and Management", + "slug": "world-editing-and-management", + "url": "https://www.curseforge.com/minecraft/bukkit-plugins/world-editing-and-management", + "iconUrl": "https://media.forgecdn.net/avatars/65/453/636162917922321868.png", + "dateModified": "2016-12-02T22:09:52.247Z", + "isClass": false, + "classId": 5, + "parentCategoryId": 5 + }, + { + "id": 127, + "gameId": 432, + "name": "General", + "slug": "general", + "url": "https://www.curseforge.com/minecraft/bukkit-plugins/general", + "iconUrl": "https://media.forgecdn.net/avatars/65/456/636162917924196887.png", + "dateModified": "2016-12-02T22:09:52.42Z", + "isClass": false, + "classId": 5, + "parentCategoryId": 5 + }, + { + "id": 116, + "gameId": 432, + "name": "Anti-Griefing Tools", + "slug": "anti-griefing-tools", + "url": "https://www.curseforge.com/minecraft/bukkit-plugins/anti-griefing-tools", + "iconUrl": "https://media.forgecdn.net/avatars/65/449/636162917920134408.png", + "dateModified": "2016-12-02T22:09:52.043Z", + "isClass": false, + "classId": 5, + "parentCategoryId": 5 + } + ], + "classId": 5, + "authors": [ + { + "id": 6858201, + "name": "sk89q", + "url": "https://www.curseforge.com/members/sk89q", + "avatarUrl": "https://static-cdn.jtvnw.net/jtv_user_pictures/sk89q-profile_image-a0ad33c9593065f8-150x150.png" + }, + { + "id": 6838603, + "name": "me4502", + "url": "https://www.curseforge.com/members/me4502", + "avatarUrl": "https://static-cdn.jtvnw.net/jtv_user_pictures/d3cc0211-dd10-4e28-a173-9e8941363b71-profile_image-150x150.png" + }, + { + "id": 3597986, + "name": "wizjany_", + "url": "https://www.curseforge.com/members/wizjany_", + "avatarUrl": "https://static-cdn.jtvnw.net/jtv_user_pictures/wizjany_-profile_image-115bb98f4b3a9786-150x150.png" + }, + { + "id": 6847816, + "name": "zmlaoeu", + "url": "https://www.curseforge.com/members/zmlaoeu", + "avatarUrl": "https://static-cdn.jtvnw.net/jtv_user_pictures/f6d662e967098273-profile_image-150x150.png" + } + ], + "logo": { + "id": 308041, + "modId": 31054, + "title": "637390520722371961.png", + "description": "", + "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/308/41/256/256/637390520722371961.png", + "url": "https://media.forgecdn.net/avatars/308/41/637390520722371961.png" + }, + "screenshots": [ + { + "id": 117768, + "modId": 31054, + "title": "Logo", + "description": "

Logo

\n", + "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/117/768/310/172/wg_logo.png", + "url": "https://media.forgecdn.net/attachments/117/768/wg_logo.png" + }, + { + "id": 117769, + "modId": 31054, + "title": "Logo", + "description": "

Logo

\n", + "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/117/769/310/172/curse_wg.png", + "url": "https://media.forgecdn.net/attachments/117/769/curse_wg.png" + } + ], + "mainFileId": 5719698, + "latestFiles": [ + { + "id": 5719698, + "gameId": 432, + "modId": 31054, + "isAvailable": true, + "displayName": "WorldGuard 7.0.12 (MC 1.21+)", + "fileName": "worldguard-bukkit-7.0.12-dist.jar", + "releaseType": 1, + "fileStatus": 4, + "hashes": [ + { + "value": "85689d36f5fc052031f1df37e44bb608cf075314", + "algo": 1 + }, + { + "value": "bec6d1378639d8552abbc11107a0d872", + "algo": 2 + } + ], + "fileDate": "2024-09-13T16:15:48.92Z", + "fileLength": 1163769, + "downloadCount": 13359, + "fileSizeOnDisk": 2475989, + "downloadUrl": "https://edge.forgecdn.net/files/5719/698/worldguard-bukkit-7.0.12-dist.jar", + "gameVersions": [ + "1.21" + ], + "sortableGameVersions": [ + { + "gameVersionName": "1.21", + "gameVersionPadded": "0000000001.0000000021", + "gameVersion": "1.21", + "gameVersionReleaseDate": "2024-06-20T00:00:00Z", + "gameVersionTypeId": 1 + } + ], + "dependencies": [ + { + "modId": 31043, + "relationType": 3 + } + ], + "alternateFileId": 0, + "isServerPack": false, + "fileFingerprint": 4125519748, + "modules": [ + { + "name": "META-INF", + "fingerprint": 207497007 + }, + { + "name": "com", + "fingerprint": 4048281274 + }, + { + "name": "defaults", + "fingerprint": 1739653355 + }, + { + "name": "migrations", + "fingerprint": 4042865746 + }, + { + "name": "plugin.yml", + "fingerprint": 1280486578 + }, + { + "name": "org", + "fingerprint": 2661636969 + } + ] + } + ], + "latestFilesIndexes": [ + { + "gameVersion": "1.21", + "fileId": 5719698, + "filename": "worldguard-bukkit-7.0.12-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.20.6", + "fileId": 5344377, + "filename": "worldguard-bukkit-7.0.10-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.20.4", + "fileId": 4675318, + "filename": "worldguard-bukkit-7.0.9-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.20.2", + "fileId": 4675318, + "filename": "worldguard-bukkit-7.0.9-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.20", + "fileId": 4675318, + "filename": "worldguard-bukkit-7.0.9-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.19.2", + "fileId": 4554903, + "filename": "worldguard-bukkit-7.0.8-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.19.3", + "fileId": 4554903, + "filename": "worldguard-bukkit-7.0.8-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.19.1", + "fileId": 4554903, + "filename": "worldguard-bukkit-7.0.8-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.19.4", + "fileId": 4554903, + "filename": "worldguard-bukkit-7.0.8-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.19", + "fileId": 4554903, + "filename": "worldguard-bukkit-7.0.8-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.18.1", + "fileId": 3677516, + "filename": "worldguard-bukkit-7.0.7-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.17", + "fileId": 3677516, + "filename": "worldguard-bukkit-7.0.7-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.18.2", + "fileId": 3677516, + "filename": "worldguard-bukkit-7.0.7-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.16", + "fileId": 3342964, + "filename": "worldguard-bukkit-7.0.5-dist.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.14", + "fileId": 2989116, + "filename": "worldguard-bukkit-7.0.3.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.15", + "fileId": 2989116, + "filename": "worldguard-bukkit-7.0.3.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.13", + "fileId": 2723606, + "filename": "worldguard-bukkit-7.0.0.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.12", + "fileId": 2610618, + "filename": "worldguard-bukkit-6.2.2.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.9", + "fileId": 956770, + "filename": "worldguard-6.2.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.10", + "fileId": 956770, + "filename": "worldguard-6.2.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.11", + "fileId": 956770, + "filename": "worldguard-6.2.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.8", + "fileId": 956770, + "filename": "worldguard-6.2.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.8.1", + "fileId": 881691, + "filename": "worldguard-6.1.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.7.2", + "fileId": 776555, + "filename": "worldguard-5.9.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.7.4", + "fileId": 776555, + "filename": "worldguard-5.9.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.6.4", + "fileId": 776555, + "filename": "worldguard-5.9.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.6.2", + "fileId": 719257, + "filename": "worldguard-5.8.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.6.1", + "fileId": 719257, + "filename": "worldguard-5.8.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.5.0", + "fileId": 706558, + "filename": "worldguard-5.7.5.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.5.1", + "fileId": 706558, + "filename": "worldguard-5.7.5.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.5.2", + "fileId": 706558, + "filename": "worldguard-5.7.5.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.4.7", + "fileId": 668843, + "filename": "worldguard-5.7.1.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.4.6", + "fileId": 668843, + "filename": "worldguard-5.7.1.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + }, + { + "gameVersion": "1.2.5", + "fileId": 586911, + "filename": "worldguard-5.5.2.jar", + "releaseType": 1, + "gameVersionTypeId": 1 + } + ], + "latestEarlyAccessFilesIndexes": [], + "dateCreated": "2011-08-25T01:04:04.123Z", + "dateModified": "2024-09-27T19:09:18.457Z", + "dateReleased": "2024-09-13T16:15:48.92Z", + "allowModDistribution": true, + "gamePopularityRank": 4377, + "isAvailable": true, + "hasCommentsEnabled": false, + "thumbsUpCount": 0 + } + }, + "headers" : { + "Content-Type" : "application/json; charset=utf-8" + } + } +} \ No newline at end of file diff --git a/src/test/resources/curseforge/mappings/v1_mods_667389.json b/src/test/resources/curseforge/mappings/v1_mods_667389.json new file mode 100644 index 00000000..d08ffff7 --- /dev/null +++ b/src/test/resources/curseforge/mappings/v1_mods_667389.json @@ -0,0 +1,253 @@ +{ + "request" : { + "url" : "/v1/mods/667389", + "method" : "GET" + }, + "response" : { + "status" : 200, + "jsonBody" : { + "data": { + "id": 667389, + "gameId": 432, + "name": "Goblin Traders [Fabric]", + "slug": "goblintraders-fabric", + "links": { + "websiteUrl": "https://www.curseforge.com/minecraft/mc-mods/goblintraders-fabric", + "wikiUrl": "", + "issuesUrl": "https://github.com/MrCrayfish/GoblinTraders/issues", + "sourceUrl": "https://github.com/MrCrayfish/GoblinTraders" + }, + "summary": "The official port of Goblin Traders for Fabric", + "status": 4, + "downloadCount": 5030800, + "isFeatured": false, + "primaryCategoryId": 422, + "categories": [ + { + "id": 406, + "gameId": 432, + "name": "World Gen", + "slug": "world-gen", + "url": "https://www.curseforge.com/minecraft/mc-mods/world-gen", + "iconUrl": "https://media.forgecdn.net/avatars/6/26/635351494599748860.png", + "dateModified": "2017-10-16T21:40:11.473Z", + "isClass": false, + "classId": 6, + "parentCategoryId": 6 + }, + { + "id": 411, + "gameId": 432, + "name": "Mobs", + "slug": "world-mobs", + "url": "https://www.curseforge.com/minecraft/mc-mods/world-gen/world-mobs", + "iconUrl": "https://media.forgecdn.net/avatars/6/53/635351500195070414.png", + "dateModified": "2014-05-08T17:46:59.507Z", + "isClass": false, + "classId": 6, + "parentCategoryId": 406 + }, + { + "id": 422, + "gameId": 432, + "name": "Adventure and RPG", + "slug": "adventure-rpg", + "url": "https://www.curseforge.com/minecraft/mc-mods/adventure-rpg", + "iconUrl": "https://media.forgecdn.net/avatars/6/37/635351497295252123.png", + "dateModified": "2014-05-08T17:42:09.54Z", + "isClass": false, + "classId": 6, + "parentCategoryId": 6 + } + ], + "classId": 6, + "authors": [ + { + "id": 7244595, + "name": "MrCrayfish", + "url": "https://www.curseforge.com/members/mrcrayfish", + "avatarUrl": "https://static-cdn.jtvnw.net/jtv_user_pictures/4ce607d6-66e9-45b8-a5ea-db8577519da2-profile_image-150x150.jpeg" + } + ], + "logo": { + "id": 871322, + "modId": 667389, + "title": "638288811682649421.png", + "description": "", + "thumbnailUrl": "https://media.forgecdn.net/avatars/thumbnails/871/322/256/256/638288811682649421.png", + "url": "https://media.forgecdn.net/avatars/871/322/638288811682649421.png" + }, + "screenshots": [ + { + "id": 670642, + "modId": 667389, + "title": "Banner", + "description": "", + "thumbnailUrl": "https://media.forgecdn.net/attachments/thumbnails/670/642/310/172/goblintraders_banner.png", + "url": "https://media.forgecdn.net/attachments/670/642/goblintraders_banner.png" + } + ], + "mainFileId": 4802504, + "latestFiles": [ + { + "id": 4802504, + "gameId": 432, + "modId": 667389, + "isAvailable": true, + "displayName": "Goblin Traders 1.9.3", + "fileName": "goblintraders-fabric-1.20.1-1.9.3.jar", + "releaseType": 1, + "fileStatus": 4, + "hashes": [ + { + "value": "04e2a0f65854beac78bbf79126f61b5807953b47", + "algo": 1 + }, + { + "value": "b996dc3cd27dc7ddcd3f6c550f40eed2", + "algo": 2 + } + ], + "fileDate": "2023-10-17T01:57:30.517Z", + "fileLength": 312370, + "downloadCount": 1391667, + "downloadUrl": "https://edge.forgecdn.net/files/4802/504/goblintraders-fabric-1.20.1-1.9.3.jar", + "gameVersions": [ + "Fabric", + "1.20.1" + ], + "sortableGameVersions": [ + { + "gameVersionName": "Fabric", + "gameVersionPadded": "0", + "gameVersion": "", + "gameVersionReleaseDate": "2022-09-01T00:00:00Z", + "gameVersionTypeId": 68441 + }, + { + "gameVersionName": "1.20.1", + "gameVersionPadded": "0000000001.0000000020.0000000001", + "gameVersion": "1.20.1", + "gameVersionReleaseDate": "2023-06-12T14:26:38.477Z", + "gameVersionTypeId": 75125 + } + ], + "dependencies": [ + { + "modId": 667377, + "relationType": 2 + }, + { + "modId": 667391, + "relationType": 3 + } + ], + "alternateFileId": 0, + "isServerPack": false, + "fileFingerprint": 3916209973, + "modules": [ + { + "name": "META-INF", + "fingerprint": 582908178 + }, + { + "name": "LICENSE_Goblin Traders", + "fingerprint": 2228520401 + }, + { + "name": ".cache", + "fingerprint": 2906097444 + }, + { + "name": "fabric.mod.json", + "fingerprint": 4033018105 + }, + { + "name": "goblintraders_background.png", + "fingerprint": 808496097 + }, + { + "name": "data", + "fingerprint": 1040927923 + }, + { + "name": "goblintraders_banner.png", + "fingerprint": 2978243803 + }, + { + "name": "goblintraders_icon.png", + "fingerprint": 3040146432 + }, + { + "name": "goblintraders.common.mixins.json", + "fingerprint": 1385391998 + }, + { + "name": "assets", + "fingerprint": 3973342126 + }, + { + "name": "pack.mcmeta", + "fingerprint": 1979345674 + }, + { + "name": "goblintraders.refmap.json", + "fingerprint": 2712489594 + }, + { + "name": "com", + "fingerprint": 3137009607 + } + ] + } + ], + "latestFilesIndexes": [ + { + "gameVersion": "1.20.1", + "fileId": 4802504, + "filename": "goblintraders-fabric-1.20.1-1.9.3.jar", + "releaseType": 1, + "gameVersionTypeId": 75125, + "modLoader": 4 + }, + { + "gameVersion": "1.19.4", + "fileId": 4728953, + "filename": "goblintraders-fabric-1.19.4-1.8.3.jar", + "releaseType": 1, + "gameVersionTypeId": 73407, + "modLoader": 4 + }, + { + "gameVersion": "1.19.3", + "fileId": 4416561, + "filename": "goblintraders-fabric-1.8.1-1.19.3.jar", + "releaseType": 1, + "gameVersionTypeId": 73407, + "modLoader": 4 + }, + { + "gameVersion": "1.19.2", + "fileId": 4119629, + "filename": "goblintraders-fabric-1.8.1-1.19.2.jar", + "releaseType": 1, + "gameVersionTypeId": 73407, + "modLoader": 4 + } + ], + "latestEarlyAccessFilesIndexes": [], + "dateCreated": "2022-09-02T15:19:05.287Z", + "dateModified": "2024-05-10T14:11:18.18Z", + "dateReleased": "2023-10-17T01:57:30.517Z", + "allowModDistribution": true, + "gamePopularityRank": 2046, + "isAvailable": true, + "hasCommentsEnabled": true, + "thumbsUpCount": 0 + } + }, + "headers" : { + "Content-Type" : "application/json; charset=utf-8" + } + } +} \ No newline at end of file