Files
sugarr/internal/client/tmdb.go
2026-04-22 18:23:19 +02:00

112 lines
2.8 KiB
Go

package client
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"github.com/fjurmanovic/sugarr/internal/model"
"github.com/fjurmanovic/sugarr/internal/utl/configs"
)
const tmdbBaseURL = "https://api.themoviedb.org"
type TMDbClient struct {
apiKey string
http *http.Client
}
func NewTMDbClient(cfg *configs.Config) *TMDbClient {
return &TMDbClient{
apiKey: cfg.TMDbAPIKey,
http: &http.Client{},
}
}
type tmdbSearchResponse struct {
Results []tmdbMovieJSON `json:"results"`
}
type tmdbMovieJSON struct {
ID int `json:"id"`
Title string `json:"title"`
ReleaseDate string `json:"release_date"`
}
func (m tmdbMovieJSON) toModel() model.TMDbMovie {
year := 0
if len(m.ReleaseDate) >= 4 {
year, _ = strconv.Atoi(m.ReleaseDate[:4])
}
return model.TMDbMovie{ID: m.ID, Title: m.Title, Year: year}
}
func (c *TMDbClient) get(path string, params url.Values) (*http.Response, error) {
params.Set("api_key", c.apiKey)
u := fmt.Sprintf("%s%s?%s", tmdbBaseURL, path, params.Encode())
req, err := http.NewRequest(http.MethodGet, u, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/json")
resp, err := c.http.Do(req)
if err != nil {
return nil, fmt.Errorf("tmdb request failed: %w", err)
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("tmdb returned status %d for %s", resp.StatusCode, path)
}
return resp, nil
}
func (c *TMDbClient) SearchMovie(title string, year int) (*model.TMDbMovie, error) {
params := url.Values{"query": {title}}
if year > 0 {
params.Set("year", strconv.Itoa(year))
}
resp, err := c.get("/3/search/movie", params)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result tmdbSearchResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("decoding tmdb search response: %w", err)
}
if len(result.Results) == 0 {
return nil, nil
}
m := result.Results[0].toModel()
return &m, nil
}
func (c *TMDbClient) getMovieList(path string) ([]model.TMDbMovie, error) {
resp, err := c.get(path, url.Values{})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result tmdbSearchResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("decoding tmdb response: %w", err)
}
movies := make([]model.TMDbMovie, 0, len(result.Results))
for _, r := range result.Results {
movies = append(movies, r.toModel())
}
return movies, nil
}
func (c *TMDbClient) GetSimilar(tmdbID int) ([]model.TMDbMovie, error) {
return c.getMovieList(fmt.Sprintf("/3/movie/%d/similar", tmdbID))
}
func (c *TMDbClient) GetRecommendations(tmdbID int) ([]model.TMDbMovie, error) {
return c.getMovieList(fmt.Sprintf("/3/movie/%d/recommendations", tmdbID))
}