Added Cursefoge Source

This commit is contained in:
Kallum Jones 2022-08-09 14:55:54 +01:00
parent fdc880e7fc
commit 7d6d49ec19
No known key found for this signature in database
GPG Key ID: D7F4589C4D7F81A9
4 changed files with 116 additions and 30 deletions

View File

@ -7,14 +7,22 @@ import ModManager from "../mod-manager.js";
export default class FileDownloader {
static downloadMod(version: Version): void {
try {
if (version.url == null) {
throw new Error("URL was null");
}
https.get(version.url, res => {
const filePath = path.join(ModManager.FilePaths.MODS_FOLDER_PATH, version.fileName);
const writeStream = createWriteStream(filePath);
res.pipe(writeStream);
writeStream.on("finish", () => writeStream.close());
writeStream.on('error', () => {
throw new Error("Error while writing file during download")
})
})
} catch (e) {
throw new DownloadError(`Failed to download ${version.fileName} from ${version.url}`)
})
})
}
}
}

View File

@ -16,6 +16,7 @@ import MigratePossibleCommand from "./commands/migrate_possible.js";
import MigrateCommand from "./commands/migrate_command.js";
import ModrinthSource from "./mods/sources/modrinth_source.js";
import Mods from "./mods/mods.js";
import {CurseforgeSource} from "./mods/sources/curseforge_source.js";
export default class ModManager {
public static logger: Logger | null = null;
@ -42,7 +43,7 @@ export default class ModManager {
public static readonly MODS_FOLDER_PATH = path.join("mods")
}
static async init() {
static init() {
if (Initialiser.isInitialised()) {
this.logger = ModManager.createLogger();
}
@ -55,11 +56,8 @@ export default class ModManager {
command.registerCommand(this.program);
}
/* const source = new CurseforgeSource();
console.log(await source.search("lithium"))*/
Mods.registerSource(new ModrinthSource())
//Mods.registerSource(new CurseforgeSource(), "CURSEFORGE_API_KEY")
Mods.registerSource(new CurseforgeSource(), "CURSEFORGE_API_KEY")
this.program.showSuggestionAfterError();
this.program.showHelpAfterError();

View File

@ -37,7 +37,7 @@ export default class Mods {
id = await source.search(mod);
} catch (e) {
if (e instanceof ModNotFoundError) {
spinner.stop(`Mod not found on ${source.getSourceName()}`)
spinner.stop(`Mod ${mod} not found on ${source.getSourceName()}`)
} else {
spinner.error(`An error occurred searching for ${mod} on ${source.getSourceName()}. Skipping ${source.getSourceName()}`)
// Try the next source

View File

@ -1,31 +1,95 @@
/*import ModSource from "./mod_source.js";
import ModSource from "./mod_source.js";
import axios from "axios";
import MinecraftUtils from "../../util/minecraft_utils.js";
import ModNotFoundError from "../../errors/mod_not_found_error.js";
import Util from "../../util/util.js";
import {format} from "util";
import Mods from "../mods.js";
import FileDownloader from "../../io/file_downloder.js";
import DownloadError from "../../errors/download_error.js";
export class CurseforgeSource implements ModSource {
private static readonly BASE_URL: string = "https://api.curseforge.com";
private static readonly SEARCH_URL: string = `${CurseforgeSource.BASE_URL}/v1/mods/search`;
private static readonly BASE_URL: string = "https://api.curseforge.com/v1";
private static readonly SEARCH_URL: string = `${CurseforgeSource.BASE_URL}/mods/search`;
private static readonly GET_MOD_URL: string = `${CurseforgeSource.BASE_URL}/mods/%s`
private static readonly GET_FILE_URL: string = `${CurseforgeSource.BASE_URL}/mods/%s/files/%s`
private static readonly DOWNLOAD_CDN_URL: string = "https://edge.forgecdn.net/files/%s/%s/%s";
private static readonly MINECRAFT_ID: number = 432;
private static readonly FABRIC_TYPE: number = 4;
getLatestVersion(id: string, mcVersion: string): Promise<Version> {
const response = await
async getLatestVersion(id: string, mcVersion: string): Promise<Version> {
const modResponse = await this.makeRequest(format(CurseforgeSource.GET_MOD_URL, id));
const latestFiles: Array<any> = modResponse.data.latestFilesIndexes;
return Promise.resolve(undefined);
const latestFilesArr = latestFiles.filter(file => file.gameVersion === mcVersion);
if (Util.isArrayEmpty(latestFilesArr)) {
throw new ModNotFoundError(`Mod with id ${id} has no available versions on ${this.getSourceName()} for Minecraft version ${mcVersion}`)
}
getProjectName(id: string): Promise<string> {
return Promise.resolve("");
const fileId = latestFilesArr[0].fileId;
const fileResponse = await this.makeRequest(format(CurseforgeSource.GET_FILE_URL, id, fileId))
const fileObj = fileResponse.data;
const dependencies = [];
if (!Util.isArrayEmpty(fileObj.dependencies)) {
for (let dependency of fileObj.dependencies) {
// If dependency is required
if (dependency.relationType == 3) {
dependencies.push(await this.getLatestVersion(dependency.modId, mcVersion));
}
}
}
const downloadUrl = fileObj.downloadUrl != null ? fileObj.downloadUrl : this.constructDownloadUrl(id, fileObj.fileName);
return {
modId: id.toString(),
fileName: fileObj.fileName,
url: downloadUrl,
versionNumber: fileObj.displayName,
dependencies: dependencies
}
}
async getProjectName(id: string): Promise<string> {
const response = await this.makeRequest(format(CurseforgeSource.GET_MOD_URL, id))
return response.data.name;
}
getSourceName(): string {
return "Curseforge";
}
install(version: Version, essential: boolean): Promise<Mod> {
return Promise.resolve(undefined);
async install(version: Version, essential: boolean): Promise<void> {
try {
if (Mods.isModInstalled(version.modId)) {
return;
}
const dependencies = [];
if (!Util.isArrayEmpty(version.dependencies)) {
for (let dependency of version.dependencies) {
await this.install(dependency, essential);
dependencies.push(dependency.modId)
}
}
FileDownloader.downloadMod(version)
const mod = {
name: await this.getProjectName(version.modId),
id: version.modId,
fileName: version.fileName,
version: version.versionNumber,
source: this.getSourceName(),
essential: essential,
dependencies: dependencies
}
Mods.trackMod(mod);
} catch (e) {
throw new DownloadError(`An error occurred downloading mod with id ${version.modId} from ${this.getSourceName()}`)
}
}
async search(query: string): Promise<string> {
@ -40,21 +104,24 @@ export class CurseforgeSource implements ModSource {
}
const response = await this.makeRequest(CurseforgeSource.SEARCH_URL, params);
const results = response.data;
const id = response.data[0].id;
if (id == undefined) {
if (Util.isArrayEmpty(results)) {
throw new ModNotFoundError(`Mod ${query} could not be found on ${this.getSourceName()}`);
}
return id;
return results[0].id;
}
private async makeRequest(url: string, params: object) {
private async makeRequest(url: string, params?: object) {
if (process.env.CURSEFORGE_API_KEY == undefined) {
throw new Error("Attempted Curseforge api calls with undefined api key environment variable (CURSEFORGE_API_KEY)")
}
if (params == undefined) {
params = {}
}
const response = await axios.get(url, {
headers: {
"x-api-key": process.env.CURSEFORGE_API_KEY
@ -64,5 +131,18 @@ export class CurseforgeSource implements ModSource {
return await response.data;
}
private constructDownloadUrl(id: string, fileName: string) {
// Some mods have a null download link. Download links follow a pattern such that we can
// create the URL ourselves in those rare cases. If download link is invalid, download
// will gracefully fail
const first = id.toString().substring(0, 4);
let last = id.toString().substring(4);
if (last.charAt(0) == '0') {
last = last.replace("0", "");
}
*/
return format(CurseforgeSource.DOWNLOAD_CDN_URL, first, last, fileName);
}
}