feat: Start working on implementing Curseforge into the frontend + fetch_categories helper script
This commit is contained in:
parent
f9788ae49b
commit
0fb2017924
14 changed files with 309 additions and 107 deletions
3
.template.env
Normal file
3
.template.env
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#! Use single quotes due to special characters and dotenvy being weird
|
||||
|
||||
CURSEFORGE_API_KEY = ''
|
||||
9
Cargo.lock
generated
9
Cargo.lock
generated
|
|
@ -1672,6 +1672,11 @@ version = "0.15.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
source = "git+https://github.com/allan2/dotenvy/?rev=db0d6aa8fc8d1b0ddbfe94b5c833372e463eb1c5#db0d6aa8fc8d1b0ddbfe94b5c833372e463eb1c5"
|
||||
|
||||
[[package]]
|
||||
name = "dpi"
|
||||
version = "0.1.1"
|
||||
|
|
@ -4357,7 +4362,7 @@ dependencies = [
|
|||
"chrono",
|
||||
"dirs 5.0.1",
|
||||
"discord-rich-presence",
|
||||
"dotenvy",
|
||||
"dotenvy 0.15.7 (git+https://github.com/allan2/dotenvy/?rev=db0d6aa8fc8d1b0ddbfe94b5c833372e463eb1c5)",
|
||||
"flate2",
|
||||
"futures",
|
||||
"indicatif",
|
||||
|
|
@ -5153,7 +5158,7 @@ dependencies = [
|
|||
"bigdecimal 0.4.5",
|
||||
"chrono",
|
||||
"diagnostics",
|
||||
"dotenvy",
|
||||
"dotenvy 0.15.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures",
|
||||
"include_dir",
|
||||
"indexmap 1.9.3",
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ cocoa = { version = "0.26" }
|
|||
objc = { version = "0.2" }
|
||||
cc = { version = "1.1" }
|
||||
libc = { version = "0.2" }
|
||||
dotenvy = { version = "0.15.7" }
|
||||
dotenvy = { git = "https://github.com/allan2/dotenvy/", rev = "db0d6aa8fc8d1b0ddbfe94b5c833372e463eb1c5" }
|
||||
# https://github.com/tauri-apps/tauri/blob/dev/crates/tauri/Cargo.toml#L102
|
||||
webkit2gtk = { version = "=2.0.1", features = [ "v2_40" ] }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import type { Cluster, ManagedPackage, PackageType, Providers, ProviderSearchQuery, ProviderSearchResults } from '@onelauncher/client/bindings';
|
||||
import type { ModalProps } from '~ui/components/overlay/Modal';
|
||||
import { useNavigate } from '@solidjs/router';
|
||||
import { bridge } from '~imports';
|
||||
import Button from '~ui/components/base/Button';
|
||||
|
|
@ -7,6 +5,8 @@ import Dropdown from '~ui/components/base/Dropdown';
|
|||
import Modal, { createModal } from '~ui/components/overlay/Modal';
|
||||
import BrowserPackage from '~ui/pages/browser/BrowserPackage';
|
||||
import { type Accessor, type Context, createContext, createEffect, createSignal, For, on, onMount, type ParentProps, type Setter, Show, untrack, useContext } from 'solid-js';
|
||||
import type { Cluster, ManagedPackage, PackageType, Providers, ProviderSearchQuery, ProviderSearchResults } from '@onelauncher/client/bindings';
|
||||
import type { ModalProps } from '~ui/components/overlay/Modal';
|
||||
import { useRecentCluster } from './useCluster';
|
||||
import useCommand, { tryResult } from './useCommand';
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ function BrowserCategories() {
|
|||
<div class="flex flex-col gap-y-6">
|
||||
<div class="flex flex-col gap-y-2">
|
||||
<h6 class="my-1">Categories</h6>
|
||||
<For each={browserCategories.byPackageType(browser.packageType())}>
|
||||
<For each={browserCategories.byPackageType(browser.packageType(), browser.searchQuery().provider)}>
|
||||
{category => (
|
||||
<p
|
||||
class={`text-md capitalize text-fg-primary hover:text-fg-primary-hover ${isEnabled(category.id) ? 'text-opacity-100! hover:text-opacity-90!' : 'text-opacity-60! hover:text-opacity-70!'}`}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { BrowserContent } from './BrowserRoot';
|
|||
|
||||
function BrowserSearch() {
|
||||
const browser = useBrowser();
|
||||
const [results, { refetch }] = useCommand(() => bridge.commands.searchProviderPackages('Modrinth', browser.searchQuery()));
|
||||
const [results, { refetch }] = useCommand(() => bridge.commands.searchProviderPackages(browser.searchQuery().provider, browser.searchQuery()));
|
||||
|
||||
const paginationOptions = (): PaginationOptions => ({
|
||||
itemsCount: () => results()?.total || 0,
|
||||
|
|
|
|||
|
|
@ -7,74 +7,11 @@ export interface CategoryItem {
|
|||
id: string;
|
||||
}
|
||||
|
||||
type ModCategories = [
|
||||
'adventure',
|
||||
'library',
|
||||
'equipment',
|
||||
'decoration',
|
||||
'food',
|
||||
'magic',
|
||||
'performance',
|
||||
'storage',
|
||||
'technology',
|
||||
'utility',
|
||||
'worldgen',
|
||||
];
|
||||
|
||||
const modMapping: Record<Providers, { [key in ModCategories[number]]: CategoryItem }> = {
|
||||
Modrinth: {
|
||||
adventure: {
|
||||
display: 'Adventure',
|
||||
id: 'adventure',
|
||||
},
|
||||
library: {
|
||||
display: 'Library',
|
||||
id: 'library',
|
||||
},
|
||||
decoration: {
|
||||
display: 'Decoration',
|
||||
id: 'decoration',
|
||||
},
|
||||
equipment: {
|
||||
display: 'Equipment',
|
||||
id: 'equipment',
|
||||
},
|
||||
food: {
|
||||
display: 'Food',
|
||||
id: 'food',
|
||||
},
|
||||
magic: {
|
||||
display: 'Magic',
|
||||
id: 'magic',
|
||||
},
|
||||
performance: {
|
||||
display: 'Performance',
|
||||
id: 'optimization',
|
||||
},
|
||||
storage: {
|
||||
display: 'Storage',
|
||||
id: 'storage',
|
||||
},
|
||||
technology: {
|
||||
display: 'Technology',
|
||||
id: 'technology',
|
||||
},
|
||||
utility: {
|
||||
display: 'Utility',
|
||||
id: 'utility',
|
||||
},
|
||||
worldgen: {
|
||||
display: 'World Generation',
|
||||
id: 'worldgen',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const browserCategories = {
|
||||
byPackageType(packageType: PackageType): CategoryItem[] {
|
||||
byPackageType(packageType: PackageType, provider: Providers = 'Modrinth'): CategoryItem[] {
|
||||
switch (packageType) {
|
||||
case 'mod':
|
||||
return this.mod();
|
||||
return this.mod(provider);
|
||||
}
|
||||
|
||||
return [];
|
||||
|
|
@ -84,3 +21,75 @@ export const browserCategories = {
|
|||
return Object.values(modMapping[provider]);
|
||||
},
|
||||
};
|
||||
|
||||
const modMapping: Record<Providers, CategoryItem[]> = {
|
||||
Curseforge: [
|
||||
{ display: 'Server Utility', id: '435' },
|
||||
{ display: 'Armor, Tools, and Weapons', id: '434' },
|
||||
{ display: 'API and Library', id: '421' },
|
||||
{ display: 'Adventure and RPG', id: '422' },
|
||||
{ display: 'World Gen', id: '406' },
|
||||
{ display: 'Buildcraft', id: '432' },
|
||||
{ display: 'Twitch Integration', id: '4671' },
|
||||
{ display: 'Industrial Craft', id: '429' },
|
||||
{ display: 'Magic', id: '419' },
|
||||
{ display: 'Cosmetic', id: '424' },
|
||||
{ display: 'KubeJS', id: '5314' },
|
||||
{ display: 'Storage', id: '420' },
|
||||
{ display: 'Mobs', id: '411' },
|
||||
{ display: 'Ores and Resources', id: '408' },
|
||||
{ display: 'Create', id: '6484' },
|
||||
{ display: 'Genetics', id: '418' },
|
||||
{ display: 'Food', id: '436' },
|
||||
{ display: 'Automation', id: '4843' },
|
||||
{ display: 'Performance', id: '6814' },
|
||||
{ display: 'Farming', id: '416' },
|
||||
{ display: 'CraftTweaker', id: '4773' },
|
||||
{ display: 'Structures', id: '409' },
|
||||
{ display: 'Map and Information', id: '423' },
|
||||
{ display: 'Bug Fixes', id: '6821' },
|
||||
{ display: 'Biomes', id: '407' },
|
||||
{ display: 'Energy', id: '417' },
|
||||
{ display: 'Addons', id: '426' },
|
||||
{ display: 'Blood Magic', id: '4485' },
|
||||
{ display: 'Energy, Fluid, and Item Transport', id: '415' },
|
||||
{ display: 'Forestry', id: '433' },
|
||||
{ display: 'Education', id: '5299' },
|
||||
{ display: 'Technology', id: '412' },
|
||||
{ display: 'Thermal Expansion', id: '427' },
|
||||
{ display: 'Dimensions', id: '410' },
|
||||
{ display: 'Thaumcraft', id: '430' },
|
||||
{ display: 'Processing', id: '413' },
|
||||
{ display: 'Tinker\'s Construct', id: '428' },
|
||||
{ display: 'Miscellaneous', id: '425' },
|
||||
{ display: 'Skyblock', id: '6145' },
|
||||
{ display: 'Galacticraft', id: '5232' },
|
||||
{ display: 'Player Transport', id: '414' },
|
||||
{ display: 'Applied Energistics 2', id: '4545' },
|
||||
{ display: 'Integrated Dynamics', id: '6954' },
|
||||
{ display: 'Redstone', id: '4558' },
|
||||
{ display: 'Utility & QoL', id: '5191' },
|
||||
{ display: 'MCreator', id: '4906' },
|
||||
],
|
||||
Modrinth: [
|
||||
{ display: 'Adventure', id: 'adventure' },
|
||||
{ display: 'Cursed', id: 'cursed' },
|
||||
{ display: 'Decoration', id: 'decoration' },
|
||||
{ display: 'Economy', id: 'economy' },
|
||||
{ display: 'Equipment', id: 'equipment' },
|
||||
{ display: 'Food', id: 'food' },
|
||||
{ display: 'Game Mechanics', id: 'game-mechanics' },
|
||||
{ display: 'Library', id: 'library' },
|
||||
{ display: 'Magic', id: 'magic' },
|
||||
{ display: 'Management', id: 'management' },
|
||||
{ display: 'Minigame', id: 'minigame' },
|
||||
{ display: 'Mobs', id: 'mobs' },
|
||||
{ display: 'Optimization', id: 'optimization' },
|
||||
{ display: 'Social', id: 'social' },
|
||||
{ display: 'Storage', id: 'storage' },
|
||||
{ display: 'Technology', id: 'technology' },
|
||||
{ display: 'Transportation', id: 'transportation' },
|
||||
{ display: 'Utility', id: 'utility' },
|
||||
{ display: 'World Generation', id: 'worldgen' },
|
||||
],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { Cluster, ImportType, License, Loader, PackageType, Providers, VersionType } from '@onelauncher/client/bindings';
|
||||
import { DurationFormat } from '@formatjs/intl-durationformat';
|
||||
import { open } from '@tauri-apps/plugin-shell';
|
||||
import type { Cluster, ImportType, License, Loader, PackageType, Providers, VersionType } from '@onelauncher/client/bindings';
|
||||
|
||||
export function setAsyncTimeout(ms: number): Promise<void>;
|
||||
export function setAsyncTimeout(callback: () => void, ms: number): Promise<void>;
|
||||
|
|
@ -163,12 +163,13 @@ export function getLicenseUrl(licenseId: string | License | null | undefined): s
|
|||
export function getPackageUrl(provider: Providers, id: string, package_type: PackageType = 'mod'): string {
|
||||
const mapping: Record<Providers, string> = {
|
||||
Modrinth: `https://modrinth.com/${package_type}/${id}`,
|
||||
Curseforge: `https://www.curseforge.com/minecraft/${package_type}/${id}`,
|
||||
};
|
||||
|
||||
return mapping[provider];
|
||||
}
|
||||
|
||||
export const LOADERS: Loader[] = ['vanilla', 'fabric', 'forge', 'neoforge', 'quilt'] as const;
|
||||
export const PROVIDERS: Providers[] = ['Modrinth'] as const;
|
||||
export const PROVIDERS: Providers[] = ['Modrinth', 'Curseforge'] as const;
|
||||
export const PACKAGE_TYPES: PackageType[] = ['mod', 'resourcepack', 'datapack', 'shaderpack'] as const;
|
||||
export const LAUNCHER_IMPORT_TYPES: ImportType[] = ['PrismLauncher', 'Curseforge', 'Modrinth', 'ATLauncher', 'GDLauncher', 'FTBLauncher', 'MultiMC', 'TLauncher', 'Technic'] as const;
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
# You may need to escape any special characters such as $
|
||||
CURSEFORGE_API_KEY = ""
|
||||
|
|
@ -1,9 +1,41 @@
|
|||
fn main() {
|
||||
let dotenv_path = dotenvy::dotenv().expect("failed to find .env file");
|
||||
println!("cargo:rerun-if-changed={}", dotenv_path.display());
|
||||
use std::path::PathBuf;
|
||||
|
||||
for env_var in dotenvy::dotenv_iter().unwrap() {
|
||||
let (key, value) = env_var.unwrap();
|
||||
fn main() {
|
||||
let paths = vec![
|
||||
get_root_workspace().join(".env"),
|
||||
PathBuf::from(".env"),
|
||||
];
|
||||
|
||||
for path in paths {
|
||||
if path.exists() {
|
||||
load_from_path(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_root_workspace() -> PathBuf {
|
||||
let output: Vec<u8> = std::process::Command::new(env!("CARGO"))
|
||||
.arg("locate-project")
|
||||
.arg("--workspace")
|
||||
.arg("--message-format=plain")
|
||||
.output()
|
||||
.unwrap()
|
||||
.stdout;
|
||||
|
||||
let root_workspace = String::from_utf8(output).unwrap();
|
||||
let mut root_workspace = PathBuf::from(root_workspace.trim());
|
||||
|
||||
if root_workspace.is_file() {
|
||||
root_workspace.pop();
|
||||
}
|
||||
|
||||
root_workspace
|
||||
}
|
||||
|
||||
fn load_from_path(path: PathBuf) {
|
||||
let vars = dotenvy::EnvLoader::with_path(path).load().expect("failed to find .env file");
|
||||
|
||||
for (key, value) in vars {
|
||||
println!("cargo:rustc-env={key}={value}");
|
||||
}
|
||||
}
|
||||
|
|
@ -71,6 +71,7 @@ impl From<Loader> for CurseforgeLoader {
|
|||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[allow(dead_code)]
|
||||
pub struct Pagination {
|
||||
pub index: u32,
|
||||
pub page_size: u32,
|
||||
|
|
@ -89,25 +90,25 @@ pub struct CurseforgePackage {
|
|||
pub slug: String,
|
||||
pub links: Links,
|
||||
pub summary: String,
|
||||
pub status: u32,
|
||||
// pub status: u32,
|
||||
pub download_count: u64,
|
||||
pub is_featured: bool,
|
||||
pub primary_category_id: u32,
|
||||
// pub primary_category_id: u32,
|
||||
pub categories: Vec<Category>,
|
||||
pub authors: Vec<Author>,
|
||||
pub logo: Logo,
|
||||
pub screenshots: Vec<Screenshot>,
|
||||
pub main_file_id: u32,
|
||||
pub latest_files: Vec<LatestFile>,
|
||||
pub latest_files_indexes: Vec<LatestFilesIndex>,
|
||||
pub latest_early_access_files_indexes: Vec<LatestEarlyAccessFilesIndex>,
|
||||
// pub screenshots: Vec<Screenshot>,
|
||||
// pub main_file_id: u32,
|
||||
// pub latest_files: Vec<LatestFile>,
|
||||
// pub latest_files_indexes: Vec<LatestFilesIndex>,
|
||||
// pub latest_early_access_files_indexes: Vec<LatestEarlyAccessFilesIndex>,
|
||||
pub date_created: DateTime<Utc>,
|
||||
pub date_modified: DateTime<Utc>,
|
||||
pub date_released: DateTime<Utc>,
|
||||
#[serde(default)]
|
||||
pub allow_mod_distribution: bool,
|
||||
pub game_popularity_rank: u32,
|
||||
pub is_available: bool,
|
||||
// pub game_popularity_rank: u32,
|
||||
// pub is_available: bool,
|
||||
pub thumbs_up_count: u32,
|
||||
pub rating: Option<f32>,
|
||||
}
|
||||
|
|
@ -183,7 +184,7 @@ pub struct Category {
|
|||
pub url: String,
|
||||
pub icon_url: String,
|
||||
pub date_modified: String,
|
||||
pub parent_category_id: u32,
|
||||
// pub parent_category_id: u32,
|
||||
// pub display_index: u32,
|
||||
}
|
||||
|
||||
|
|
@ -226,11 +227,11 @@ pub struct LatestFile {
|
|||
pub is_available: bool,
|
||||
pub display_name: String,
|
||||
pub file_name: String,
|
||||
pub release_type: u32,
|
||||
pub file_status: u32,
|
||||
pub hashes: Vec<Hash>,
|
||||
pub file_date: String,
|
||||
pub file_length: u32,
|
||||
// pub release_type: u32,
|
||||
// pub file_status: u32,
|
||||
// pub hashes: Vec<Hash>,
|
||||
// pub file_date: String,
|
||||
// pub file_length: u32,
|
||||
pub download_count: u32,
|
||||
// pub file_size_on_disk: u32,
|
||||
pub download_url: Option<String>,
|
||||
|
|
@ -319,7 +320,7 @@ struct CFData<T> {
|
|||
data: T,
|
||||
}
|
||||
|
||||
pub async fn fetch(url: &str, url_builder: Option<Box<dyn FnOnce(&mut Url)>>) -> Result<Bytes> {
|
||||
pub async fn fetch_url_builder<F: FnOnce(&mut Url)>(url: &str, url_builder: Option<F>) -> Result<Bytes> {
|
||||
// TODO: Get the API key from settings, fallback to constant, and error if missing
|
||||
let key = crate::constants::CURSEFORGE_API_KEY.ok_or(anyhow::anyhow!("missing curseforge api key"))?;
|
||||
|
||||
|
|
@ -338,7 +339,7 @@ pub async fn fetch(url: &str, url_builder: Option<Box<dyn FnOnce(&mut Url)>>) ->
|
|||
|
||||
http::fetch_advanced(
|
||||
Method::GET,
|
||||
url.as_str(),
|
||||
dbg!(url.as_str()),
|
||||
None,
|
||||
None,
|
||||
Some(headers),
|
||||
|
|
@ -348,6 +349,10 @@ pub async fn fetch(url: &str, url_builder: Option<Box<dyn FnOnce(&mut Url)>>) ->
|
|||
.await
|
||||
}
|
||||
|
||||
async fn fetch(url: &str) -> Result<Bytes> {
|
||||
fetch_url_builder(url, None::<fn(&mut Url)>).await
|
||||
}
|
||||
|
||||
pub async fn search(
|
||||
query: Option<String>,
|
||||
limit: Option<u8>,
|
||||
|
|
@ -364,7 +369,7 @@ pub async fn search(
|
|||
}
|
||||
|
||||
let results: CFSearchResults = serde_json::from_slice(
|
||||
&fetch(
|
||||
&fetch_url_builder(
|
||||
"/v1/mods/search",
|
||||
Some(Box::new(move |url: &mut Url| {
|
||||
let params = &mut url.query_pairs_mut();
|
||||
|
|
@ -378,16 +383,16 @@ pub async fn search(
|
|||
|
||||
if let Some(game_versions) = game_versions {
|
||||
// Need to create a list e.g. ["1.16.5", "1.17.1"]
|
||||
params.append_pair("gameVersions", game_versions.into_iter().map(|v| format!("\"{}\"", v)).collect::<Vec<String>>().join(",").as_str());
|
||||
params.append_pair("gameVersions", format!("[{}]", game_versions.into_iter().map(|v| format!("\"{}\"", v)).collect::<Vec<String>>().join(",")).as_str());
|
||||
}
|
||||
|
||||
if let Some(loaders) = loaders {
|
||||
params.append_pair("modLoaderTypes", loaders.into_iter().map(|l| (CurseforgeLoader::from(l) as u32).to_string()).collect::<Vec<String>>().join(",").as_str());
|
||||
params.append_pair("modLoaderTypes", format!("[{}]", loaders.into_iter().map(|l| (CurseforgeLoader::from(l) as u32).to_string()).collect::<Vec<String>>().join(",")).as_str());
|
||||
}
|
||||
|
||||
// TODO: Merge this with categories
|
||||
if let Some(package_types) = package_types {
|
||||
params.append_pair("categoryIds", package_types.into_iter().map(|p| (CFPackageType::from(p) as u32).to_string()).collect::<Vec<_>>().join(",").as_str());
|
||||
params.append_pair("categoryIds", format!("[{}]", package_types.into_iter().map(|p| (CFPackageType::from(p) as u32).to_string()).collect::<Vec<_>>().join(",")).as_str());
|
||||
}
|
||||
}))
|
||||
).await?)?;
|
||||
|
|
@ -401,10 +406,7 @@ pub async fn search(
|
|||
|
||||
pub async fn get(id: u32) -> Result<CurseforgePackage> {
|
||||
Ok(serde_json::from_slice::<CFData<_>>(
|
||||
&fetch(
|
||||
format!("/v1/mods/{}", id).as_str(),
|
||||
None,
|
||||
)
|
||||
&fetch(format!("/v1/mods/{}", id).as_str())
|
||||
.await?,
|
||||
)?.data)
|
||||
}
|
||||
120
packages/scripts/api/fetch_categories.ts
Normal file
120
packages/scripts/api/fetch_categories.ts
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
import process from 'node:process';
|
||||
import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
interface MappedCategoryItem {
|
||||
display: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
type MappingList = Record<string, MappedCategoryItem[]>;
|
||||
|
||||
const packageTypes = ['mod', 'modpack', 'shader', 'resourcepack'] as const;
|
||||
type PackageType = typeof packageTypes[number];
|
||||
|
||||
async function loadEnv() {
|
||||
const path = await findWorkspaceDir(process.cwd());
|
||||
console.log(`Loading .env from ${path}`);
|
||||
dotenv.config({ path: `${path}/.env` });
|
||||
}
|
||||
|
||||
async function fetchCategoriesCurseforge(packageType: PackageType): Promise<MappingList> {
|
||||
const key = process.env.CURSEFORGE_API_KEY;
|
||||
|
||||
if (key === undefined) {
|
||||
console.error('CURSEFORGE_API_KEY is not defined');
|
||||
return {};
|
||||
}
|
||||
|
||||
const response = await fetch('https://api.curseforge.com/v1/categories?gameId=432', {
|
||||
headers: {
|
||||
'x-api-key': key,
|
||||
},
|
||||
});
|
||||
|
||||
interface CFCategory {
|
||||
id: number;
|
||||
name: 'Server Utility';
|
||||
slug: 'server-utility';
|
||||
url: 'https://www.curseforge.com/minecraft/mc-mods/server-utility';
|
||||
iconUrl: 'https://media.forgecdn.net/avatars/6/48/635351498950580836.png';
|
||||
classId?: number;
|
||||
isClass?: boolean;
|
||||
parentCategoryId?: number;
|
||||
};
|
||||
|
||||
const data = (await response.json() as { data: CFCategory[] }).data;
|
||||
|
||||
// TODO: Add more calssIds
|
||||
const classIds: Record<PackageType, number> = {
|
||||
mod: 6,
|
||||
resourcepack: 12,
|
||||
modpack: 4471,
|
||||
shader: 6552,
|
||||
};
|
||||
|
||||
const mapping: MappingList = {};
|
||||
|
||||
mapping.Curseforge = data
|
||||
.filter((category) => {
|
||||
return category.isClass !== true && category.classId === classIds[packageType];
|
||||
})
|
||||
.map((category) => {
|
||||
return {
|
||||
display: category.name,
|
||||
id: category.id.toString(),
|
||||
};
|
||||
});
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
async function fetchCategoriesModrinth(packageType: PackageType): Promise<MappingList> {
|
||||
const response = await fetch('https://api.modrinth.com/v2/tag/category');
|
||||
|
||||
interface ModrinthCategory {
|
||||
icon: string;
|
||||
name: string;
|
||||
project_type: PackageType;
|
||||
header: string;
|
||||
}
|
||||
|
||||
const data = await response.json() as ModrinthCategory[];
|
||||
|
||||
const mapping: MappingList = {};
|
||||
|
||||
mapping.Modrinth = data
|
||||
.filter((category) => {
|
||||
return category.project_type === packageType;
|
||||
})
|
||||
.map((category) => {
|
||||
return {
|
||||
display: category.name.charAt(0).toUpperCase() + category.name.slice(1),
|
||||
id: category.name,
|
||||
};
|
||||
});
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const arg = process.argv[2];
|
||||
if (arg === undefined) {
|
||||
console.error(`Please provide an argument for package type.\n Valid: '${packageTypes.join('\', \'')}'`);
|
||||
return;
|
||||
}
|
||||
|
||||
const packageType = arg as PackageType;
|
||||
|
||||
await loadEnv();
|
||||
|
||||
const cfCategories = await fetchCategoriesCurseforge(packageType);
|
||||
const modrinthCategories = await fetchCategoriesModrinth(packageType);
|
||||
|
||||
const categories = {
|
||||
...cfCategories,
|
||||
...modrinthCategories,
|
||||
};
|
||||
|
||||
console.log(categories);
|
||||
})();
|
||||
|
|
@ -22,10 +22,13 @@
|
|||
"setup-nix": "./setup.sh",
|
||||
"autoformat": "./format.sh",
|
||||
"fix-deb": "./fix-deb.sh",
|
||||
"dupe": "./dedupe.sh"
|
||||
"dupe": "./dedupe.sh",
|
||||
"api:categories": "pnpm tsx ./api/fetch_categories.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/find-workspace-dir": "^7.0.1",
|
||||
"consola": "^3.2.3",
|
||||
"dotenv": "^16.4.5",
|
||||
"execa": "^9.4.0",
|
||||
"mustache": "^4.2.0",
|
||||
"pathe": "^1.1.2",
|
||||
|
|
|
|||
29
pnpm-lock.yaml
generated
29
pnpm-lock.yaml
generated
|
|
@ -264,9 +264,15 @@ importers:
|
|||
|
||||
packages/scripts:
|
||||
dependencies:
|
||||
'@pnpm/find-workspace-dir':
|
||||
specifier: ^7.0.1
|
||||
version: 7.0.1
|
||||
consola:
|
||||
specifier: ^3.2.3
|
||||
version: 3.2.3
|
||||
dotenv:
|
||||
specifier: ^16.4.5
|
||||
version: 16.4.5
|
||||
execa:
|
||||
specifier: ^9.4.0
|
||||
version: 9.4.0
|
||||
|
|
@ -1020,6 +1026,18 @@ packages:
|
|||
resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
|
||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
||||
|
||||
'@pnpm/constants@8.0.0':
|
||||
resolution: {integrity: sha512-yQosGUvYPpAjb1jOFcdbwekRjZRVxN6C0hHzfRCZrMKbxGjt/E0g0RcFlEDNVZ95tm4oMMcr7nEPa7H7LX3emw==}
|
||||
engines: {node: '>=18.12'}
|
||||
|
||||
'@pnpm/error@6.0.1':
|
||||
resolution: {integrity: sha512-7yjO0RgmWYb4OKgcWC33yD4Z2CxE7Tm7vXX1SmS7GDifDT/bgZZhHeS2xq/+W6y9yhwIrRSA+7AlQL1NM2wIvw==}
|
||||
engines: {node: '>=18.12'}
|
||||
|
||||
'@pnpm/find-workspace-dir@7.0.1':
|
||||
resolution: {integrity: sha512-o1LAFM/5MChI6qBolMBOznzatch01UK3wIgoAE/b779qs1FakksB278nMRTwRY58PZSBT+RxZ2RCMjlxPLeVWw==}
|
||||
engines: {node: '>=18.12'}
|
||||
|
||||
'@polka/url@1.0.0-next.25':
|
||||
resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==}
|
||||
|
||||
|
|
@ -4866,6 +4884,17 @@ snapshots:
|
|||
|
||||
'@pkgr/core@0.1.1': {}
|
||||
|
||||
'@pnpm/constants@8.0.0': {}
|
||||
|
||||
'@pnpm/error@6.0.1':
|
||||
dependencies:
|
||||
'@pnpm/constants': 8.0.0
|
||||
|
||||
'@pnpm/find-workspace-dir@7.0.1':
|
||||
dependencies:
|
||||
'@pnpm/error': 6.0.1
|
||||
find-up: 5.0.0
|
||||
|
||||
'@polka/url@1.0.0-next.25': {}
|
||||
|
||||
'@prisma/debug@5.19.1': {}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue