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 }