181 lines
4.4 KiB
Go
181 lines
4.4 KiB
Go
package steamimmich
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"slices"
|
|
"sort"
|
|
"strconv"
|
|
)
|
|
|
|
type Config struct {
|
|
BaseURL string
|
|
APIKey string
|
|
UserdataDir string
|
|
DeiveID string
|
|
Album string
|
|
CacheFile string
|
|
}
|
|
|
|
func Run(config Config) int {
|
|
screenshotFiles, err := listScreenshots(config.UserdataDir)
|
|
|
|
if err != nil {
|
|
slog.Error("Failed to scan for screenshots", "err", err)
|
|
return 1
|
|
}
|
|
|
|
if len(screenshotFiles) == 0 {
|
|
slog.Info("No screenshots found")
|
|
return 0
|
|
}
|
|
|
|
immichClient := newImmichHttpClient(config.APIKey)
|
|
|
|
localState, err := loadLocalState(config.CacheFile)
|
|
if err != nil {
|
|
slog.Error("Failed to load local cache", "err", err)
|
|
return 1
|
|
}
|
|
|
|
for appid, files := range screenshotFiles {
|
|
gameName := getGameName(appid, localState.GameIDs)
|
|
assetsInGame := make([]string, 0)
|
|
|
|
for _, filepath := range files {
|
|
|
|
idx := slices.IndexFunc(localState.Assets, func(e assetState) bool { return e.FilePath == filepath })
|
|
|
|
if idx != -1 {
|
|
slog.Debug("Asset already uploaded", "path", filepath)
|
|
continue
|
|
}
|
|
|
|
res, err := uploadToImmich(filepath, appid, config.BaseURL, config.DeiveID, &immichClient)
|
|
|
|
if err != nil {
|
|
slog.Error("Failed to upload to immich", "err", err)
|
|
return 1
|
|
}
|
|
|
|
slog.Info("Assets uploaded", "path", filepath, "id", res.ID)
|
|
|
|
localState.Assets = append(localState.Assets, assetState{FilePath: filepath, ImmichID: res.ID})
|
|
|
|
assetsInGame = append(assetsInGame, res.ID)
|
|
}
|
|
|
|
err := setAssetMetadata(assetsInGame, fmt.Sprintf("Game: %s", gameName), config.BaseURL, immichClient)
|
|
if err != nil {
|
|
slog.Error("Failed to set assets metadata", "err", err)
|
|
return 1
|
|
}
|
|
|
|
if config.Album != "" {
|
|
err = addAssetsToAlbum(assetsInGame, config.Album, config.BaseURL, immichClient)
|
|
if err != nil {
|
|
slog.Error("Failed to add assets to album", "err", err)
|
|
return 1
|
|
}
|
|
|
|
if len(assetsInGame) > 0 {
|
|
slog.Info("Added assets to album", "count", len(assetsInGame))
|
|
}
|
|
}
|
|
}
|
|
|
|
slog.Info("Finished uploading screenshots")
|
|
err = saveLocalState(config.CacheFile, *localState)
|
|
if err != nil {
|
|
slog.Error("Failed to save local cache", "err", err)
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func Revalidate(config Config) int {
|
|
immichClient := newImmichHttpClient(config.APIKey)
|
|
|
|
localState, err := loadLocalState(config.CacheFile)
|
|
if err != nil {
|
|
slog.Error("Failed to load local cache", "err", err)
|
|
|
|
return 1
|
|
}
|
|
|
|
hashes, err := hashFiles(localState.Assets)
|
|
if err != nil {
|
|
slog.Error("Failed to generate hashes", "err", err)
|
|
return 1
|
|
}
|
|
|
|
idxToRemoveFromCache := []int{}
|
|
|
|
rejected := 0
|
|
updated := 0
|
|
missingFromImmich := 0
|
|
missingFile := 0
|
|
|
|
// Prepare payload
|
|
var idsToCheck = []assetBulkUploadCheckItem{}
|
|
for i := range localState.Assets {
|
|
hash := hashes[i]
|
|
if hash == "" {
|
|
idxToRemoveFromCache = append(idxToRemoveFromCache, i)
|
|
missingFile += 1
|
|
} else {
|
|
idsToCheck = append(idsToCheck, assetBulkUploadCheckItem{Checksum: hashes[i], Id: strconv.Itoa(i)})
|
|
}
|
|
}
|
|
|
|
results, err := bulkCheckAssets(idsToCheck, config.BaseURL, immichClient)
|
|
if err != nil {
|
|
slog.Error("Failed to bulk check hashes", "err", err)
|
|
return 1
|
|
}
|
|
|
|
for _, result := range results {
|
|
i, err := strconv.Atoi(result.Id)
|
|
if err != nil {
|
|
slog.Error("What ? This should never happen", "err", err)
|
|
return 1
|
|
}
|
|
|
|
asset := localState.Assets[i]
|
|
|
|
if asset.ImmichID != result.AssetId {
|
|
slog.Info("Asset has the wrong Immich ID", "path", asset.FilePath, "id", result.AssetId)
|
|
localState.Assets[i].ImmichID = result.AssetId
|
|
updated += 1
|
|
}
|
|
|
|
if result.Action == "reject" {
|
|
rejected += 1
|
|
} else {
|
|
idxToRemoveFromCache = append(idxToRemoveFromCache, i)
|
|
missingFromImmich += 1
|
|
}
|
|
}
|
|
|
|
sort.Sort(sort.Reverse(sort.IntSlice(idxToRemoveFromCache)))
|
|
for _, idx := range idxToRemoveFromCache {
|
|
localState.Assets[idx] = localState.Assets[len(localState.Assets)-1]
|
|
localState.Assets = localState.Assets[:len(localState.Assets)-1]
|
|
}
|
|
|
|
saveLocalState(config.CacheFile, *localState)
|
|
|
|
slog.Info("Revalidated assets", "count", len(localState.Assets))
|
|
slog.Info("Assets present on immich", "count", rejected)
|
|
slog.Info("Wrong metadata on cache", "count", updated)
|
|
slog.Info("Missing on Immich", "count", missingFromImmich)
|
|
slog.Info("Missing on local storage", "count", missingFile)
|
|
|
|
if (updated + missingFromImmich + missingFromImmich) > 0 {
|
|
slog.Info("Fixed cache. Run the normal upload command again. If error persists try removing cache file", "path", config.CacheFile)
|
|
}
|
|
|
|
return 0
|
|
}
|