diff --git a/cmd/init.go b/cmd/init.go index 98d824a..f63b621 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "net/http" "os" "path/filepath" "sort" @@ -267,7 +266,7 @@ func (m mcVersionManifest) checkValid(version string) { } func getValidMCVersions() (mcVersionManifest, error) { - res, err := http.Get("https://launchermeta.mojang.com/mc/game/version_manifest.json") + res, err := core.GetWithUA("https://launchermeta.mojang.com/mc/game/version_manifest.json", "application/json") if err != nil { return mcVersionManifest{}, err } diff --git a/core/download.go b/core/download.go index 7073041..ffefc3e 100644 --- a/core/download.go +++ b/core/download.go @@ -13,6 +13,18 @@ import ( "strings" ) +const UserAgent = "packwiz/packwiz" + +func GetWithUA(url string, contentType string) (resp *http.Response, err error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + req.Header.Set("User-Agent", UserAgent) + req.Header.Set("Accept", contentType) + return http.DefaultClient.Do(req) +} + const DownloadCacheImportFolder = "import" type DownloadSession interface { @@ -143,8 +155,7 @@ func downloadNewFile(task *downloadTask, cacheFolder string, hashesToObtain []st if len(hashesToObtain) > 0 { var data io.ReadCloser if task.url != "" { - resp, err := http.Get(task.url) - // TODO: content type, user-agent? + resp, err := GetWithUA(task.url, "application/octet-stream") if err != nil { return CompletedDownload{}, fmt.Errorf("failed to download %s: %w", task.url, err) } diff --git a/core/versionutil.go b/core/versionutil.go index 0bed4dc..a971227 100644 --- a/core/versionutil.go +++ b/core/versionutil.go @@ -3,7 +3,6 @@ package core import ( "encoding/xml" "errors" - "net/http" "strings" ) @@ -53,7 +52,7 @@ var ModLoaders = map[string]ModLoaderComponent{ func FetchMavenVersionList(url string) func(mcVersion string) ([]string, string, error) { return func(mcVersion string) ([]string, string, error) { - res, err := http.Get(url) + res, err := GetWithUA(url, "application/xml") if err != nil { return []string{}, "", err } @@ -69,7 +68,7 @@ func FetchMavenVersionList(url string) func(mcVersion string) ([]string, string, func FetchMavenVersionPrefixedList(url string, friendlyName string) func(mcVersion string) ([]string, string, error) { return func(mcVersion string) ([]string, string, error) { - res, err := http.Get(url) + res, err := GetWithUA(url, "application/xml") if err != nil { return []string{}, "", err } diff --git a/curseforge/curseforge.go b/curseforge/curseforge.go index 8df8fe4..bdf5aee 100644 --- a/curseforge/curseforge.go +++ b/curseforge/curseforge.go @@ -6,7 +6,6 @@ import ( "github.com/spf13/viper" "golang.org/x/exp/slices" "io" - "net/http" "path/filepath" "regexp" "strconv" @@ -540,8 +539,7 @@ func (m *cfDownloadMetadata) GetManualDownload() (bool, core.ManualDownload) { } func (m *cfDownloadMetadata) DownloadFile() (io.ReadCloser, error) { - resp, err := http.Get(m.url) - // TODO: content type, user-agent? + resp, err := core.GetWithUA(m.url, "application/octet-stream") if err != nil { return nil, fmt.Errorf("failed to download %s: %w", m.url, err) } diff --git a/curseforge/request.go b/curseforge/request.go index 977b1fa..efb154d 100644 --- a/curseforge/request.go +++ b/curseforge/request.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "github.com/packwiz/packwiz/core" "io" "net/http" "net/url" @@ -40,8 +41,7 @@ func (c *cfApiClient) makeGet(endpoint string) (*http.Response, error) { return nil, err } - // TODO: make this configurable application-wide - req.Header.Set("User-Agent", "packwiz/packwiz client") + req.Header.Set("User-Agent", core.UserAgent) req.Header.Set("Accept", "application/json") if cfApiKey == "" { cfApiKey = decodeDefaultKey() @@ -65,8 +65,7 @@ func (c *cfApiClient) makePost(endpoint string, body io.Reader) (*http.Response, return nil, err } - // TODO: make this configurable application-wide - req.Header.Set("User-Agent", "packwiz/packwiz client") + req.Header.Set("User-Agent", core.UserAgent) req.Header.Set("Accept", "application/json") req.Header.Set("Content-Type", "application/json") if cfApiKey == "" { diff --git a/go.mod b/go.mod index 02f8b9b..921bf2b 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,11 @@ require ( gopkg.in/dixonwille/wmenu.v4 v4.0.2 ) -require golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd +require ( + codeberg.org/jmansfield/go-modrinth v0.4.1 + github.com/spf13/pflag v1.0.5 + golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd +) require ( github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect @@ -40,7 +44,6 @@ require ( github.com/spf13/afero v1.8.2 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.2.0 // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 54b991e..eb574b2 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +codeberg.org/jmansfield/go-modrinth v0.4.1 h1:uCWqjep41jplMLLyarQ6rvD+4FyFIBsa5UcPfZWOGTo= +codeberg.org/jmansfield/go-modrinth v0.4.1/go.mod h1:feVF2NqtWdzIpCMgUT/Q/Wb8CpEVpi64m4TJXB+ejq0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= diff --git a/modrinth/install.go b/modrinth/install.go index e9c32dc..c40a8e3 100644 --- a/modrinth/install.go +++ b/modrinth/install.go @@ -1,6 +1,7 @@ package modrinth import ( + modrinthApi "codeberg.org/jmansfield/go-modrinth/modrinth" "errors" "fmt" "github.com/spf13/viper" @@ -69,7 +70,7 @@ var installCmd = &cobra.Command{ modStr = args[0] } - mod, err := fetchMod(modStr) + mod, err := mrDefaultClient.Projects.Get(modStr) if err == nil { //We found a mod with that id/slug @@ -111,7 +112,8 @@ func installViaSearch(query string, pack core.Pack) error { menu := wmenu.NewMenu("Choose a number:") menu.Option("Cancel", nil, false, nil) for i, v := range results { - menu.Option(v.Title, v, i == 0, nil) + // Should be non-nil (Title is a required field) + menu.Option(*v.Title, v, i == 0, nil) } menu.Action(func(menuRes []wmenu.Opt) error { @@ -120,15 +122,13 @@ func installViaSearch(query string, pack core.Pack) error { } //Get the selected mod - selectedMod, ok := menuRes[0].Value.(ModResult) + selectedMod, ok := menuRes[0].Value.(*modrinthApi.SearchResult) if !ok { return errors.New("error converting interface from wmenu") } //Install the selected mod - modId := strings.TrimPrefix(selectedMod.ModID, "local-") - - mod, err := fetchMod(modId) + mod, err := mrDefaultClient.Projects.Get(*selectedMod.ProjectID) if err != nil { return err } @@ -139,21 +139,21 @@ func installViaSearch(query string, pack core.Pack) error { return menu.Run() } -func installMod(mod Mod, pack core.Pack) error { - fmt.Printf("Found mod %s: '%s'.\n", mod.Title, mod.Description) +func installMod(mod *modrinthApi.Project, pack core.Pack) error { + fmt.Printf("Found mod %s: '%s'.\n", *mod.Title, *mod.Description) - latestVersion, err := getLatestVersion(mod.ID, pack) + latestVersion, err := getLatestVersion(*mod.ID, pack) if err != nil { return fmt.Errorf("failed to get latest version: %v", err) } - if latestVersion.ID == "" { + if latestVersion.ID == nil { return errors.New("mod is not available for this Minecraft version (use the acceptable-game-versions option to accept more) or mod loader") } return installVersion(mod, latestVersion, pack) } -func installVersion(mod Mod, version Version, pack core.Pack) error { +func installVersion(mod *modrinthApi.Project, version *modrinthApi.Version, pack core.Pack) error { var files = version.Files if len(files) == 0 { @@ -164,13 +164,13 @@ func installVersion(mod Mod, version Version, pack core.Pack) error { var file = files[0] // Prefer the primary file for _, v := range files { - if v.Primary { + if *v.Primary { file = v } } //Install the file - fmt.Printf("Installing %s from version %s\n", file.Filename, version.VersionNumber) + fmt.Printf("Installing %s from version %s\n", *file.Filename, *version.VersionNumber) index, err := pack.LoadIndex() if err != nil { return err @@ -179,29 +179,29 @@ func installVersion(mod Mod, version Version, pack core.Pack) error { updateMap := make(map[string]map[string]interface{}) updateMap["modrinth"], err = mrUpdateData{ - ModID: mod.ID, - InstalledVersion: version.ID, + ModID: *mod.ID, + InstalledVersion: *version.ID, }.ToMap() if err != nil { return err } - side := mod.getSide() + side := getSide(mod) if side == "" { - return errors.New("version doesn't have a side that's supported. Server: " + mod.ServerSide + " Client: " + mod.ClientSide) + return errors.New("version doesn't have a side that's supported. Server: " + *mod.ServerSide + " Client: " + *mod.ClientSide) } - algorithm, hash := file.getBestHash() + algorithm, hash := getBestHash(file) if algorithm == "" { return errors.New("file doesn't have a hash") } modMeta := core.Mod{ - Name: mod.Title, - FileName: file.Filename, + Name: *mod.Title, + FileName: *file.Filename, Side: side, Download: core.ModDownload{ - URL: file.Url, + URL: *file.URL, HashFormat: algorithm, Hash: hash, }, @@ -212,10 +212,10 @@ func installVersion(mod Mod, version Version, pack core.Pack) error { if folder == "" { folder = "mods" } - if mod.Slug != "" { - path = modMeta.SetMetaPath(filepath.Join(viper.GetString("meta-folder-base"), folder, mod.Slug+core.MetaExtension)) + if mod.Slug != nil { + path = modMeta.SetMetaPath(filepath.Join(viper.GetString("meta-folder-base"), folder, *mod.Slug+core.MetaExtension)) } else { - path = modMeta.SetMetaPath(filepath.Join(viper.GetString("meta-folder-base"), folder, mod.Title+core.MetaExtension)) + path = modMeta.SetMetaPath(filepath.Join(viper.GetString("meta-folder-base"), folder, *mod.Title+core.MetaExtension)) } // If the file already exists, this will overwrite it!!! @@ -247,14 +247,14 @@ func installVersion(mod Mod, version Version, pack core.Pack) error { } func installVersionById(versionId string, pack core.Pack) error { - version, err := fetchVersion(versionId) + version, err := mrDefaultClient.Versions.Get(versionId) if err != nil { return fmt.Errorf("failed to fetch version %s: %v", versionId, err) } - mod, err := fetchMod(version.ModID) + mod, err := mrDefaultClient.Projects.Get(*version.ProjectID) if err != nil { - return fmt.Errorf("failed to fetch mod %s: %v", version.ModID, err) + return fmt.Errorf("failed to fetch mod %s: %v", *version.ProjectID, err) } return installVersion(mod, version, pack) diff --git a/modrinth/modrinth.go b/modrinth/modrinth.go index 51f3341..9c2a7f0 100644 --- a/modrinth/modrinth.go +++ b/modrinth/modrinth.go @@ -1,227 +1,72 @@ package modrinth import ( - "encoding/json" + modrinthApi "codeberg.org/jmansfield/go-modrinth/modrinth" "errors" - "fmt" - "github.com/spf13/viper" - "golang.org/x/exp/slices" - "io/ioutil" - "net/http" - "net/url" - "time" - "github.com/Masterminds/semver/v3" "github.com/packwiz/packwiz/cmd" "github.com/packwiz/packwiz/core" "github.com/spf13/cobra" + "github.com/spf13/viper" + "golang.org/x/exp/slices" + "net/http" ) -const modrinthApiUrl = "https://api.modrinth.com/api/v1/" - -var modrinthApiUrlParsed, _ = url.Parse(modrinthApiUrl) - var modrinthCmd = &cobra.Command{ Use: "modrinth", Aliases: []string{"mr"}, Short: "Manage modrinth-based mods", } +var mrDefaultClient = modrinthApi.NewClient(&http.Client{}) + func init() { cmd.Add(modrinthCmd) core.Updaters["modrinth"] = mrUpdater{} + + mrDefaultClient.UserAgent = core.UserAgent } -type License struct { - Id string `json:"id"` //The license id of a mod, retrieved from the licenses get route - Name string `json:"name"` //The long for name of a license - Url string `json:"url"` //The URL to this license -} - -type Mod struct { - ID string `json:"id"` //The ID of the mod, encoded as a base62 string - Slug string `json:"slug"` //The slug of a mod, used for vanity URLs - Team string `json:"team"` //The id of the team that has ownership of this mod - Title string `json:"title"` //The title or name of the mod - Description string `json:"description"` //A short description of the mod - Body string `json:"body"` //A long form description of the mod. - BodyUrl string `json:"body_url"` //DEPRECATED The link to the long description of the mod (Optional) - Published string `json:"published"` //The date at which the mod was first published - Updated string `json:"updated"` //The date at which the mod was updated - Status string `json:"status"` //The status of the mod - approved, rejected, draft, unlisted, processing, or unknown - License struct { //The license of the mod - ID string `json:"id"` - Name string `json:"name"` - URL string `json:"url"` - } `json:"license"` - ClientSide string `json:"client_side"` //The support range for the client mod - required, optional, unsupported, or unknown - ServerSide string `json:"server_side"` //The support range for the server mod - required, optional, unsupported, or unknown - Downloads uint32 `json:"downloads"` //The total number of downloads the mod has - Categories []string `json:"categories"` //A list of the categories that the mod is in - Versions []string `json:"versions"` //A list of ids for versions of the mod - IconUrl string `json:"icon_url"` //The URL of the icon of the mod (Optional) - IssuesUrl string `json:"issues_url"` //An optional link to where to submit bugs or issues with the mod (Optional) - SourceUrl string `json:"source_url"` //An optional link to the source code for the mod (Optional) - WikiUrl string `json:"wiki_url"` //An optional link to the mod's wiki page or other relevant information (Optional) - DiscordUrl string `json:"discord_url"` //An optional link to the mod's discord (Optional) -} - -type ModResult struct { - ModID string `json:"mod_id"` //The id of the mod; prefixed with local- - ProjectType string `json:"project_id"` //The project type of the mod - Author string `json:"author"` //The username of the author of the mod - Title string `json:"title"` //The name of the mod - Description string `json:"description"` //A short description of the mod - Categories []string `json:"categories"` //A list of the categories the mod is in - Versions []string `json:"versions"` //A list of the minecraft versions supported by the mod - Downloads uint32 `json:"downloads"` //The total number of downloads for the mod - PageUrl string `json:"page_url"` //A link to the mod's main page; - IconUrl string `json:"icon_url"` //The url of the mod's icon - AuthorUrl string `json:"author_url"` //The url of the mod's author - DateCreated string `json:"date_created"` //The date that the mod was originally created - DateModified string `json:"date_modified"` //The date that the mod was last modified - LatestVersion string `json:"latest_version"` //The latest version of minecraft that this mod supports - License string `json:"license"` //The id of the license this mod follows - ClientSide string `json:"client_side"` //The side type id that this mod is on the client - ServerSide string `json:"server_side"` //The side type id that this mod is on the server - Host string `json:"host"` //The host that this mod is from, always modrinth -} - -type ModSearchResult struct { - Hits []ModResult `json:"hits"` //The list of results - Offset uint32 `json:"offset"` //The number of results that were skipped by the query - Limit uint32 `json:"limit"` //The number of mods returned by the query - TotalHits uint32 `json:"total_hits"` //The total number of mods that the query found -} - -type Version struct { - ID string `json:"id"` //The ID of the version, encoded as a base62 string - ModID string `json:"mod_id"` //The ID of the mod this version is for - AuthorId string `json:"author_id"` //The ID of the author who published this version - Featured bool `json:"featured"` //Whether the version is featured or not - Name string `json:"name"` //The name of this version - VersionNumber string `json:"version_number"` //The version number. Ideally will follow semantic versioning - Changelog string `json:"changelog"` //The changelog for this version of the mod. (Optional) - DatePublished string `json:"date_published"` //The date that this version was published - Downloads uint32 `json:"downloads"` //The number of downloads this specific version has - VersionType string `json:"version_type"` //The type of the release - alpha, beta, or release - Files []VersionFile `json:"files"` //A list of files available for download for this version - //Dependencies []string `json:"dependencies"` //A list of specific versions of mods that this version depends on - GameVersions []string `json:"game_versions"` //A list of versions of Minecraft that this version of the mod supports - Loaders []string `json:"loaders"` //The mod loaders that this version supports -} - -type VersionFile struct { - Hashes map[string]string //A map of hashes of the file. The key is the hashing algorithm and the value is the string version of the hash. - Url string //A direct link to the file - Filename string //The name of the file - Primary bool // Is the file the primary file? -} - -func getModIdsViaSearch(query string, versions []string) ([]ModResult, error) { - baseUrl := *modrinthApiUrlParsed - baseUrl.Path += "mod" - - params := url.Values{} - params.Add("limit", "5") - params.Add("index", "relevance") +func getModIdsViaSearch(query string, versions []string) ([]*modrinthApi.SearchResult, error) { facets := make([]string, 0) for _, v := range versions { facets = append(facets, "versions:"+v) } - facetsEncoded, err := json.Marshal(facets) + + res, err := mrDefaultClient.Projects.Search(&modrinthApi.SearchOptions{ + Limit: 5, + Index: "relevance", + // Filters by mod since currently only mods and modpacks are supported by Modrinth + Facets: [][]string{facets, {"project_type:mod"}}, + Query: query, + }) + if err != nil { - return []ModResult{}, err + return nil, err } - params.Add("facets", "["+string(facetsEncoded)+"]") - params.Add("query", query) - - baseUrl.RawQuery = params.Encode() - - resp, err := http.Get(baseUrl.String()) - if err != nil { - return []ModResult{}, err - } - - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return []ModResult{}, err - } - - var result ModSearchResult - err = json.Unmarshal(body, &result) - if err != nil { - return []ModResult{}, err - } - - if result.TotalHits <= 0 { - return []ModResult{}, errors.New("Couldn't find that mod. Is it available for this version?") - } - - return result.Hits, nil + return res.Hits, nil } -func getLatestVersion(modID string, pack core.Pack) (Version, error) { +func getLatestVersion(modID string, pack core.Pack) (*modrinthApi.Version, error) { mcVersion, err := pack.GetMCVersion() if err != nil { - return Version{}, err + return nil, err } gameVersions := append([]string{mcVersion}, viper.GetStringSlice("acceptable-game-versions")...) - gameVersionsEncoded, err := json.Marshal(gameVersions) - if err != nil { - return Version{}, err - } - loadersEncoded, err := json.Marshal(pack.GetLoaders()) - if err != nil { - return Version{}, err - } - - baseUrl := *modrinthApiUrlParsed - baseUrl.Path += "mod/" - baseUrl.Path += modID - baseUrl.Path += "/version" - - params := url.Values{} - params.Add("game_versions", string(gameVersionsEncoded)) - params.Add("loaders", string(loadersEncoded)) - - baseUrl.RawQuery = params.Encode() - - resp, err := http.Get(baseUrl.String()) - if err != nil { - return Version{}, err - } - - if resp.StatusCode == 404 { - return Version{}, fmt.Errorf("mod not found (for URL %v)", baseUrl) - } - - if resp.StatusCode != 200 { - return Version{}, fmt.Errorf("invalid response status %v for URL %v", resp.Status, baseUrl) - } - - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return Version{}, err - } - - var result []Version - err = json.Unmarshal(body, &result) - if err != nil { - return Version{}, err - } + result, err := mrDefaultClient.Versions.ListVersions(modID, modrinthApi.ListVersionsOptions{ + GameVersions: gameVersions, + Loaders: pack.GetLoaders(), + }) if len(result) == 0 { - return Version{}, errors.New("no valid versions found") + return nil, errors.New("no valid versions found") } latestValidVersion := result[0] for _, v := range result[1:] { - currVersion, err1 := semver.NewVersion(v.VersionNumber) - latestVersion, err2 := semver.NewVersion(latestValidVersion.VersionNumber) + currVersion, err1 := semver.NewVersion(*v.VersionNumber) + latestVersion, err2 := semver.NewVersion(*latestValidVersion.VersionNumber) var semverCompare = 0 // Only compare with semver if both are valid semver - otherwise compare by release date if err1 == nil && err2 == nil { @@ -236,9 +81,7 @@ func getLatestVersion(modID string, pack core.Pack) (Version, error) { } //Semver is equal, compare date instead - vDate, _ := time.Parse(time.RFC3339Nano, v.DatePublished) - latestDate, _ := time.Parse(time.RFC3339Nano, latestValidVersion.DatePublished) - if vDate.After(latestDate) { + if v.DatePublished.After(*latestValidVersion.DatePublished) { latestValidVersion = v } } else if semverCompare == 1 { @@ -249,77 +92,9 @@ func getLatestVersion(modID string, pack core.Pack) (Version, error) { return latestValidVersion, nil } -func fetchMod(modID string) (Mod, error) { - var mod Mod - - resp, err := http.Get(modrinthApiUrl + "mod/" + modID) - if err != nil { - return mod, err - } - - if resp.StatusCode == 404 { - return mod, fmt.Errorf("mod not found (for URL %v)", modrinthApiUrl+"mod/"+modID) - } - - if resp.StatusCode != 200 { - return mod, fmt.Errorf("invalid response status %v for URL %v", resp.Status, modrinthApiUrl+"mod/"+modID) - } - - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return mod, err - } - - err = json.Unmarshal(body, &mod) - if err != nil { - return mod, err - } - - if mod.ID == "" { - return mod, errors.New("invalid json whilst fetching mod: " + modID) - } - - return mod, nil -} - -func fetchVersion(versionId string) (Version, error) { - var version Version - - resp, err := http.Get(modrinthApiUrl + "version/" + versionId) - if err != nil { - return version, err - } - - if resp.StatusCode == 404 { - return version, fmt.Errorf("version not found (for URL %v)", modrinthApiUrl+"version/"+versionId) - } - - if resp.StatusCode != 200 { - return version, fmt.Errorf("invalid response status %v for URL %v", resp.Status, modrinthApiUrl+"version/"+versionId) - } - - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return version, err - } - - err = json.Unmarshal(body, &version) - if err != nil { - return version, err - } - - if version.ID == "" { - return version, errors.New("invalid json whilst fetching version: " + versionId) - } - - return version, nil -} - -func (mod Mod) getSide() string { - server := shouldDownloadOnSide(mod.ServerSide) - client := shouldDownloadOnSide(mod.ClientSide) +func getSide(mod *modrinthApi.Project) string { + server := shouldDownloadOnSide(*mod.ServerSide) + client := shouldDownloadOnSide(*mod.ClientSide) if server && client { return core.UniversalSide @@ -336,7 +111,7 @@ func shouldDownloadOnSide(side string) bool { return side == "required" || side == "optional" } -func (v VersionFile) getBestHash() (string, string) { +func getBestHash(v *modrinthApi.File) (string, string) { // Try preferred hashes first; SHA1 is first as it is required for Modrinth pack exporting val, exists := v.Hashes["sha1"] if exists { diff --git a/modrinth/updater.go b/modrinth/updater.go index e927ccb..1c1f993 100644 --- a/modrinth/updater.go +++ b/modrinth/updater.go @@ -1,6 +1,7 @@ package modrinth import ( + modrinthApi "codeberg.org/jmansfield/go-modrinth/modrinth" "errors" "fmt" @@ -29,7 +30,7 @@ func (u mrUpdater) ParseUpdate(updateUnparsed map[string]interface{}) (interface type cachedStateStore struct { ModID string - Version Version + Version *modrinthApi.Version } func (u mrUpdater) CheckUpdate(mods []core.Mod, mcVersion string, pack core.Pack) ([]core.UpdateCheck, error) { @@ -50,12 +51,7 @@ func (u mrUpdater) CheckUpdate(mods []core.Mod, mcVersion string, pack core.Pack continue } - if newVersion.ID == "" { //There is no version available for this minecraft version or loader. - results[i] = core.UpdateCheck{UpdateAvailable: false} - continue - } - - if newVersion.ID == data.InstalledVersion { //The latest version from the site is the same as the installed one + if *newVersion.ID == data.InstalledVersion { //The latest version from the site is the same as the installed one results[i] = core.UpdateCheck{UpdateAvailable: false} continue } @@ -68,14 +64,14 @@ func (u mrUpdater) CheckUpdate(mods []core.Mod, mcVersion string, pack core.Pack newFilename := newVersion.Files[0].Filename // Prefer the primary file for _, v := range newVersion.Files { - if v.Primary { + if *v.Primary { newFilename = v.Filename } } results[i] = core.UpdateCheck{ UpdateAvailable: true, - UpdateString: mod.FileName + " -> " + newFilename, + UpdateString: mod.FileName + " -> " + *newFilename, CachedState: cachedStateStore{data.ModID, newVersion}, } } @@ -91,19 +87,19 @@ func (u mrUpdater) DoUpdate(mods []*core.Mod, cachedState []interface{}) error { var file = version.Files[0] // Prefer the primary file for _, v := range version.Files { - if v.Primary { + if *v.Primary { file = v } } - algorithm, hash := file.getBestHash() + algorithm, hash := getBestHash(file) if algorithm == "" { return errors.New("file for mod " + mod.Name + " doesn't have a hash") } - mod.FileName = file.Filename + mod.FileName = *file.Filename mod.Download = core.ModDownload{ - URL: file.Url, + URL: *file.URL, HashFormat: algorithm, Hash: hash, }