Add "services" (api_services) at same level than controller and model
This commit is contained in:
parent
f2ecb56657
commit
fb4f5c7ed6
107
internal/movies/api_fanart.go
Normal file
107
internal/movies/api_fanart.go
Normal file
@ -0,0 +1,107 @@
|
||||
// internal/movies/api_trakt.go
|
||||
package movies
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hugo-medialog/utils"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// FanartResponse Struct
|
||||
type FanartResponse struct {
|
||||
MoviePosters []FanartImage `json:"movieposter"`
|
||||
MovieBackgrounds []FanartImage `json:"moviebackground"`
|
||||
MovieLogos []FanartImage `json:"movielogo"`
|
||||
}
|
||||
|
||||
// FanartImage struct
|
||||
type FanartImage struct {
|
||||
URL string `json:"url"`
|
||||
Likes string `json:"likes"`
|
||||
}
|
||||
|
||||
func FetchImagesFromFanart(ID int) (posterURL, backgroundURL string, logoURL string, err error) {
|
||||
fanartAPIKey := os.Getenv("FANART_API_KEY")
|
||||
url := fmt.Sprintf("https://webservice.fanart.tv/v3/movies/%s?api_key=%s", strconv.Itoa(ID), fanartAPIKey)
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("error fetching data from fanart.tv: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", "", "", fmt.Errorf("fanart.tv returned non-200 status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("error reading response body: %w", err)
|
||||
}
|
||||
|
||||
var fanartResp FanartResponse
|
||||
err = json.Unmarshal(body, &fanartResp)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("error unmarshalling response: %w", err)
|
||||
}
|
||||
|
||||
// Get the most voted poster
|
||||
if len(fanartResp.MoviePosters) > 0 {
|
||||
posterURL = fanartResp.MoviePosters[0].URL
|
||||
}
|
||||
|
||||
// Get the most voted background
|
||||
if len(fanartResp.MovieBackgrounds) > 0 {
|
||||
backgroundURL = fanartResp.MovieBackgrounds[0].URL
|
||||
}
|
||||
|
||||
// Get the most voted logo
|
||||
if len(fanartResp.MovieLogos) > 0 {
|
||||
logoURL = fanartResp.MovieLogos[0].URL
|
||||
}
|
||||
|
||||
return posterURL, backgroundURL, logoURL, nil
|
||||
}
|
||||
|
||||
func DownloadImage(url, slug, imageType string) error {
|
||||
imageDir := os.Getenv("IMAGES_OUTPUT_DIR")
|
||||
if err := utils.CreateDirIfNotExists(imageDir); err != nil {
|
||||
return err
|
||||
}
|
||||
filename := fmt.Sprintf("%s-%s.jpg", slug, imageType)
|
||||
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
|
||||
}
|
198
internal/movies/api_trakt.go
Normal file
198
internal/movies/api_trakt.go
Normal file
@ -0,0 +1,198 @@
|
||||
// internal/movies/api_trakt.go
|
||||
package movies
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hugo-medialog/utils"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var (
|
||||
traktAPIBaseURL = "https://api.trakt.tv"
|
||||
traktClientID string
|
||||
traktClientSecret string
|
||||
traktRedirectURI string
|
||||
)
|
||||
|
||||
var traktOAuthConfig = &oauth2.Config{
|
||||
ClientID: os.Getenv("TRAKT_CLIENT_ID"),
|
||||
ClientSecret: os.Getenv("TRAKT_CLIENT_SECRET"),
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://api.trakt.tv/oauth/authorize",
|
||||
TokenURL: "https://api.trakt.tv/oauth/token",
|
||||
},
|
||||
RedirectURL: os.Getenv("TRAKT_REDIRECT_URI"),
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Load runtime variables
|
||||
utils.LoadConfig()
|
||||
traktClientID = os.Getenv("TRAKT_CLIENT_ID")
|
||||
traktClientSecret = os.Getenv("TRAKT_CLIENT_SECRET")
|
||||
traktRedirectURI = os.Getenv("TRAKT_REDIRECT_URI")
|
||||
|
||||
if traktClientID == "" || traktClientSecret == "" || traktRedirectURI == "" {
|
||||
log.Fatal("Missing Trakt API credentials in environment variables")
|
||||
}
|
||||
}
|
||||
|
||||
// Function to fetch a token and save it for reuse
|
||||
func GetTraktToken() (*oauth2.Token, error) {
|
||||
// Check if the token already exists (from a previous session)
|
||||
tokenFile := "trakt_token.json"
|
||||
if _, err := os.Stat(tokenFile); err == nil {
|
||||
// If token file exists, read and return it
|
||||
file, err := os.Open(tokenFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
token := &oauth2.Token{}
|
||||
if err = json.NewDecoder(file).Decode(token); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// If no token exists, we need to go through the OAuth flow
|
||||
authURL := traktOAuthConfig.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
|
||||
fmt.Printf("Visit the URL to authorize: %v\n", authURL)
|
||||
|
||||
// After user authorization, you will receive an authorization code.
|
||||
// Once you have the code, get a token by calling the token endpoint.
|
||||
var authCode string
|
||||
fmt.Print("Enter authorization code: ")
|
||||
fmt.Scan(&authCode)
|
||||
|
||||
token, err := traktOAuthConfig.Exchange(context.Background(), authCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Save the token to file for future use
|
||||
file, err := os.Create(tokenFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err := json.NewEncoder(file).Encode(token); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// Function to get movie details by Trakt ID
|
||||
func GetMovieByID(traktID int) (*Movie, error) {
|
||||
token, err := GetTraktToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := traktOAuthConfig.Client(context.Background(), token)
|
||||
|
||||
// Call the Trakt API to get movie details by ID
|
||||
apiURL := fmt.Sprintf("%s/movies/%s", traktAPIBaseURL, strconv.Itoa(traktID))
|
||||
req, _ := http.NewRequest("GET", apiURL, nil)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+token.AccessToken)
|
||||
req.Header.Set("trakt-api-version", "2")
|
||||
req.Header.Set("trakt-api-key", traktClientID)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to fetch movie: %v", resp.Status)
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
var traktMovie Movie
|
||||
if err := json.Unmarshal(body, &traktMovie); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &traktMovie, nil
|
||||
}
|
||||
|
||||
// Function to search for a movie by title
|
||||
func SearchMovieByTitle(title string, movie *Movie) error {
|
||||
token, err := GetTraktToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := traktOAuthConfig.Client(context.Background(), token)
|
||||
|
||||
// URL encode the title
|
||||
query := url.QueryEscape(title)
|
||||
apiURL := fmt.Sprintf("%s/search/movie?query=%s", traktAPIBaseURL, query)
|
||||
req, _ := http.NewRequest("GET", apiURL, nil)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+token.AccessToken)
|
||||
req.Header.Set("trakt-api-version", "2")
|
||||
req.Header.Set("trakt-api-key", traktClientID)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("failed to search movie: %v", resp.Status)
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
|
||||
// utils.DebugBody(body)
|
||||
|
||||
// Parse trakt.tv response to a struct
|
||||
var traktResponse []struct {
|
||||
Movie struct {
|
||||
Title string `json:"title"`
|
||||
Year int `json:"year"`
|
||||
IDs struct {
|
||||
Slug string `json:"slug"`
|
||||
IMDB string `json:"imdb"`
|
||||
TMDB int `json:"tmdb"`
|
||||
Trakt int `json:"trakt"`
|
||||
} `json:"ids"`
|
||||
} `json:"movie"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(body, &traktResponse); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(traktResponse) == 0 {
|
||||
return fmt.Errorf("no movie found with title %s", title)
|
||||
}
|
||||
|
||||
// Using 1st result to fill our Movie structure
|
||||
foundMovie := traktResponse[0].Movie
|
||||
|
||||
movie.Title = foundMovie.Title
|
||||
movie.IDs.Slug = foundMovie.IDs.Slug
|
||||
movie.IDs.IMDB = foundMovie.IDs.IMDB
|
||||
movie.IDs.TMDB = foundMovie.IDs.TMDB
|
||||
movie.IDs.Trakt = foundMovie.IDs.Trakt
|
||||
movie.Subtitle = fmt.Sprintf("%d", foundMovie.Year)
|
||||
movie.Year = foundMovie.Year
|
||||
movie.Link = fmt.Sprintf("https://trakt.tv/movies/%d", foundMovie.IDs.Trakt)
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user