Compare commits
2 commits
baccafb8ce
...
527994abba
| Author | SHA1 | Date | |
|---|---|---|---|
| 527994abba | |||
| 226c6a0e93 |
6 changed files with 189 additions and 20 deletions
143
gamebanana/download.go
Normal file
143
gamebanana/download.go
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
package gamebanana
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/log"
|
||||||
|
"github.com/gen2brain/go-unarr"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dlurl string
|
||||||
|
|
||||||
|
func DownloadGameBananaMod(id, outdir string) (error) {
|
||||||
|
modData, err := GetGameBananaMod(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create temporary file to download mod to
|
||||||
|
tmpFolderDownload, err := os.MkdirTemp("", "diva-mod-download-*")
|
||||||
|
log.Info("created temp folder for downloading", "name", tmpFolderDownload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// really really jank, but api doesn't really have a better way of doing this
|
||||||
|
for _, x := range modData.FilesAFiles {
|
||||||
|
dlurl = x.SDownloadURL
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
fileDlPath := path.Join(tmpFolderDownload, "download")
|
||||||
|
fileDl, err := os.Create(fileDlPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fileDl.Close()
|
||||||
|
|
||||||
|
// download mod
|
||||||
|
resp, err := http.Get(dlurl)
|
||||||
|
log.Info("downloading mod", "url", dlurl, "modid", id, "path", fileDl)
|
||||||
|
// TODO: handle error code properly
|
||||||
|
if err != nil || resp.StatusCode != 200 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// save to temp file
|
||||||
|
_, err = io.Copy(fileDl, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract files to same directory
|
||||||
|
a, err := unarr.NewArchive(fileDlPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = a.Extract(tmpFolderDownload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// such jank but ive spent wayyy too long on this
|
||||||
|
var moddir string = ""
|
||||||
|
err = filepath.Walk(tmpFolderDownload, func(filePath string, _ fs.FileInfo, err error) error {
|
||||||
|
if path.Base(filePath) == "config.toml" {
|
||||||
|
moddir = path.Dir(filePath)
|
||||||
|
log.Info("found mod folder", "configloc", filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if moddir == "" {
|
||||||
|
errors.New("config.toml not found in mod dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a folder put the mod in
|
||||||
|
outpath := path.Join(outdir, fmt.Sprintf("%s@%d", id, modData.Udate))
|
||||||
|
err = os.Mkdir(outpath, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = filepath.Walk(moddir, func(filePath string, fileinfo fs.FileInfo, err error) error {
|
||||||
|
relativepath := strings.Replace(filePath, moddir, "", 1)
|
||||||
|
newpath := path.Join(outdir, relativepath)
|
||||||
|
|
||||||
|
if relativepath == "" { return nil }
|
||||||
|
|
||||||
|
if fileinfo.IsDir() == true {
|
||||||
|
log.Warn("found directory, creating it!", "path", filePath, "relpath", relativepath, "newpath", newpath)
|
||||||
|
err = os.Mkdir(newpath, 0744)
|
||||||
|
if err != nil { return err }
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var src *os.File
|
||||||
|
var dst *os.File
|
||||||
|
|
||||||
|
if src, err = os.Open(filePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
|
||||||
|
if dst, err = os.Create(newpath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dst.Close()
|
||||||
|
|
||||||
|
if _, err = io.Copy(dst, src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("copied file!!!", "src", filePath, "dst", newpath, "relativepath", relativepath)
|
||||||
|
return os.Chmod(newpath, 0744)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: cleanup temp files
|
||||||
|
err = os.RemoveAll(fileDlPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
24
gamebanana/download_test.go
Normal file
24
gamebanana/download_test.go
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
package gamebanana
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDownloadGameBananaMod(t *testing.T) {
|
||||||
|
tmpdir := t.TempDir()
|
||||||
|
err := DownloadGameBananaMod("602180", tmpdir)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("failed to download game banana mod", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// udate is the time the mod was last updated
|
||||||
|
// in unix time, used here as a version number
|
||||||
|
// modID@udate
|
||||||
|
expectedPathToExist := path.Join(tmpdir, "602180@1750696716")
|
||||||
|
|
||||||
|
if _, err := os.Stat(expectedPathToExist); err != nil {
|
||||||
|
t.Error("mod doesn't have expected name or doesn't exist")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,24 +29,7 @@ type GameBananaMod struct {
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Downloads int `json:"downloads"`
|
Downloads int `json:"downloads"`
|
||||||
FeedbackInstructions string `json:"feedback_instructions"`
|
FeedbackInstructions string `json:"feedback_instructions"`
|
||||||
FilesAFiles struct {
|
FilesAFiles map[string]gamebananaDownloadInformation `json:"Files().aFiles()"`
|
||||||
Num884808 struct {
|
|
||||||
IDRow string `json:"_idRow"`
|
|
||||||
SFile string `json:"_sFile"`
|
|
||||||
NFilesize int `json:"_nFilesize"`
|
|
||||||
TsDateAdded int `json:"_tsDateAdded"`
|
|
||||||
NDownloadCount int `json:"_nDownloadCount"`
|
|
||||||
SDownloadURL string `json:"_sDownloadUrl"`
|
|
||||||
SMd5Checksum string `json:"_sMd5Checksum"`
|
|
||||||
SAnalysisState string `json:"_sAnalysisState"`
|
|
||||||
SAnalysisResult string `json:"_sAnalysisResult"`
|
|
||||||
SAnalysisResultVerbose string `json:"_sAnalysisResultVerbose"`
|
|
||||||
SAvState string `json:"_sAvState"`
|
|
||||||
SAvResult string `json:"_sAvResult"`
|
|
||||||
BIsArchived bool `json:"_bIsArchived"`
|
|
||||||
BHasContents bool `json:"_bHasContents"`
|
|
||||||
} `json:"884808"`
|
|
||||||
} `json:"Files().aFiles()"`
|
|
||||||
GameName string `json:"Game().name"`
|
GameName string `json:"Game().name"`
|
||||||
InstallInstructions string `json:"install_instructions"`
|
InstallInstructions string `json:"install_instructions"`
|
||||||
IsObsolete string `json:"is_obsolete"`
|
IsObsolete string `json:"is_obsolete"`
|
||||||
|
|
@ -91,6 +74,22 @@ type GameBananaMod struct {
|
||||||
WithholdBIsWithheld bool `json:"Withhold().bIsWithheld()"`
|
WithholdBIsWithheld bool `json:"Withhold().bIsWithheld()"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type gamebananaDownloadInformation struct {
|
||||||
|
IDRow string `json:"_idRow"`
|
||||||
|
SFile string `json:"_sFile"`
|
||||||
|
NFilesize int `json:"_nFilesize"`
|
||||||
|
TsDateAdded int `json:"_tsDateAdded"`
|
||||||
|
NDownloadCount int `json:"_nDownloadCount"`
|
||||||
|
SDownloadURL string `json:"_sDownloadUrl"`
|
||||||
|
SMd5Checksum string `json:"_sMd5Checksum"`
|
||||||
|
SAnalysisState string `json:"_sAnalysisState"`
|
||||||
|
SAnalysisResult string `json:"_sAnalysisResult"`
|
||||||
|
SAnalysisResultVerbose string `json:"_sAnalysisResultVerbose"`
|
||||||
|
SAvState string `json:"_sAvState"`
|
||||||
|
SAvResult string `json:"_sAvResult"`
|
||||||
|
BIsArchived bool `json:"_bIsArchived"`
|
||||||
|
BHasContents bool `json:"_bHasContents"`
|
||||||
|
}
|
||||||
|
|
||||||
func GetGameBananaMod(id string) (GameBananaMod, error) {
|
func GetGameBananaMod(id string) (GameBananaMod, error) {
|
||||||
// build URL params
|
// build URL params
|
||||||
|
|
@ -105,7 +104,7 @@ func GetGameBananaMod(id string) (GameBananaMod, error) {
|
||||||
params.Add("itemid", id)
|
params.Add("itemid", id)
|
||||||
params.Add("format", "json_min")
|
params.Add("format", "json_min")
|
||||||
params.Add("return_keys", "1")
|
params.Add("return_keys", "1")
|
||||||
params.Add("fields", "apps_used,authors,Category().name,catid,contestid,creator,Credits().aAuthors(),Credits().aAuthorsAndGroups(),Credits().ssvAuthorNames(),date,description,downloads,feedback_instructions,Files().aFiles(),Game().name,install_instructions,is_obsolete,lastpost_date,lastpost_userid,likes,mdate,modnote,name,Nsfw().bIsNsfw(),obsol_notice,Owner().name,postcount,Posts().LastPost().idPosterRow(),Posts().LastPost().sText(),Posts().LastPost().tsDateAdded(),Posts().Postcount().nPostCount(),Preview().sStructuredDataFullsizeUrl(),Preview().sSubFeedImageUrl(),RootCategory().id,RootCategory().name,screenshots,studioid,text,Trash().bIsTrashed(),udate,Updates().aGetLatestUpdates(),Updates().aLatestUpdates(),Updates().bSubmissionHasUpdates(),Updates().nUpdatesCount(),Url().sDownloadUrl(),Url().sEditUrl(),Url().sEmbeddablesUrl(),Url().sHistoryUrl(),Url().sProfileUrl(),Url().sTrashUrl(),Url().sUntrashUrl(),Url().sUpdatesUrl(),Url().sWithholdUrl(),userid,views,Withhold().bIsWithheld()")
|
params.Add("fields", "apps_used,authors,Category().name,catid,contestid,creator,Credits().ssvAuthorNames(),date,description,downloads,feedback_instructions,Files().aFiles(),Game().name,install_instructions,is_obsolete,lastpost_date,lastpost_userid,likes,mdate,modnote,name,Nsfw().bIsNsfw(),obsol_notice,Owner().name,postcount,Posts().LastPost().idPosterRow(),Posts().LastPost().sText(),Posts().LastPost().tsDateAdded(),Posts().Postcount().nPostCount(),Preview().sStructuredDataFullsizeUrl(),Preview().sSubFeedImageUrl(),RootCategory().id,RootCategory().name,screenshots,studioid,text,Trash().bIsTrashed(),udate,Updates().aGetLatestUpdates(),Updates().aLatestUpdates(),Updates().bSubmissionHasUpdates(),Updates().nUpdatesCount(),Url().sDownloadUrl(),Url().sEditUrl(),Url().sEmbeddablesUrl(),Url().sHistoryUrl(),Url().sProfileUrl(),Url().sTrashUrl(),Url().sUntrashUrl(),Url().sUpdatesUrl(),Url().sWithholdUrl(),userid,views,Withhold().bIsWithheld()")
|
||||||
base.RawQuery = params.Encode()
|
base.RawQuery = params.Encode()
|
||||||
|
|
||||||
// send request
|
// send request
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
1
go.mod
1
go.mod
|
|
@ -4,6 +4,7 @@ go 1.24.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/charmbracelet/log v0.4.2
|
github.com/charmbracelet/log v0.4.2
|
||||||
|
github.com/gen2brain/go-unarr v0.2.4
|
||||||
github.com/google/go-cmp v0.5.8
|
github.com/google/go-cmp v0.5.8
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -14,6 +14,8 @@ github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQ
|
||||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/gen2brain/go-unarr v0.2.4 h1:Iu2kqtGfkLBSQoTFwMkSCmp0g3GrEM/XMVWzo9TQr/Y=
|
||||||
|
github.com/gen2brain/go-unarr v0.2.4/go.mod h1:0kdy3HtjKBcEaewifXZguHCvt4qD9V8iJCx4FPEOWT8=
|
||||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue