Added games

main
Óscar M. Lage 2024-10-10 20:03:49 +02:00
parent 0920771bbf
commit 769fc87f0a
7 changed files with 368 additions and 12 deletions

View File

@ -2,12 +2,12 @@
TRAKT_CLIENT_ID=your_trakt_client_id TRAKT_CLIENT_ID=your_trakt_client_id
TRAKT_CLIENT_SECRET=your_trakt_client_secret TRAKT_CLIENT_SECRET=your_trakt_client_secret
TRAKT_REDIRECT_URI=your_trakt_redirect_uri TRAKT_REDIRECT_URI=your_trakt_redirect_uri
IMDB_API_KEY=your_imdb_api_key FANART_API_KEY=your_fanart_api_key
IGDB_API_KEY=your_igdb_api_key
GOOGLE_BOOKS_API_KEY=your_gbooks_api_key GOOGLE_BOOKS_API_KEY=your_gbooks_api_key
GOOGLE_CUSTOM_SEARCH_ENGINE_API_KEY=your_cse_api_key GOOGLE_CUSTOM_SEARCH_ENGINE_API_KEY=your_cse_api_key
GOOGLE_CX=your_google_cx GOOGLE_CX=your_google_cx
FANART_API_KEY=your_fanart_api_key TWITCH_CLIENT_ID=your_twitch_client_id
TWITCH_SECRET_ID=your_twitch_secret_id
SPOTIFY_API_KEY=your_spotify_api_key SPOTIFY_API_KEY=your_spotify_api_key
# PATH to Obisdian files where the information is # PATH to Obisdian files where the information is

235
internal/games/api_igdb.go Normal file
View File

@ -0,0 +1,235 @@
package games
import (
"bytes"
"encoding/json"
"fmt"
"hugo-medialog/utils"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"time"
)
type TokenResponse struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
}
func getAccessToken() (string, error) {
clientID := os.Getenv("TWITCH_CLIENT_ID")
clientSecret := os.Getenv("TWITCH_SECRET_ID")
authURL := "https://id.twitch.tv/oauth2/token"
data := url.Values{}
data.Set("client_id", clientID)
data.Set("client_secret", clientSecret)
data.Set("grant_type", "client_credentials")
req, err := http.NewRequest("POST", authURL, strings.NewReader(data.Encode()))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("error: non-200 response code: %d", resp.StatusCode)
}
var tokenResp TokenResponse
if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
return "", err
}
return tokenResp.AccessToken, nil
}
func SearchGameByTitle(title string, game *Game) error {
clientID := os.Getenv("TWITCH_CLIENT_ID")
accessToken, err := getAccessToken()
if err != nil {
fmt.Printf("Error getting access token: %v\n", err)
return err
}
url := "https://api.igdb.com/v4/games"
query := fmt.Sprintf("search \"%s\";\nfields id,name,slug,cover,artworks,first_release_date,genres,platforms,screenshots,slug,url;\n", title)
req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(query)))
if err != nil {
return err
}
req.Header.Set("Client-ID", clientID)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "text/plain")
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
var games []map[string]interface{}
if err := json.Unmarshal(body, &games); err != nil {
return err
}
if len(games) == 0 {
return fmt.Errorf("No games found")
}
first_game := games[0]
// Timestamp
if timestamp, ok := first_game["first_release_date"].(float64); ok {
game.Year = time.Unix(int64(timestamp), 0).Format("2006")
game.Subtitle = game.Year
}
// Cover
coverID := first_game["cover"].(float64)
coverURL, err := makeRequest("covers", coverID)
if err != nil {
fmt.Printf("Err: %s", err)
}
game.Image = coverURL
// Artwork
if artworks, ok := first_game["artworks"].([]interface{}); ok && len(artworks) > 0 {
artworkURL, err := makeRequest("artworks", artworks[0].(float64))
if err != nil {
fmt.Printf("Err: %s", err)
}
game.Poster = artworkURL
}
// Screenshots
if screenshots, ok := first_game["screenshots"].([]interface{}); ok && len(screenshots) > 0 {
artworkURL, err := makeRequest("screenshots", screenshots[0].(float64))
if err != nil {
fmt.Printf("Err: %s", err)
}
game.Background = artworkURL
}
game.ID = int(first_game["id"].(float64))
game.Link = first_game["url"].(string)
return nil
}
func getImageURL(imageID int, size string) string {
return fmt.Sprintf("https://images.igdb.com/igdb/image/upload/t_%s/%d.jpg", size, imageID)
}
// Function to make requests to IGDB
func makeRequest(endpoint string, id float64) (string, error) {
clientID := os.Getenv("TWITCH_CLIENT_ID")
accessToken, err := getAccessToken()
if err != nil {
fmt.Printf("Error getting access token: %v\n", err)
return "", err
}
// Create the request body
idStr := fmt.Sprint(id)
body := fmt.Sprintf("fields id, image_id; where id = (%s);", idStr)
req, err := http.NewRequest("POST", "https://api.igdb.com/v4/"+endpoint, strings.NewReader(body))
if err != nil {
return "", err
}
// Add headers
req.Header.Add("Client-ID", clientID)
req.Header.Add("Authorization", "Bearer "+accessToken)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
// Send the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
// Read the response
responseData, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
// Parse the response
var coverResponse []map[string]interface{}
err = json.Unmarshal(responseData, &coverResponse)
if err != nil {
return "", err
}
// Get the image_id and construct the URL
if len(coverResponse) > 0 {
imageID := coverResponse[0]["image_id"].(string)
coverURL := fmt.Sprintf("https://images.igdb.com/igdb/image/upload/t_original/%s.jpg", imageID)
return coverURL, nil
}
return "", fmt.Errorf("no image found for ID %d", id)
}
func DownloadImage(url, slug, suffix string) error {
imageDir := filepath.Join(os.Getenv("MARKDOWN_OUTPUT_GAMES_DIR"), os.Getenv("IMAGES_OUTPUT_DIR"))
if err := utils.CreateDirIfNotExists(imageDir); err != nil {
return err
}
filename := fmt.Sprintf("%s%s.jpg", slug, suffix)
filePath := filepath.Join(imageDir, filename)
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("error downloading image: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("non-200 response while downloading image: %d", resp.StatusCode)
}
file, err := os.Create(filePath)
if err != nil {
return fmt.Errorf("error creating image file: %w", err)
}
defer file.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("error reading image data: %w", err)
}
_, err = file.Write(body)
if err != nil {
return fmt.Errorf("error writing image data: %w", err)
}
fmt.Printf(" - Image saved successfully at: %s\n", filePath)
return nil
}

View File

@ -1 +1,103 @@
package games
import (
"fmt"
"hugo-medialog/utils"
"os"
"path/filepath"
"gopkg.in/yaml.v2"
)
func LoadGames() ([]Game, error) {
gamesFile := os.Getenv("OBSIDIAN_GAMES_FILE")
fileData, err := os.ReadFile(gamesFile)
if err != nil {
return nil, err
}
var games []Game
err = yaml.Unmarshal(fileData, &games)
if err != nil {
return nil, err
}
return games, nil
}
func ProcessGames(games []Game) error {
utils.Sep()
for _, game := range games {
fmt.Printf("Title: %s\n", game.Title)
// If we dont have ID, search movie by Title and get the ID
if game.ID == 0 {
err := SearchGameByTitle(game.Title, &game)
if err != nil {
fmt.Printf("Error searching game by title %s: %s\n", game.Title, err)
continue
}
}
game.Slug = utils.Sluggify(game.Title)
// Download Images
if game.Image != "" {
suffix := ""
err := DownloadImage(game.Image, game.Slug, suffix)
if err != nil {
fmt.Printf("Error downloading %s: %s\n", game.Image, err)
}
game.Image = fmt.Sprintf("%s.jpg", game.Slug)
}
if game.Poster != "" {
suffix := "-poster"
err := DownloadImage(game.Poster, game.Slug, suffix)
if err != nil {
fmt.Printf("Error downloading %s: %s\n", game.Image, err)
}
game.Poster = fmt.Sprintf("%s%s.jpg", game.Slug, suffix)
}
if game.Background != "" {
suffix := "-background"
err := DownloadImage(game.Background, game.Slug, suffix)
if err != nil {
fmt.Printf("Error downloading %s: %s\n", game.Background, err)
}
game.Background = fmt.Sprintf("%s%s.jpg", game.Slug, suffix)
}
err := generateGameMarkdown(game)
if err != nil {
return err
}
utils.Sep()
}
return nil
}
func generateGameMarkdown(game Game) error {
templatePath := filepath.Join(os.Getenv("TEMPLATES_DIR"), "game.md.tpl")
outputDir := os.Getenv("MARKDOWN_OUTPUT_GAMES_DIR")
if err := utils.CreateDirIfNotExists(outputDir); err != nil {
return err
}
outputPath := filepath.Join(outputDir, fmt.Sprintf("%s.md", game.Slug))
data := map[string]interface{}{
"Title": game.Title,
"Platform": game.Platform,
"Link": game.Link,
"Subtitle": game.Year,
"Year": game.Year,
"Rate": game.Rate,
"Progress": game.Progress,
"Image": game.Image,
"Poster": game.Poster,
"Background": game.Background,
"Date": game.Date,
"Tags": "playing",
}
return utils.GenerateMarkdown(templatePath, outputPath, data)
}

View File

@ -2,9 +2,12 @@ package games
type Game struct { type Game struct {
Title string `yaml:"title"` Title string `yaml:"title"`
Platform string `yaml:"platform"`
ID int `yaml:"igdb_id"`
Link string `yaml:"link"` Link string `yaml:"link"`
Slug string `yaml:"slug"`
Subtitle string `yaml:"subtitle"` Subtitle string `yaml:"subtitle"`
Year int `yaml:"year"` Year string `yaml:"year"`
Rate float64 `yaml:"rate"` Rate float64 `yaml:"rate"`
Progress string `yaml:"progress"` Progress string `yaml:"progress"`
Image string `yaml:"image"` Image string `yaml:"image"`

23
main.go
View File

@ -4,6 +4,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"hugo-medialog/internal/books" "hugo-medialog/internal/books"
"hugo-medialog/internal/games"
"hugo-medialog/internal/movies" "hugo-medialog/internal/movies"
"hugo-medialog/internal/series" "hugo-medialog/internal/series"
"hugo-medialog/utils" "hugo-medialog/utils"
@ -13,7 +14,7 @@ func main() {
// Load .env file // Load .env file
utils.LoadConfig() utils.LoadConfig()
// Parameter // --media parameter
media := flag.String("media", "", "Specify the media type to process: movies, series, books, games, or music") media := flag.String("media", "", "Specify the media type to process: movies, series, books, games, or music")
flag.Parse() flag.Parse()
@ -27,8 +28,8 @@ func main() {
processSeries() processSeries()
case "books": case "books":
processBooks() processBooks()
// case "games": case "games":
// processGames() processGames()
// case "music": // case "music":
// processMusic() // processMusic()
default: default:
@ -41,10 +42,10 @@ func processAll() {
processMovies() processMovies()
processSeries() processSeries()
processBooks() processBooks()
processGames()
} }
func processMovies() { func processMovies() {
// Movies
moviesList, err := movies.LoadMovies() moviesList, err := movies.LoadMovies()
if err != nil { if err != nil {
fmt.Printf("Error reading movies file: %v\n", err) fmt.Printf("Error reading movies file: %v\n", err)
@ -57,7 +58,6 @@ func processMovies() {
} }
func processSeries() { func processSeries() {
// Series
seriesList, err := series.LoadSeries() seriesList, err := series.LoadSeries()
if err != nil { if err != nil {
fmt.Printf("Error reading series file: %v\n", err) fmt.Printf("Error reading series file: %v\n", err)
@ -70,7 +70,6 @@ func processSeries() {
} }
func processBooks() { func processBooks() {
// Books
booksList, err := books.LoadBooks() booksList, err := books.LoadBooks()
if err != nil { if err != nil {
fmt.Printf("Error reading books file: %v\n", err) fmt.Printf("Error reading books file: %v\n", err)
@ -81,3 +80,15 @@ func processBooks() {
fmt.Printf("Error processing books: %v\n", err) fmt.Printf("Error processing books: %v\n", err)
} }
} }
func processGames() {
gamesList, err := games.LoadGames()
if err != nil {
fmt.Printf("Error reading games file: %v\n", err)
return
}
err = games.ProcessGames(gamesList)
if err != nil {
fmt.Printf("Error processing games: %v\n", err)
}
}

View File

@ -2,3 +2,8 @@
progress: "40%" progress: "40%"
rate: 9.8 rate: 9.8
date: 2024-10-08 date: 2024-10-08
- title: "Diablo 4"
progress: "100%"
rate: 9.8
date: 2024-10-08

View File

@ -6,8 +6,8 @@ year: {{ .Year }}
rate: {{ .Rate }} rate: {{ .Rate }}
progress: {{ .Progress }} progress: {{ .Progress }}
image: {{ .Image }} image: {{ .Image }}
poster-image: {{ .PosterImage }} poster-image: {{ .Poster }}
background-image: {{ .BackgroundImage }} background-image: {{ .Background }}
date: {{ .Date }} date: {{ .Date }}
draft: false draft: false
tags: {{ .Tags }} tags: {{ .Tags }}