200 lines
5.0 KiB
Go
200 lines
5.0 KiB
Go
package series
|
|
|
|
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 serie details by Trakt ID
|
|
func GetSerieByID(traktID int) (*Serie, error) {
|
|
token, err := GetTraktToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
client := traktOAuthConfig.Client(context.Background(), token)
|
|
|
|
// Call the Trakt API to get serie details by ID
|
|
apiURL := fmt.Sprintf("%s/shows/%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 serie: %v", resp.Status)
|
|
}
|
|
|
|
body, _ := ioutil.ReadAll(resp.Body)
|
|
var traktSerie Serie
|
|
if err := json.Unmarshal(body, &traktSerie); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &traktSerie, nil
|
|
}
|
|
|
|
// Function to search for a serie by title
|
|
func SearchSerieByTitle(title string, serie *Serie) 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/show?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 serie: %v", resp.Status)
|
|
}
|
|
|
|
body, _ := ioutil.ReadAll(resp.Body)
|
|
|
|
// utils.DebugBody(body)
|
|
|
|
// Parse trakt.tv response to a struct
|
|
var traktResponse []struct {
|
|
Serie 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"`
|
|
TVDB int `json:"tvdb"`
|
|
} `json:"ids"`
|
|
} `json:"show"`
|
|
}
|
|
|
|
if err := json.Unmarshal(body, &traktResponse); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(traktResponse) == 0 {
|
|
return fmt.Errorf("no serie found with title %s", title)
|
|
}
|
|
|
|
// Using 1st result to fill our Serie structure
|
|
foundSerie := traktResponse[0].Serie
|
|
|
|
serie.Title = foundSerie.Title
|
|
serie.IDs.Slug = foundSerie.IDs.Slug
|
|
serie.IDs.IMDB = foundSerie.IDs.IMDB
|
|
serie.IDs.TMDB = foundSerie.IDs.TMDB
|
|
serie.IDs.Trakt = foundSerie.IDs.Trakt
|
|
serie.IDs.TVDB = foundSerie.IDs.TVDB
|
|
serie.Subtitle = fmt.Sprintf("%d", foundSerie.Year)
|
|
serie.Year = foundSerie.Year
|
|
serie.Link = fmt.Sprintf("https://trakt.tv/shows/%d", foundSerie.IDs.Trakt)
|
|
|
|
return nil
|
|
}
|