diff --git a/README.md b/README.md index 20df19d..90a4f57 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ again when you get more games or want to update the category overlays. * *(optional)* Append `--skipCategory ` to skip processing of games assigned to a specific Steam category * *(optional)* Append `--steamgriddbonly` to search for artwork only in SteamGridDB * *(optional)* Append `--namefilter ""` to process only games with names that contain provided *text* + * *(optional)* Append `--searchcustomgames` if you have non-steam games that have a page in the steam store * *(tip)* Run with `--help` to see all available options again. 6. Read the report and open Steam in grid view to check the results. diff --git a/custom-games.go b/custom-games.go new file mode 100644 index 0000000..e2903b2 --- /dev/null +++ b/custom-games.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "io" + "net/url" + "regexp" +) + +const steamSearchUrl = `https://store.steampowered.com/search/?category1=998%2C994%2C21%2C10%2C997&term=` // category is set to exclude bundles +const regexPattern = `https:\/\/store\.steampowered\.com\/app\/(\d*)\/` + +// updateIdForCustomGames searches Steam Store using the game name and updates the game's ID. +// For non-steam games that have page in steam store, we can update their ID and download art from steam server as normal +func updateIdForCustomGames(game *Game) error { + // Search for the game on Steam + formattedUrl := steamSearchUrl + url.QueryEscape(game.Name) + response, err := tryDownload(formattedUrl) + if err != nil || response == nil { + return fmt.Errorf("downloading html: %w", err) + } + // Read the response body + defer response.Body.Close() + htmlBytes, err2 := io.ReadAll(response.Body) + if err2 != nil { + return fmt.Errorf("reading response body: %w", err2) + } + html := string(htmlBytes) + // Get the first match using regex + re, err := regexp.Compile(regexPattern) + if err != nil { + return fmt.Errorf("invalid regex pattern: %w", err) + } + matches := re.FindStringSubmatch(html) + if len(matches) < 2 { + return fmt.Errorf("no match or no capture group found") + } + game.ID = matches[1] + fmt.Println("Found a custom game on Steam:", game.Name, ", Id:", game.ID) + return nil +} diff --git a/games.go b/games.go index 7546c32..0e3afb4 100644 --- a/games.go +++ b/games.go @@ -32,6 +32,8 @@ type Game struct { Custom bool // LegacyID used in BigPicture LegacyID uint64 + // For steam games it's the same as ID, but for non-steam games it's the shortcut ID (in case ID get overwritten by updateIdForCustomGames) + OriginalID string } // Pattern of game declarations in the public profile. It's actually JSON @@ -53,7 +55,7 @@ func addGamesFromProfile(user User, games map[string]*Game) (err error) { gameID := groups[1] gameName := groups[2] tags := []string{""} - games[gameID] = &Game{gameID, gameName, tags, "", nil, nil, "", false, 0} + games[gameID] = &Game{gameID, gameName, tags, "", nil, nil, "", false, 0, gameID} } return @@ -91,7 +93,7 @@ func addUnknownGames(user User, games map[string]*Game, skipCategory string) { // If for some reason it wasn't included in the profile, create a new // entry for it now. Unfortunately we don't have a name. gameName := "" - games[gameID] = &Game{gameID, gameName, []string{tag}, "", nil, nil, "", false, 0} + games[gameID] = &Game{gameID, gameName, []string{tag}, "", nil, nil, "", false, 0, gameID} } if len(skipCategory) > 0 && strings.Contains(strings.ToLower(tag), strings.ToLower(skipCategory)) { @@ -130,7 +132,7 @@ func addNonSteamGames(user User, games map[string]*Game, skipCategory string) { uniqueName := bytes.Join([][]byte{target, gameName}, []byte("")) LegacyID := uint64(crc32.ChecksumIEEE(uniqueName)) | 0x80000000 - game := Game{gameID, string(gameName), []string{}, "", nil, nil, "", true, LegacyID} + game := Game{gameID, string(gameName), []string{}, "", nil, nil, "", true, LegacyID, gameID} games[gameID] = &game tagsText := gameGroups[4] @@ -153,7 +155,7 @@ func GetGames(user User, nonSteamOnly bool, appIDs string, skipCategory string) if appIDs != "" { for _, appID := range strings.Split(appIDs, ",") { - games[appID] = &Game{appID, "", []string{}, "", nil, nil, "", false, 0} + games[appID] = &Game{appID, "", []string{}, "", nil, nil, "", false, 0, appID} } return games } diff --git a/steamgrid.go b/steamgrid.go index 2702c79..9fd403e 100644 --- a/steamgrid.go +++ b/steamgrid.go @@ -58,6 +58,7 @@ func startApplication() { skipCategory := flag.String("skipcategory", "", "Name of the category with games to skip during processing") steamgriddbonly := flag.Bool("steamgriddbonly", false, "Search for artwork only in SteamGridDB") nameFilter := flag.String("namefilter", "", "Process only games with name that contains this value") + searchCustomGames := flag.Bool("searchcustomgames", false, "Try to find non-steam games on Steam Store using their game name") flag.Parse() if flag.NArg() == 1 { steamDir = &flag.Args()[0] @@ -177,6 +178,13 @@ func startApplication() { for _, game := range games { i++ + if game.Custom && *searchCustomGames && game.Name != "" { + err := updateIdForCustomGames(game) + if err != nil { + fmt.Println("Error updating id for custom games: " + err.Error()) + } + } + var name string if game.Name == "" { game.Name = getGameName(game.ID) @@ -271,7 +279,7 @@ func startApplication() { errorAndExit(err) } - imagePath := filepath.Join(gridDir, game.ID+artStyleExtensions[0]+game.ImageExt) + imagePath := filepath.Join(gridDir, game.OriginalID+artStyleExtensions[0]+game.ImageExt) err = ioutil.WriteFile(imagePath, game.OverlayImageBytes, 0666) // Copy with legacy naming for Big Picture mode