Created internal/cmd folders

This commit is contained in:
Felipe M 2021-05-08 11:37:52 +02:00
parent ef03e13e86
commit d27e4947ae
Signed by: fmartingr
GPG Key ID: 716BC147715E716F
11 changed files with 71 additions and 67 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
Output Output
Album Album
build

View File

@ -1,2 +1,5 @@
build: clean:
go build ./... rm -rf build
build: clean
go build -o build/games-screenshot-manager cmd/games-screenshot-manager/*.go

View File

@ -0,0 +1,9 @@
package main
import (
"github.com/fmartingr/games-screenshot-manager/internal/cli"
)
func main() {
cli.Start()
}

View File

@ -8,8 +8,8 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/fmartingr/games-screenshot-manager/pkg/games"
"github.com/fmartingr/games-screenshot-manager/pkg/helpers" "github.com/fmartingr/games-screenshot-manager/pkg/helpers"
"github.com/fmartingr/games-screenshot-manager/pkg/providers"
"github.com/fmartingr/games-screenshot-manager/pkg/providers/minecraft" "github.com/fmartingr/games-screenshot-manager/pkg/providers/minecraft"
"github.com/fmartingr/games-screenshot-manager/pkg/providers/nintendo_switch" "github.com/fmartingr/games-screenshot-manager/pkg/providers/nintendo_switch"
"github.com/fmartingr/games-screenshot-manager/pkg/providers/playstation4" "github.com/fmartingr/games-screenshot-manager/pkg/providers/playstation4"
@ -28,7 +28,7 @@ const defaultDryRun bool = false
const defaultDownloadCovers bool = false const defaultDownloadCovers bool = false
func Start() { func Start() {
cliOptions := games.CLIOptions{ cliOptions := providers.ProviderOptions{
OutputPath: flag.String("output-path", defaultOutputPath, "The destination path of the screenshots"), OutputPath: flag.String("output-path", defaultOutputPath, "The destination path of the screenshots"),
InputPath: flag.String("input-path", defaultInputPath, "Input path for the provider that requires it"), InputPath: flag.String("input-path", defaultInputPath, "Input path for the provider that requires it"),
DownloadCovers: flag.Bool("download-covers", defaultDownloadCovers, "use to enable the download of covers (if the provider supports it)"), DownloadCovers: flag.Bool("download-covers", defaultDownloadCovers, "use to enable the download of covers (if the provider supports it)"),
@ -46,8 +46,8 @@ func Start() {
} }
} }
func getGamesFromProvider(provider string, cliOptions games.CLIOptions) []games.Game { func getGamesFromProvider(provider string, cliOptions providers.ProviderOptions) []providers.Game {
var games []games.Game var games []providers.Game
switch provider { switch provider {
case "steam": case "steam":
games = append(games, steam.GetGames(cliOptions)...) games = append(games, steam.GetGames(cliOptions)...)
@ -63,7 +63,7 @@ func getGamesFromProvider(provider string, cliOptions games.CLIOptions) []games.
return games return games
} }
func processGames(games []games.Game, cliOptions games.CLIOptions) { func processGames(games []providers.Game, cliOptions providers.ProviderOptions) {
for _, game := range games { for _, game := range games {
destinationPath := filepath.Join(helpers.ExpandUser(*cliOptions.OutputPath), game.Platform) destinationPath := filepath.Join(helpers.ExpandUser(*cliOptions.OutputPath), game.Platform)
if len(game.Name) > 0 { if len(game.Name) > 0 {
@ -112,7 +112,7 @@ func processGames(games []games.Game, cliOptions games.CLIOptions) {
continue continue
} }
if bytes.Compare(sourceMd5, destinationMd5) != 0 { if !bytes.Equal(sourceMd5, destinationMd5) {
// Images are not equal, we should copy it anyway, but how? // Images are not equal, we should copy it anyway, but how?
log.Println("Found different screenshot with equal timestamp for game ", game.Name, screenshot.Path) log.Println("Found different screenshot with equal timestamp for game ", game.Name, screenshot.Path)
} }

View File

@ -1,9 +0,0 @@
package main
import (
"github.com/fmartingr/games-screenshot-manager/pkg/cli"
)
func main() {
cli.Start()
}

View File

@ -8,11 +8,11 @@ import (
"runtime" "runtime"
"strings" "strings"
"github.com/fmartingr/games-screenshot-manager/pkg/games"
"github.com/fmartingr/games-screenshot-manager/pkg/helpers" "github.com/fmartingr/games-screenshot-manager/pkg/helpers"
"github.com/fmartingr/games-screenshot-manager/pkg/providers"
) )
func getScreenshotsFromPath(game *games.Game, path string) { func getScreenshotsFromPath(game *providers.Game, path string) {
path = helpers.ExpandUser(path) path = helpers.ExpandUser(path)
if _, err := os.Stat(path); !os.IsNotExist(err) { if _, err := os.Stat(path); !os.IsNotExist(err) {
files, err := ioutil.ReadDir(path) files, err := ioutil.ReadDir(path)
@ -22,22 +22,22 @@ func getScreenshotsFromPath(game *games.Game, path string) {
for _, file := range files { for _, file := range files {
if strings.Contains(file.Name(), ".png") { if strings.Contains(file.Name(), ".png") {
game.Screenshots = append(game.Screenshots, games.Screenshot{Path: path + "/" + file.Name(), DestinationName: file.Name()}) game.Screenshots = append(game.Screenshots, providers.Screenshot{Path: path + "/" + file.Name(), DestinationName: file.Name()})
} }
} }
} }
} }
func GetGames(cliOptions games.CLIOptions) []games.Game { func GetGames(cliOptions providers.ProviderOptions) []providers.Game {
var result []games.Game var result []providers.Game
// Standalone minecraft // Standalone minecraft
minecraftStandalone := games.Game{Name: "Minecraft", Platform: "PC", Notes: "Standalone"} minecraftStandalone := providers.Game{Name: "Minecraft", Platform: "PC", Notes: "Standalone"}
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
getScreenshotsFromPath(&minecraftStandalone, "~/.minecraft/screenshots") getScreenshotsFromPath(&minecraftStandalone, "~/.minecraft/screenshots")
// Flatpak minecraft // Flatpak minecraft
minecraftFlatpak := games.Game{Name: "Minecraft", Platform: "PC", Notes: "Flatpak"} minecraftFlatpak := providers.Game{Name: "Minecraft", Platform: "PC", Notes: "Flatpak"}
for _, path := range [2]string{"~/.var/app/com.mojang.Minecraft/.minecraft/screenshots", "~/.var/app/com.mojang.Minecraft/data/minecraft/screenshots"} { for _, path := range [2]string{"~/.var/app/com.mojang.Minecraft/.minecraft/screenshots", "~/.var/app/com.mojang.Minecraft/data/minecraft/screenshots"} {
getScreenshotsFromPath(&minecraftFlatpak, path) getScreenshotsFromPath(&minecraftFlatpak, path)
} }

View File

@ -9,8 +9,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/fmartingr/games-screenshot-manager/pkg/games"
"github.com/fmartingr/games-screenshot-manager/pkg/helpers" "github.com/fmartingr/games-screenshot-manager/pkg/helpers"
"github.com/fmartingr/games-screenshot-manager/pkg/providers"
) )
const providerName = "nintendo-switch" const providerName = "nintendo-switch"
@ -25,7 +25,7 @@ type SwitchGame struct {
func findGameByEncryptedID(gameList []SwitchGame, encryptedGameID string) SwitchGame { func findGameByEncryptedID(gameList []SwitchGame, encryptedGameID string) SwitchGame {
var gameFound SwitchGame = SwitchGame{EncryptedGameID: encryptedGameID} var gameFound SwitchGame = SwitchGame{EncryptedGameID: encryptedGameID}
for _, game := range gameList { for _, game := range gameList {
if strings.ToUpper(game.EncryptedGameID) == strings.ToUpper(encryptedGameID) { if strings.EqualFold(game.EncryptedGameID, encryptedGameID) {
gameFound = game gameFound = game
} }
} }
@ -54,13 +54,13 @@ func getSwitchGameList() []SwitchGame {
log.Fatal(jsonErr) log.Fatal(jsonErr)
} }
log.Printf("Updated Nintendo Switch game list. Found %d games.", len(switchGameList)) log.Printf("Updated Nintendo Switch game list. Found %d providers.", len(switchGameList))
return switchGameList return switchGameList
} }
func addScreenshotToGame(userGames []games.Game, switchGame SwitchGame, screenshot games.Screenshot) []games.Game { func addScreenshotToGame(userGames []providers.Game, switchGame SwitchGame, screenshot providers.Screenshot) []providers.Game {
var foundGame games.Game var foundGame providers.Game
for gameIndex, game := range userGames { for gameIndex, game := range userGames {
if game.ID == switchGame.EncryptedGameID { if game.ID == switchGame.EncryptedGameID {
foundGame = game foundGame = game
@ -69,7 +69,7 @@ func addScreenshotToGame(userGames []games.Game, switchGame SwitchGame, screensh
} }
if foundGame.ID == "" { if foundGame.ID == "" {
foundGame := games.Game{Name: switchGame.Name, ID: switchGame.EncryptedGameID, Platform: platformName, Provider: providerName} foundGame := providers.Game{Name: switchGame.Name, ID: switchGame.EncryptedGameID, Platform: platformName, Provider: providerName}
foundGame.Screenshots = append(foundGame.Screenshots, screenshot) foundGame.Screenshots = append(foundGame.Screenshots, screenshot)
userGames = append(userGames, foundGame) userGames = append(userGames, foundGame)
} }
@ -77,9 +77,9 @@ func addScreenshotToGame(userGames []games.Game, switchGame SwitchGame, screensh
return userGames return userGames
} }
func GetGames(cliOptions games.CLIOptions) []games.Game { func GetGames(cliOptions providers.ProviderOptions) []providers.Game {
switchGames := getSwitchGameList() switchGames := getSwitchGameList()
var userGames []games.Game var userGames []providers.Game
err := filepath.Walk(*cliOptions.InputPath, err := filepath.Walk(*cliOptions.InputPath,
func(path string, info os.FileInfo, err error) error { func(path string, info os.FileInfo, err error) error {
@ -100,7 +100,7 @@ func GetGames(cliOptions games.CLIOptions) []games.Game {
log.Panic("Could not parse filename: ", err) log.Panic("Could not parse filename: ", err)
} }
screenshot := games.Screenshot{Path: path, DestinationName: destinationName.Format(games.DatetimeFormat) + extension} screenshot := providers.Screenshot{Path: path, DestinationName: destinationName.Format(providers.DatetimeFormat) + extension}
userGames = addScreenshotToGame(userGames, switchGame, screenshot) userGames = addScreenshotToGame(userGames, switchGame, screenshot)
} }
return nil return nil

View File

@ -6,16 +6,15 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/fmartingr/games-screenshot-manager/pkg/providers"
"github.com/rwcarlsen/goexif/exif" "github.com/rwcarlsen/goexif/exif"
"github.com/fmartingr/games-screenshot-manager/pkg/games"
) )
const providerName = "playstation-4" const providerName = "playstation-4"
const platformName = "PlayStation 4" const platformName = "PlayStation 4"
func addScreenshotToGame(userGames []games.Game, gameName string, screenshot games.Screenshot) []games.Game { func addScreenshotToGame(userGames []providers.Game, gameName string, screenshot providers.Screenshot) []providers.Game {
var foundGame games.Game var foundGame providers.Game
for gameIndex, game := range userGames { for gameIndex, game := range userGames {
if game.Name == gameName { if game.Name == gameName {
foundGame = game foundGame = game
@ -25,7 +24,7 @@ func addScreenshotToGame(userGames []games.Game, gameName string, screenshot gam
// Game not found // Game not found
if foundGame.Name == "" { if foundGame.Name == "" {
foundGame := games.Game{Name: gameName, ID: gameName, Platform: platformName, Provider: providerName} foundGame := providers.Game{Name: gameName, ID: gameName, Platform: platformName, Provider: providerName}
foundGame.Screenshots = append(foundGame.Screenshots, screenshot) foundGame.Screenshots = append(foundGame.Screenshots, screenshot)
userGames = append(userGames, foundGame) userGames = append(userGames, foundGame)
} }
@ -33,8 +32,8 @@ func addScreenshotToGame(userGames []games.Game, gameName string, screenshot gam
return userGames return userGames
} }
func GetGames(cliOptions games.CLIOptions) []games.Game { func GetGames(cliOptions providers.ProviderOptions) []providers.Game {
var userGames []games.Game var userGames []providers.Game
err := filepath.Walk(*cliOptions.InputPath, err := filepath.Walk(*cliOptions.InputPath,
func(filePath string, info os.FileInfo, err error) error { func(filePath string, info os.FileInfo, err error) error {
@ -63,14 +62,14 @@ func GetGames(cliOptions games.CLIOptions) []games.Game {
defer fileDescriptor.Close() defer fileDescriptor.Close()
exifDateTime, _ := exifData.DateTime() exifDateTime, _ := exifData.DateTime()
destinationName = exifDateTime.Format(games.DatetimeFormat) destinationName = exifDateTime.Format(providers.DatetimeFormat)
} else if extension == ".mp4" { } else if extension == ".mp4" {
if len(fileName) >= len(layout)+len(extension) { if len(fileName) >= len(layout)+len(extension) {
videoDatetime, err := time.Parse(layout, fileName[len(fileName)-len(extension)-len(layout):len(fileName)-len(extension)]) videoDatetime, err := time.Parse(layout, fileName[len(fileName)-len(extension)-len(layout):len(fileName)-len(extension)])
if err == nil { if err == nil {
destinationName = videoDatetime.Format(games.DatetimeFormat) destinationName = videoDatetime.Format(providers.DatetimeFormat)
} else { } else {
log.Printf("[Warning] File does not follow datetime convention: %s. (%s) skipping...", fileName, err) log.Printf("[Warning] File does not follow datetime convention: %s. (%s) skipping...", fileName, err)
return nil return nil
@ -81,7 +80,7 @@ func GetGames(cliOptions games.CLIOptions) []games.Game {
} }
} }
screenshot := games.Screenshot{Path: filePath, DestinationName: destinationName + extension} screenshot := providers.Screenshot{Path: filePath, DestinationName: destinationName + extension}
userGames = addScreenshotToGame(userGames, gameName, screenshot) userGames = addScreenshotToGame(userGames, gameName, screenshot)
} }

View File

@ -23,8 +23,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/fmartingr/games-screenshot-manager/pkg/games"
"github.com/fmartingr/games-screenshot-manager/pkg/helpers" "github.com/fmartingr/games-screenshot-manager/pkg/helpers"
"github.com/fmartingr/games-screenshot-manager/pkg/providers"
) )
const providerName = "retroarch" const providerName = "retroarch"
@ -106,8 +106,8 @@ func readPlaylists(playlistsPath string) map[string]RetroArchPlaylist {
return result return result
} }
func findScreenshotsForGame(item RetroArchPlaylistItem) []games.Screenshot { func findScreenshotsForGame(item RetroArchPlaylistItem) []providers.Screenshot {
var result []games.Screenshot var result []providers.Screenshot
filePath := filepath.Dir(item.Path) filePath := filepath.Dir(item.Path)
fileName := strings.Replace(filepath.Base(item.Path), filepath.Ext(item.Path), "", 1) fileName := strings.Replace(filepath.Base(item.Path), filepath.Ext(item.Path), "", 1)
files, err := ioutil.ReadDir(filePath) files, err := ioutil.ReadDir(filePath)
@ -130,43 +130,43 @@ func findScreenshotsForGame(item RetroArchPlaylistItem) []games.Screenshot {
if strings.Contains(file.Name(), "-cheevo-") { if strings.Contains(file.Name(), "-cheevo-") {
filenameParts := strings.Split(file.Name(), "-") filenameParts := strings.Split(file.Name(), "-")
achievementID := strings.Replace(filenameParts[len(filenameParts)-1], extension, "", 1) achievementID := strings.Replace(filenameParts[len(filenameParts)-1], extension, "", 1)
screenshotDestinationName = file.ModTime().Format(games.DatetimeFormat) + "_retroachievement-" + achievementID + extension screenshotDestinationName = file.ModTime().Format(providers.DatetimeFormat) + "_retroachievement-" + achievementID + extension
} else { } else {
screenshotDate, err := time.Parse(datetimeLayout, file.Name()[len(file.Name())-len(extension)-len(datetimeLayout):len(file.Name())-len(extension)]) screenshotDate, err := time.Parse(datetimeLayout, file.Name()[len(file.Name())-len(extension)-len(datetimeLayout):len(file.Name())-len(extension)])
if err == nil { if err == nil {
screenshotDestinationName = screenshotDate.Format(games.DatetimeFormat) + extension screenshotDestinationName = screenshotDate.Format(providers.DatetimeFormat) + extension
} else { } else {
log.Printf("[error] Formatting screenshot %s: %s", file.Name(), err) log.Printf("[error] Formatting screenshot %s: %s", file.Name(), err)
continue continue
} }
} }
result = append(result, games.Screenshot{Path: filepath.Join(filePath, file.Name()), DestinationName: screenshotDestinationName}) result = append(result, providers.Screenshot{Path: filepath.Join(filePath, file.Name()), DestinationName: screenshotDestinationName})
} }
} }
return result return result
} }
func GetGames(cliOptions games.CLIOptions) []games.Game { func GetGames(cliOptions providers.ProviderOptions) []providers.Game {
var userGames []games.Game var userGames []providers.Game
playlists := readPlaylists(*cliOptions.InputPath) playlists := readPlaylists(*cliOptions.InputPath)
for playlistName := range playlists { for playlistName := range playlists {
for _, item := range playlists[playlistName].Items { for _, item := range playlists[playlistName].Items {
var cover games.Screenshot var cover providers.Screenshot
if *cliOptions.DownloadCovers { if *cliOptions.DownloadCovers {
coverURL := formatLibretroBoxartURL(playlistName, item.Label) coverURL := formatLibretroBoxartURL(playlistName, item.Label)
boxartPath, err := helpers.DownloadURLIntoTempFile(coverURL) boxartPath, err := helpers.DownloadURLIntoTempFile(coverURL)
if err == nil { if err == nil {
cover = games.Screenshot{Path: boxartPath, DestinationName: ".cover"} cover = providers.Screenshot{Path: boxartPath, DestinationName: ".cover"}
} else { } else {
log.Printf("[error] Error downloading cover for %s: %s", item.Label, err) log.Printf("[error] Error downloading cover for %s: %s", item.Label, err)
} }
} }
userGames = append(userGames, games.Game{ userGames = append(userGames, providers.Game{
Platform: cleanPlatformName(playlistName), Platform: cleanPlatformName(playlistName),
Name: cleanGameName(item.Label), Name: cleanGameName(item.Label),
Provider: providerName, Provider: providerName,

View File

@ -3,6 +3,7 @@ package steam
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
@ -11,13 +12,13 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/fmartingr/games-screenshot-manager/pkg/games"
"github.com/fmartingr/games-screenshot-manager/pkg/helpers" "github.com/fmartingr/games-screenshot-manager/pkg/helpers"
"github.com/fmartingr/games-screenshot-manager/pkg/providers"
) )
const providerName string = "steam" const providerName string = "steam"
const gameListURL string = "https://api.steampowered.com/ISteamApps/GetAppList/v2/" const gameListURL string = "https://api.steampowered.com/ISteamApps/GetAppList/v2/"
const baseGameHeaderURL string = "https://cdn.cloudflare.steamstatic.com/steam/apps/{id}/header.jpg" const baseGameHeaderURL string = "https://cdn.cloudflare.steamstatic.com/steam/apps/%s/header.jpg"
type SteamApp struct { type SteamApp struct {
AppID uint64 `json:"appid"` AppID uint64 `json:"appid"`
@ -29,7 +30,7 @@ type SteamAppList struct {
} }
func (appList SteamAppList) FindID(id string) (SteamApp, error) { func (appList SteamAppList) FindID(id string) (SteamApp, error) {
GameIDNotFound := errors.New("Game ID not found") GameIDNotFound := errors.New("game ID not found")
for _, game := range appList.Apps { for _, game := range appList.Apps {
uintGameID, err := strconv.ParseUint(id, 10, 64) uintGameID, err := strconv.ParseUint(id, 10, 64)
if err != nil { if err != nil {
@ -83,13 +84,13 @@ func getSteamAppList(c chan SteamAppList) {
log.Fatal(jsonErr) log.Fatal(jsonErr)
} }
log.Printf("Updated Steam game list. Found %d games.", len(steamListResponse.AppList.Apps)) log.Printf("Updated Steam game list. Found %d apps.", len(steamListResponse.AppList.Apps))
c <- steamListResponse.AppList c <- steamListResponse.AppList
} }
func downloadGameHeaderImage(appId string) (string, error) { func downloadGameHeaderImage(appId string) (string, error) {
url := "https://cdn.cloudflare.steamstatic.com/steam/apps/" + appId + "/header.jpg" url := fmt.Sprintf(baseGameHeaderURL, appId)
coverURL, err := helpers.DownloadURLIntoTempFile(url) coverURL, err := helpers.DownloadURLIntoTempFile(url)
if err != nil { if err != nil {
return "", err return "", err
@ -137,7 +138,7 @@ func GetGamesFromUser(user string) []string {
return userGames return userGames
} }
func GetScreenshotsForGame(user string, game *games.Game) { func GetScreenshotsForGame(user string, game *providers.Game) {
path := filepath.Join(getBasePathForOS(), "userdata", user, "/760/remote/", game.ID, "screenshots") path := filepath.Join(getBasePathForOS(), "userdata", user, "/760/remote/", game.ID, "screenshots")
files, err := ioutil.ReadDir(path) files, err := ioutil.ReadDir(path)
if err != nil { if err != nil {
@ -146,7 +147,7 @@ func GetScreenshotsForGame(user string, game *games.Game) {
for _, file := range files { for _, file := range files {
if strings.Contains(file.Name(), ".jpg") { if strings.Contains(file.Name(), ".jpg") {
game.Screenshots = append(game.Screenshots, games.Screenshot{Path: path + "/" + file.Name()}) game.Screenshots = append(game.Screenshots, providers.Screenshot{Path: path + "/" + file.Name()})
// log.Printf("Found screenshot for user %s and game %d: %s", user, game.ID, path+"/"+file.Name()) // log.Printf("Found screenshot for user %s and game %d: %s", user, game.ID, path+"/"+file.Name())
} }
} }
@ -156,8 +157,8 @@ func GetScreenshotsForGame(user string, game *games.Game) {
} }
} }
func GetGames(cliOptions games.CLIOptions) []games.Game { func GetGames(cliOptions providers.ProviderOptions) []providers.Game {
var localGames []games.Game var localGames []providers.Game
c := make(chan SteamAppList) c := make(chan SteamAppList)
go getSteamAppList(c) go getSteamAppList(c)
users := GuessUsers() users := GuessUsers()
@ -169,12 +170,12 @@ func GetGames(cliOptions games.CLIOptions) []games.Game {
if err != nil { if err != nil {
log.Print("[ERROR] Steam game ID not found: ", userGameID) log.Print("[ERROR] Steam game ID not found: ", userGameID)
} }
userGame := games.Game{ID: userGameID, Name: steamGame.Name, Provider: providerName, Platform: "PC"} userGame := providers.Game{ID: userGameID, Name: steamGame.Name, Provider: providerName, Platform: "PC"}
if *cliOptions.DownloadCovers { if *cliOptions.DownloadCovers {
coverPath, err := downloadGameHeaderImage(userGameID) coverPath, err := downloadGameHeaderImage(userGameID)
if err == nil { if err == nil {
userGame.Cover = games.Screenshot{Path: coverPath, DestinationName: ".cover"} userGame.Cover = providers.Screenshot{Path: coverPath, DestinationName: ".cover"}
} }
} }

View File

@ -1,4 +1,4 @@
package games package providers
import ( import (
"log" "log"
@ -8,7 +8,7 @@ import (
const DatetimeFormat = "2006-01-02_15-04-05" const DatetimeFormat = "2006-01-02_15-04-05"
type CLIOptions struct { type ProviderOptions struct {
OutputPath *string OutputPath *string
InputPath *string InputPath *string
DownloadCovers *bool DownloadCovers *bool