mirror of
https://git.bits.team/Bits/mod-manager.git
synced 2024-11-14 10:28:21 -05:00
Added Cursefoge Source
This commit is contained in:
parent
fdc880e7fc
commit
7d6d49ec19
@ -7,14 +7,22 @@ import ModManager from "../mod-manager.js";
|
|||||||
|
|
||||||
export default class FileDownloader {
|
export default class FileDownloader {
|
||||||
static downloadMod(version: Version): void {
|
static downloadMod(version: Version): void {
|
||||||
|
try {
|
||||||
|
if (version.url == null) {
|
||||||
|
throw new Error("URL was null");
|
||||||
|
}
|
||||||
|
|
||||||
https.get(version.url, res => {
|
https.get(version.url, res => {
|
||||||
const filePath = path.join(ModManager.FilePaths.MODS_FOLDER_PATH, version.fileName);
|
const filePath = path.join(ModManager.FilePaths.MODS_FOLDER_PATH, version.fileName);
|
||||||
const writeStream = createWriteStream(filePath);
|
const writeStream = createWriteStream(filePath);
|
||||||
res.pipe(writeStream);
|
res.pipe(writeStream);
|
||||||
writeStream.on("finish", () => writeStream.close());
|
writeStream.on("finish", () => writeStream.close());
|
||||||
writeStream.on('error', () => {
|
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}`)
|
throw new DownloadError(`Failed to download ${version.fileName} from ${version.url}`)
|
||||||
})
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,6 +16,7 @@ import MigratePossibleCommand from "./commands/migrate_possible.js";
|
|||||||
import MigrateCommand from "./commands/migrate_command.js";
|
import MigrateCommand from "./commands/migrate_command.js";
|
||||||
import ModrinthSource from "./mods/sources/modrinth_source.js";
|
import ModrinthSource from "./mods/sources/modrinth_source.js";
|
||||||
import Mods from "./mods/mods.js";
|
import Mods from "./mods/mods.js";
|
||||||
|
import {CurseforgeSource} from "./mods/sources/curseforge_source.js";
|
||||||
|
|
||||||
export default class ModManager {
|
export default class ModManager {
|
||||||
public static logger: Logger | null = null;
|
public static logger: Logger | null = null;
|
||||||
@ -42,7 +43,7 @@ export default class ModManager {
|
|||||||
public static readonly MODS_FOLDER_PATH = path.join("mods")
|
public static readonly MODS_FOLDER_PATH = path.join("mods")
|
||||||
}
|
}
|
||||||
|
|
||||||
static async init() {
|
static init() {
|
||||||
if (Initialiser.isInitialised()) {
|
if (Initialiser.isInitialised()) {
|
||||||
this.logger = ModManager.createLogger();
|
this.logger = ModManager.createLogger();
|
||||||
}
|
}
|
||||||
@ -55,11 +56,8 @@ export default class ModManager {
|
|||||||
command.registerCommand(this.program);
|
command.registerCommand(this.program);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* const source = new CurseforgeSource();
|
|
||||||
console.log(await source.search("lithium"))*/
|
|
||||||
|
|
||||||
Mods.registerSource(new ModrinthSource())
|
Mods.registerSource(new ModrinthSource())
|
||||||
//Mods.registerSource(new CurseforgeSource(), "CURSEFORGE_API_KEY")
|
Mods.registerSource(new CurseforgeSource(), "CURSEFORGE_API_KEY")
|
||||||
|
|
||||||
this.program.showSuggestionAfterError();
|
this.program.showSuggestionAfterError();
|
||||||
this.program.showHelpAfterError();
|
this.program.showHelpAfterError();
|
||||||
|
@ -37,7 +37,7 @@ export default class Mods {
|
|||||||
id = await source.search(mod);
|
id = await source.search(mod);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ModNotFoundError) {
|
if (e instanceof ModNotFoundError) {
|
||||||
spinner.stop(`Mod not found on ${source.getSourceName()}`)
|
spinner.stop(`Mod ${mod} not found on ${source.getSourceName()}`)
|
||||||
} else {
|
} else {
|
||||||
spinner.error(`An error occurred searching for ${mod} on ${source.getSourceName()}. Skipping ${source.getSourceName()}`)
|
spinner.error(`An error occurred searching for ${mod} on ${source.getSourceName()}. Skipping ${source.getSourceName()}`)
|
||||||
// Try the next source
|
// Try the next source
|
||||||
|
@ -1,31 +1,95 @@
|
|||||||
/*import ModSource from "./mod_source.js";
|
import ModSource from "./mod_source.js";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import MinecraftUtils from "../../util/minecraft_utils.js";
|
import MinecraftUtils from "../../util/minecraft_utils.js";
|
||||||
import ModNotFoundError from "../../errors/mod_not_found_error.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 {
|
export class CurseforgeSource implements ModSource {
|
||||||
private static readonly BASE_URL: string = "https://api.curseforge.com";
|
private static readonly BASE_URL: string = "https://api.curseforge.com/v1";
|
||||||
private static readonly SEARCH_URL: string = `${CurseforgeSource.BASE_URL}/v1/mods/search`;
|
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 MINECRAFT_ID: number = 432;
|
||||||
private static readonly FABRIC_TYPE: number = 4;
|
private static readonly FABRIC_TYPE: number = 4;
|
||||||
|
|
||||||
getLatestVersion(id: string, mcVersion: string): Promise<Version> {
|
async getLatestVersion(id: string, mcVersion: string): Promise<Version> {
|
||||||
const response = await
|
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> {
|
const fileId = latestFilesArr[0].fileId;
|
||||||
return Promise.resolve("");
|
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 {
|
getSourceName(): string {
|
||||||
return "Curseforge";
|
return "Curseforge";
|
||||||
}
|
}
|
||||||
|
|
||||||
install(version: Version, essential: boolean): Promise<Mod> {
|
async install(version: Version, essential: boolean): Promise<void> {
|
||||||
return Promise.resolve(undefined);
|
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> {
|
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 response = await this.makeRequest(CurseforgeSource.SEARCH_URL, params);
|
||||||
|
const results = response.data;
|
||||||
|
|
||||||
const id = response.data[0].id;
|
if (Util.isArrayEmpty(results)) {
|
||||||
|
|
||||||
if (id == undefined) {
|
|
||||||
throw new ModNotFoundError(`Mod ${query} could not be found on ${this.getSourceName()}`);
|
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) {
|
if (process.env.CURSEFORGE_API_KEY == undefined) {
|
||||||
throw new Error("Attempted Curseforge api calls with undefined api key environment variable (CURSEFORGE_API_KEY)")
|
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, {
|
const response = await axios.get(url, {
|
||||||
headers: {
|
headers: {
|
||||||
"x-api-key": process.env.CURSEFORGE_API_KEY
|
"x-api-key": process.env.CURSEFORGE_API_KEY
|
||||||
@ -64,5 +131,18 @@ export class CurseforgeSource implements ModSource {
|
|||||||
return await response.data;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user