mirror of
https://gitlab.com/foxixus/neomovies-api.git
synced 2025-12-15 20:16:10 +05:00
fixes
This commit is contained in:
@@ -32,6 +32,13 @@ type Config struct {
|
||||
|
||||
func New() *Config {
|
||||
mongoURI := getMongoURI()
|
||||
|
||||
kpAPIKey := getEnv(EnvKPAPIKey, "")
|
||||
if kpAPIKey == "" {
|
||||
log.Printf("[Config] ⚠️ KPAPI_KEY is not set")
|
||||
} else {
|
||||
log.Printf("[Config] KPAPI_KEY is set (first 10 chars): %s...", kpAPIKey[:10])
|
||||
}
|
||||
|
||||
return &Config{
|
||||
MongoURI: mongoURI,
|
||||
@@ -53,7 +60,7 @@ func New() *Config {
|
||||
FrontendURL: getEnv(EnvFrontendURL, ""),
|
||||
VibixHost: getEnv(EnvVibixHost, DefaultVibixHost),
|
||||
VibixToken: getEnv(EnvVibixToken, ""),
|
||||
KPAPIKey: getEnv(EnvKPAPIKey, ""),
|
||||
KPAPIKey: kpAPIKey,
|
||||
HDVBToken: getEnv(EnvHDVBToken, ""),
|
||||
KPAPIBaseURL: getEnv("KPAPI_BASE_URL", DefaultKPAPIBase),
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetLanguage extracts the lang parameter from request and returns it with default "ru"
|
||||
@@ -22,6 +24,14 @@ func GetLanguage(r *http.Request) string {
|
||||
return "ru-RU"
|
||||
}
|
||||
|
||||
// Sanitize - remove any quotes or suspicious characters
|
||||
lang = strings.TrimSpace(lang)
|
||||
lang = strings.Trim(lang, "'\"")
|
||||
|
||||
if lang != r.URL.Query().Get("language") && lang != r.URL.Query().Get("lang") {
|
||||
log.Printf("[GetLanguage] Sanitized language parameter from %s to %s", r.URL.Query().Get("language"), lang)
|
||||
}
|
||||
|
||||
// Convert short codes to TMDB format
|
||||
switch lang {
|
||||
case "en":
|
||||
|
||||
@@ -3,6 +3,7 @@ package services
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -74,16 +75,28 @@ type KPSearchResponse struct {
|
||||
}
|
||||
|
||||
type KPFilmShort struct {
|
||||
// Old format fields
|
||||
FilmId int `json:"filmId"`
|
||||
// New format fields
|
||||
KinopoiskId int `json:"kinopoiskId"`
|
||||
|
||||
NameRu string `json:"nameRu"`
|
||||
NameEn string `json:"nameEn"`
|
||||
NameOriginal string `json:"nameOriginal"`
|
||||
ImdbId string `json:"imdbId"`
|
||||
Type string `json:"type"`
|
||||
Year string `json:"year"`
|
||||
Year int `json:"year"` // Changed from string to int
|
||||
Description string `json:"description"`
|
||||
FilmLength string `json:"filmLength"`
|
||||
Countries []KPCountry `json:"countries"`
|
||||
Genres []KPGenre `json:"genres"`
|
||||
|
||||
// Old format rating field
|
||||
Rating string `json:"rating"`
|
||||
// New format rating fields
|
||||
RatingKinopoisk float64 `json:"ratingKinopoisk"`
|
||||
RatingImdb float64 `json:"ratingImdb"`
|
||||
|
||||
RatingVoteCount int `json:"ratingVoteCount"`
|
||||
PosterUrl string `json:"posterUrl"`
|
||||
PosterUrlPreview string `json:"posterUrlPreview"`
|
||||
@@ -113,19 +126,29 @@ func NewKinopoiskService(apiKey, baseURL string) *KinopoiskService {
|
||||
func (s *KinopoiskService) makeRequest(endpoint string, target interface{}) error {
|
||||
req, err := http.NewRequest("GET", endpoint, nil)
|
||||
if err != nil {
|
||||
log.Printf("[Kinopoisk] makeRequest error creating request: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Log API key status
|
||||
if s.apiKey == "" {
|
||||
log.Printf("[Kinopoisk] ⚠️ API Key is EMPTY!")
|
||||
} else {
|
||||
log.Printf("[Kinopoisk] Using API Key (first 10 chars): %s...", s.apiKey[:10])
|
||||
}
|
||||
|
||||
req.Header.Set("X-API-KEY", s.apiKey)
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
resp, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
log.Printf("[Kinopoisk] makeRequest HTTP error: %v", err)
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Printf("[Kinopoisk] makeRequest status code: %d for endpoint: %s", resp.StatusCode, endpoint)
|
||||
return fmt.Errorf("Kinopoisk API error: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
@@ -165,6 +188,57 @@ func (s *KinopoiskService) SearchFilms(keyword string, page int) (*KPSearchRespo
|
||||
return &response, err
|
||||
}
|
||||
|
||||
// GetPopularFilms tries to get popular films using filters API
|
||||
// This is an alternative to GetTopFilms which may not work anymore
|
||||
func (s *KinopoiskService) GetPopularFilms(page int) (*KPSearchResponse, error) {
|
||||
// Try using the filters API with popularity sort
|
||||
endpoint := fmt.Sprintf("%s/v2.2/films?sortField=num_vote&sortType=DESC&page=%d", s.baseURL, page)
|
||||
log.Printf("[Kinopoisk] GetPopularFilms (via filters): %s", endpoint)
|
||||
|
||||
// Try new format first (items/total)
|
||||
var responseNew struct {
|
||||
Total int `json:"total"`
|
||||
TotalPages int `json:"totalPages"`
|
||||
Items []KPFilmShort `json:"items"`
|
||||
}
|
||||
|
||||
err := s.makeRequest(endpoint, &responseNew)
|
||||
if err != nil {
|
||||
log.Printf("[Kinopoisk] GetPopularFilms error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If we got items, use them
|
||||
if len(responseNew.Items) > 0 {
|
||||
log.Printf("[Kinopoisk] GetPopularFilms got %d films (new format)", len(responseNew.Items))
|
||||
return &KPSearchResponse{
|
||||
PagesCount: responseNew.TotalPages,
|
||||
Films: responseNew.Items,
|
||||
SearchFilmsCountResult: len(responseNew.Items),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Fallback to old format (films/pagesCount)
|
||||
var responseOld struct {
|
||||
PagesCount int `json:"pagesCount"`
|
||||
Films []KPFilmShort `json:"films"`
|
||||
}
|
||||
|
||||
err = s.makeRequest(endpoint, &responseOld)
|
||||
if err != nil {
|
||||
log.Printf("[Kinopoisk] GetPopularFilms error (old format): %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[Kinopoisk] GetPopularFilms got %d films (old format)", len(responseOld.Films))
|
||||
|
||||
return &KPSearchResponse{
|
||||
PagesCount: responseOld.PagesCount,
|
||||
Films: responseOld.Films,
|
||||
SearchFilmsCountResult: len(responseOld.Films),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *KinopoiskService) GetExternalSources(kinopoiskId int) ([]KPExternalSource, error) {
|
||||
endpoint := fmt.Sprintf("%s/v2.2/films/%d/external_sources", s.baseURL, kinopoiskId)
|
||||
|
||||
@@ -182,6 +256,7 @@ func (s *KinopoiskService) GetExternalSources(kinopoiskId int) ([]KPExternalSour
|
||||
|
||||
func (s *KinopoiskService) GetTopFilms(topType string, page int) (*KPSearchResponse, error) {
|
||||
endpoint := fmt.Sprintf("%s/v2.2/films/top?type=%s&page=%d", s.baseURL, topType, page)
|
||||
log.Printf("[Kinopoisk] GetTopFilms: %s", endpoint)
|
||||
|
||||
var response struct {
|
||||
PagesCount int `json:"pagesCount"`
|
||||
@@ -190,9 +265,17 @@ func (s *KinopoiskService) GetTopFilms(topType string, page int) (*KPSearchRespo
|
||||
|
||||
err := s.makeRequest(endpoint, &response)
|
||||
if err != nil {
|
||||
log.Printf("[Kinopoisk] GetTopFilms error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[Kinopoisk] GetTopFilms got %d films (pagesCount=%d)", len(response.Films), response.PagesCount)
|
||||
|
||||
// If no films returned, log warning
|
||||
if len(response.Films) == 0 {
|
||||
log.Printf("[Kinopoisk] ⚠️ GetTopFilms returned empty results for type=%s", topType)
|
||||
}
|
||||
|
||||
return &KPSearchResponse{
|
||||
PagesCount: response.PagesCount,
|
||||
Films: response.Films,
|
||||
|
||||
@@ -290,9 +290,11 @@ func mapKPFilmShortToMovie(film KPFilmShort) *models.Movie {
|
||||
})
|
||||
}
|
||||
|
||||
year := 0
|
||||
if film.Year != "" {
|
||||
year, _ = strconv.Atoi(film.Year)
|
||||
// Year is now int, but handle both old and new formats
|
||||
year := film.Year
|
||||
if year == 0 {
|
||||
// Try parsing from string if needed (old format)
|
||||
// This shouldn't happen with new format, but keep for compatibility
|
||||
}
|
||||
|
||||
releaseDate := ""
|
||||
@@ -310,19 +312,32 @@ func mapKPFilmShortToMovie(film KPFilmShort) *models.Movie {
|
||||
if title == "" {
|
||||
title = film.NameEn
|
||||
}
|
||||
if title == "" {
|
||||
title = film.NameOriginal
|
||||
}
|
||||
|
||||
originalTitle := film.NameEn
|
||||
originalTitle := film.NameOriginal
|
||||
if originalTitle == "" {
|
||||
originalTitle = film.NameEn
|
||||
}
|
||||
if originalTitle == "" {
|
||||
originalTitle = film.NameRu
|
||||
}
|
||||
|
||||
rating := 0.0
|
||||
if film.Rating != "" {
|
||||
// Use new format rating if available, otherwise fall back to old format
|
||||
rating := film.RatingKinopoisk
|
||||
if rating == 0 && film.Rating != "" {
|
||||
rating, _ = strconv.ParseFloat(film.Rating, 64)
|
||||
}
|
||||
|
||||
// Get ID - prefer KinopoiskId (new format) over FilmId (old format)
|
||||
id := film.KinopoiskId
|
||||
if id == 0 {
|
||||
id = film.FilmId
|
||||
}
|
||||
|
||||
return &models.Movie{
|
||||
ID: film.FilmId,
|
||||
ID: id,
|
||||
Title: title,
|
||||
OriginalTitle: originalTitle,
|
||||
Overview: film.Description,
|
||||
@@ -332,7 +347,8 @@ func mapKPFilmShortToMovie(film KPFilmShort) *models.Movie {
|
||||
VoteCount: film.RatingVoteCount,
|
||||
Popularity: rating * 100,
|
||||
Genres: genres,
|
||||
KinopoiskID: film.FilmId,
|
||||
KinopoiskID: id,
|
||||
IMDbID: film.ImdbId,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,14 +72,27 @@ func (s *MovieService) GetPopular(page int, language, region string) (*models.TM
|
||||
|
||||
if ShouldUseKinopoisk(language) && s.kpService != nil {
|
||||
log.Printf("[GetPopular] Using Kinopoisk for language: %s", language)
|
||||
|
||||
// Try GetTopFilms first
|
||||
kpTop, err := s.kpService.GetTopFilms("TOP_100_POPULAR_FILMS", page)
|
||||
if err != nil {
|
||||
log.Printf("[GetPopular] Kinopoisk error: %v, falling back to TMDB", err)
|
||||
log.Printf("[GetPopular] GetTopFilms error: %v, trying GetPopularFilms", err)
|
||||
} else if kpTop != nil && len(kpTop.Films) > 0 {
|
||||
log.Printf("[GetPopular] Got %d films from Kinopoisk", len(kpTop.Films))
|
||||
log.Printf("[GetPopular] Got %d films from GetTopFilms", len(kpTop.Films))
|
||||
return MapKPSearchToTMDBResponse(kpTop), nil
|
||||
} else {
|
||||
log.Printf("[GetPopular] Kinopoisk returned empty results, falling back to TMDB")
|
||||
log.Printf("[GetPopular] GetTopFilms returned empty results, trying GetPopularFilms")
|
||||
}
|
||||
|
||||
// Try GetPopularFilms as fallback
|
||||
kpPopular, err := s.kpService.GetPopularFilms(page)
|
||||
if err != nil {
|
||||
log.Printf("[GetPopular] GetPopularFilms error: %v, falling back to TMDB", err)
|
||||
} else if kpPopular != nil && len(kpPopular.Films) > 0 {
|
||||
log.Printf("[GetPopular] Got %d films from GetPopularFilms", len(kpPopular.Films))
|
||||
return MapKPSearchToTMDBResponse(kpPopular), nil
|
||||
} else {
|
||||
log.Printf("[GetPopular] GetPopularFilms returned empty results, falling back to TMDB")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,14 +105,27 @@ func (s *MovieService) GetTopRated(page int, language, region string) (*models.T
|
||||
|
||||
if ShouldUseKinopoisk(language) && s.kpService != nil {
|
||||
log.Printf("[GetTopRated] Using Kinopoisk for language: %s", language)
|
||||
|
||||
// Try GetTopFilms first
|
||||
kpTop, err := s.kpService.GetTopFilms("TOP_250_BEST_FILMS", page)
|
||||
if err != nil {
|
||||
log.Printf("[GetTopRated] Kinopoisk error: %v, falling back to TMDB", err)
|
||||
log.Printf("[GetTopRated] GetTopFilms error: %v, trying GetPopularFilms", err)
|
||||
} else if kpTop != nil && len(kpTop.Films) > 0 {
|
||||
log.Printf("[GetTopRated] Got %d films from Kinopoisk", len(kpTop.Films))
|
||||
log.Printf("[GetTopRated] Got %d films from GetTopFilms", len(kpTop.Films))
|
||||
return MapKPSearchToTMDBResponse(kpTop), nil
|
||||
} else {
|
||||
log.Printf("[GetTopRated] Kinopoisk returned empty results, falling back to TMDB")
|
||||
log.Printf("[GetTopRated] GetTopFilms returned empty results, trying GetPopularFilms")
|
||||
}
|
||||
|
||||
// Try GetPopularFilms as fallback (sorted by rating)
|
||||
kpPopular, err := s.kpService.GetPopularFilms(page)
|
||||
if err != nil {
|
||||
log.Printf("[GetTopRated] GetPopularFilms error: %v, falling back to TMDB", err)
|
||||
} else if kpPopular != nil && len(kpPopular.Films) > 0 {
|
||||
log.Printf("[GetTopRated] Got %d films from GetPopularFilms", len(kpPopular.Films))
|
||||
return MapKPSearchToTMDBResponse(kpPopular), nil
|
||||
} else {
|
||||
log.Printf("[GetTopRated] GetPopularFilms returned empty results, falling back to TMDB")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -272,12 +272,22 @@ func mapKPTypeToUnifiedShort(t string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func yearToDate(y string) string {
|
||||
y = strings.TrimSpace(y)
|
||||
if y == "" {
|
||||
func yearToDate(y interface{}) string {
|
||||
switch v := y.(type) {
|
||||
case int:
|
||||
if v == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%d-01-01", v)
|
||||
case string:
|
||||
v = strings.TrimSpace(v)
|
||||
if v == "" {
|
||||
return ""
|
||||
}
|
||||
return v + "-01-01"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
return y + "-01-01"
|
||||
}
|
||||
|
||||
func firstCountry(countries []models.ProductionCountry) string {
|
||||
|
||||
Reference in New Issue
Block a user