mirror of
https://gitlab.com/foxixus/neomovies-api.git
synced 2025-10-28 01:48:51 +05:00
Compare commits
3 Commits
f5a754ddf7
...
c7aa844f49
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7aa844f49 | ||
|
|
0fbf0f0f42 | ||
|
|
f2f06485fd |
@@ -57,7 +57,7 @@ TMDB_ACCESS_TOKEN=your_tmdb_access_token
|
|||||||
JWT_SECRET=your_jwt_secret_key
|
JWT_SECRET=your_jwt_secret_key
|
||||||
|
|
||||||
# Kinopoisk API
|
# Kinopoisk API
|
||||||
KPAPI_KEY=920aaf6a-9f64-46f7-bda7-209fb1069440
|
KPAPI_KEY=your_kp_api_key
|
||||||
KPAPI_BASE_URL=https://kinopoiskapiunofficial.tech/api
|
KPAPI_BASE_URL=https://kinopoiskapiunofficial.tech/api
|
||||||
|
|
||||||
# Сервис
|
# Сервис
|
||||||
@@ -74,8 +74,8 @@ GMAIL_APP_PASSWORD=your_gmail_app_password
|
|||||||
LUMEX_URL=https://p.lumex.space
|
LUMEX_URL=https://p.lumex.space
|
||||||
ALLOHA_TOKEN=your_alloha_token
|
ALLOHA_TOKEN=your_alloha_token
|
||||||
VIBIX_HOST=https://vibix.org
|
VIBIX_HOST=https://vibix.org
|
||||||
VIBIX_TOKEN=18745|NzecUXT4gikPUtFkSEFlDLPmr9kWnQACTo1N0Ixq9240bcf1
|
VIBIX_TOKEN=your_vibix_token
|
||||||
HDVB_TOKEN=b9ae5f8c4832244060916af4aa9d1939
|
HDVB_TOKEN=your_hdvb_token
|
||||||
|
|
||||||
# Торренты (RedAPI)
|
# Торренты (RedAPI)
|
||||||
REDAPI_BASE_URL=http://redapi.cfhttp.top
|
REDAPI_BASE_URL=http://redapi.cfhttp.top
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
@@ -48,14 +49,39 @@ func (h *MovieHandler) Search(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (h *MovieHandler) GetByID(w http.ResponseWriter, r *http.Request) {
|
func (h *MovieHandler) GetByID(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id, err := strconv.Atoi(vars["id"])
|
rawID := vars["id"]
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Invalid movie ID", http.StatusBadRequest)
|
// Support formats: "123" (old), "kp_123", "tmdb_123"
|
||||||
return
|
source := ""
|
||||||
|
var id int
|
||||||
|
if strings.Contains(rawID, "_") {
|
||||||
|
parts := strings.SplitN(rawID, "_", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
http.Error(w, "Invalid ID format", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
source = parts[0]
|
||||||
|
parsed, err := strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid numeric ID", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id = parsed
|
||||||
|
} else {
|
||||||
|
// Backward compatibility
|
||||||
|
parsed, err := strconv.Atoi(rawID)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid movie ID", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id = parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
language := GetLanguage(r)
|
language := GetLanguage(r)
|
||||||
idType := r.URL.Query().Get("id_type") // kp or tmdb
|
idType := r.URL.Query().Get("id_type")
|
||||||
|
if source == "kp" || source == "tmdb" {
|
||||||
|
idType = source
|
||||||
|
}
|
||||||
|
|
||||||
movie, err := h.movieService.GetByID(id, language, idType)
|
movie, err := h.movieService.GetByID(id, language, idType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -30,51 +30,60 @@ func (h *SearchHandler) MultiSearch(w http.ResponseWriter, r *http.Request) {
|
|||||||
page := getIntQuery(r, "page", 1)
|
page := getIntQuery(r, "page", 1)
|
||||||
language := GetLanguage(r)
|
language := GetLanguage(r)
|
||||||
|
|
||||||
if services.ShouldUseKinopoisk(language) && h.kpService != nil {
|
if services.ShouldUseKinopoisk(language) {
|
||||||
kpSearch, err := h.kpService.SearchFilms(query, page)
|
if h.kpService == nil {
|
||||||
if err == nil {
|
http.Error(w, "Kinopoisk service is not configured", http.StatusBadGateway)
|
||||||
tmdbResp := services.MapKPSearchToTMDBResponse(kpSearch)
|
return
|
||||||
multiResults := make([]models.MultiSearchResult, 0)
|
}
|
||||||
for _, movie := range tmdbResp.Results {
|
|
||||||
multiResults = append(multiResults, models.MultiSearchResult{
|
|
||||||
ID: movie.ID,
|
|
||||||
MediaType: "movie",
|
|
||||||
Title: movie.Title,
|
|
||||||
OriginalTitle: movie.OriginalTitle,
|
|
||||||
Overview: movie.Overview,
|
|
||||||
PosterPath: movie.PosterPath,
|
|
||||||
BackdropPath: movie.BackdropPath,
|
|
||||||
ReleaseDate: movie.ReleaseDate,
|
|
||||||
VoteAverage: movie.VoteAverage,
|
|
||||||
VoteCount: movie.VoteCount,
|
|
||||||
Popularity: movie.Popularity,
|
|
||||||
Adult: movie.Adult,
|
|
||||||
OriginalLanguage: movie.OriginalLanguage,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
json.NewEncoder(w).Encode(models.APIResponse{
|
|
||||||
Success: true,
|
|
||||||
Data: models.MultiSearchResponse{
|
|
||||||
Page: page,
|
|
||||||
Results: multiResults,
|
|
||||||
TotalPages: tmdbResp.TotalPages,
|
|
||||||
TotalResults: tmdbResp.TotalResults,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
results, err := h.tmdbService.SearchMulti(query, page, language)
|
kpSearch, err := h.kpService.SearchFilms(query, page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusBadGateway)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
tmdbResp := services.MapKPSearchToTMDBResponse(kpSearch)
|
||||||
json.NewEncoder(w).Encode(models.APIResponse{
|
multiResults := make([]models.MultiSearchResult, 0)
|
||||||
Success: true,
|
for _, movie := range tmdbResp.Results {
|
||||||
Data: results,
|
multiResults = append(multiResults, models.MultiSearchResult{
|
||||||
})
|
ID: movie.ID,
|
||||||
|
MediaType: "movie",
|
||||||
|
Title: movie.Title,
|
||||||
|
OriginalTitle: movie.OriginalTitle,
|
||||||
|
Overview: movie.Overview,
|
||||||
|
PosterPath: movie.PosterPath,
|
||||||
|
BackdropPath: movie.BackdropPath,
|
||||||
|
ReleaseDate: movie.ReleaseDate,
|
||||||
|
VoteAverage: movie.VoteAverage,
|
||||||
|
VoteCount: movie.VoteCount,
|
||||||
|
Popularity: movie.Popularity,
|
||||||
|
Adult: movie.Adult,
|
||||||
|
OriginalLanguage: movie.OriginalLanguage,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(models.APIResponse{
|
||||||
|
Success: true,
|
||||||
|
Data: models.MultiSearchResponse{
|
||||||
|
Page: page,
|
||||||
|
Results: multiResults,
|
||||||
|
TotalPages: tmdbResp.TotalPages,
|
||||||
|
TotalResults: tmdbResp.TotalResults,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// EN/прочие языки — TMDB
|
||||||
|
results, err := h.tmdbService.SearchMulti(query, page, language)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(models.APIResponse{
|
||||||
|
Success: true,
|
||||||
|
Data: results,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
@@ -47,14 +48,39 @@ func (h *TVHandler) Search(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (h *TVHandler) GetByID(w http.ResponseWriter, r *http.Request) {
|
func (h *TVHandler) GetByID(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id, err := strconv.Atoi(vars["id"])
|
rawID := vars["id"]
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Invalid TV show ID", http.StatusBadRequest)
|
// Support formats: "123" (old), "kp_123", "tmdb_123"
|
||||||
return
|
source := ""
|
||||||
|
var id int
|
||||||
|
if strings.Contains(rawID, "_") {
|
||||||
|
parts := strings.SplitN(rawID, "_", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
http.Error(w, "Invalid ID format", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
source = parts[0]
|
||||||
|
parsed, err := strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid numeric ID", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id = parsed
|
||||||
|
} else {
|
||||||
|
// Backward compatibility
|
||||||
|
parsed, err := strconv.Atoi(rawID)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Invalid TV show ID", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id = parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
language := GetLanguage(r)
|
language := GetLanguage(r)
|
||||||
idType := r.URL.Query().Get("id_type") // kp or tmdb
|
idType := r.URL.Query().Get("id_type")
|
||||||
|
if source == "kp" || source == "tmdb" {
|
||||||
|
idType = source
|
||||||
|
}
|
||||||
|
|
||||||
tvShow, err := h.tvService.GetByID(id, language, idType)
|
tvShow, err := h.tvService.GetByID(id, language, idType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -167,6 +167,89 @@ func MapKPFilmToTVShow(kpFilm *KPFilm) *models.TVShow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unified mappers with prefixed IDs
|
||||||
|
func MapKPToUnified(kpFilm *KPFilm) *models.UnifiedContent {
|
||||||
|
if kpFilm == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseDate := FormatKPDate(kpFilm.Year)
|
||||||
|
endDate := (*string)(nil)
|
||||||
|
if kpFilm.EndYear > 0 {
|
||||||
|
v := FormatKPDate(kpFilm.EndYear)
|
||||||
|
endDate = &v
|
||||||
|
}
|
||||||
|
|
||||||
|
genres := make([]models.UnifiedGenre, 0)
|
||||||
|
for _, g := range kpFilm.Genres {
|
||||||
|
genres = append(genres, models.UnifiedGenre{ID: strings.ToLower(g.Genre), Name: g.Genre})
|
||||||
|
}
|
||||||
|
|
||||||
|
poster := kpFilm.PosterUrlPreview
|
||||||
|
if poster == "" {
|
||||||
|
poster = kpFilm.PosterUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
country := ""
|
||||||
|
if len(kpFilm.Countries) > 0 {
|
||||||
|
country = kpFilm.Countries[0].Country
|
||||||
|
}
|
||||||
|
|
||||||
|
title := kpFilm.NameRu
|
||||||
|
if title == "" {
|
||||||
|
title = kpFilm.NameEn
|
||||||
|
}
|
||||||
|
originalTitle := kpFilm.NameOriginal
|
||||||
|
if originalTitle == "" {
|
||||||
|
originalTitle = kpFilm.NameEn
|
||||||
|
}
|
||||||
|
|
||||||
|
var budgetPtr *int64
|
||||||
|
var revenuePtr *int64
|
||||||
|
|
||||||
|
external := models.UnifiedExternalIDs{KP: &kpFilm.KinopoiskId, TMDB: nil, IMDb: kpFilm.ImdbId}
|
||||||
|
|
||||||
|
return &models.UnifiedContent{
|
||||||
|
ID: strconv.Itoa(kpFilm.KinopoiskId),
|
||||||
|
SourceID: "kp_" + strconv.Itoa(kpFilm.KinopoiskId),
|
||||||
|
Title: title,
|
||||||
|
OriginalTitle: originalTitle,
|
||||||
|
Description: firstNonEmpty(kpFilm.Description, kpFilm.ShortDescription),
|
||||||
|
ReleaseDate: releaseDate,
|
||||||
|
EndDate: endDate,
|
||||||
|
Type: mapKPTypeToUnified(kpFilm),
|
||||||
|
Genres: genres,
|
||||||
|
Rating: kpFilm.RatingKinopoisk,
|
||||||
|
PosterURL: poster,
|
||||||
|
BackdropURL: kpFilm.CoverUrl,
|
||||||
|
Director: "",
|
||||||
|
Cast: []models.UnifiedCastMember{},
|
||||||
|
Duration: kpFilm.FilmLength,
|
||||||
|
Country: country,
|
||||||
|
Language: detectLanguage(kpFilm),
|
||||||
|
Budget: budgetPtr,
|
||||||
|
Revenue: revenuePtr,
|
||||||
|
IMDbID: kpFilm.ImdbId,
|
||||||
|
ExternalIDs: external,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapKPTypeToUnified(kp *KPFilm) string {
|
||||||
|
if kp.Serial || kp.Type == "TV_SERIES" || kp.Type == "MINI_SERIES" {
|
||||||
|
return "tv"
|
||||||
|
}
|
||||||
|
return "movie"
|
||||||
|
}
|
||||||
|
|
||||||
|
func firstNonEmpty(values ...string) string {
|
||||||
|
for _, v := range values {
|
||||||
|
if strings.TrimSpace(v) != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func MapKPSearchToTMDBResponse(kpSearch *KPSearchResponse) *models.TMDBResponse {
|
func MapKPSearchToTMDBResponse(kpSearch *KPSearchResponse) *models.TMDBResponse {
|
||||||
if kpSearch == nil {
|
if kpSearch == nil {
|
||||||
return &models.TMDBResponse{
|
return &models.TMDBResponse{
|
||||||
|
|||||||
@@ -30,41 +30,39 @@ func (s *MovieService) Search(query string, page int, language, region string, y
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *MovieService) GetByID(id int, language string, idType string) (*models.Movie, error) {
|
func (s *MovieService) GetByID(id int, language string, idType string) (*models.Movie, error) {
|
||||||
// Если указан id_type, используем его; иначе определяем по языку
|
// Строго уважаем явный id_type, без скрытого fallback на TMDB
|
||||||
useKP := false
|
switch idType {
|
||||||
if idType == "kp" {
|
case "kp":
|
||||||
useKP = true
|
if s.kpService == nil {
|
||||||
} else if idType == "tmdb" {
|
return nil, fmt.Errorf("kinopoisk service not configured")
|
||||||
useKP = false
|
}
|
||||||
} else {
|
|
||||||
// Если id_type не указан, используем старую логику по языку
|
|
||||||
useKP = ShouldUseKinopoisk(language)
|
|
||||||
}
|
|
||||||
|
|
||||||
if useKP && s.kpService != nil {
|
// Сначала пробуем как Kinopoisk ID
|
||||||
// Сначала пробуем напрямую по KP ID
|
if kpFilm, err := s.kpService.GetFilmByKinopoiskId(id); err == nil {
|
||||||
kpFilm, err := s.kpService.GetFilmByKinopoiskId(id)
|
return MapKPFilmToTMDBMovie(kpFilm), nil
|
||||||
if err == nil {
|
}
|
||||||
return MapKPFilmToTMDBMovie(kpFilm), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Если не найдено и явно указан id_type=kp, возможно это TMDB ID
|
// Возможно пришел TMDB ID — пробуем конвертировать TMDB -> KP
|
||||||
// Пробуем конвертировать TMDB -> KP
|
if kpId, convErr := TmdbIdToKPId(s.tmdb, s.kpService, id); convErr == nil {
|
||||||
if idType == "kp" {
|
if kpFilm, err := s.kpService.GetFilmByKinopoiskId(kpId); err == nil {
|
||||||
kpId, convErr := TmdbIdToKPId(s.tmdb, s.kpService, id)
|
return MapKPFilmToTMDBMovie(kpFilm), nil
|
||||||
if convErr == nil {
|
}
|
||||||
kpFilm, err := s.kpService.GetFilmByKinopoiskId(kpId)
|
}
|
||||||
if err == nil {
|
// Явно указан KP, но ничего не нашли — возвращаем ошибку
|
||||||
return MapKPFilmToTMDBMovie(kpFilm), nil
|
return nil, fmt.Errorf("film not found in Kinopoisk with id %d", id)
|
||||||
}
|
|
||||||
}
|
|
||||||
// Если конвертация не удалась, возвращаем ошибку вместо fallback
|
|
||||||
return nil, fmt.Errorf("film not found in Kinopoisk with id %d", id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Для TMDB или если KP не указан
|
case "tmdb":
|
||||||
return s.tmdb.GetMovie(id, language)
|
return s.tmdb.GetMovie(id, language)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если id_type не указан — старая логика по языку
|
||||||
|
if ShouldUseKinopoisk(language) && s.kpService != nil {
|
||||||
|
if kpFilm, err := s.kpService.GetFilmByKinopoiskId(id); err == nil {
|
||||||
|
return MapKPFilmToTMDBMovie(kpFilm), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.tmdb.GetMovie(id, language)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MovieService) GetPopular(page int, language, region string) (*models.TMDBResponse, error) {
|
func (s *MovieService) GetPopular(page int, language, region string) (*models.TMDBResponse, error) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"neomovies-api/pkg/models"
|
"neomovies-api/pkg/models"
|
||||||
)
|
)
|
||||||
@@ -179,6 +180,80 @@ func (s *TMDBService) GetTVShow(id int, language string) (*models.TVShow, error)
|
|||||||
return &tvShow, err
|
return &tvShow, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map TMDB movie to unified content with prefixed IDs. Requires optional external IDs for imdbId.
|
||||||
|
func MapTMDBToUnifiedMovie(movie *models.Movie, external *models.ExternalIDs) *models.UnifiedContent {
|
||||||
|
if movie == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
genres := make([]models.UnifiedGenre, 0, len(movie.Genres))
|
||||||
|
for _, g := range movie.Genres {
|
||||||
|
name := strings.TrimSpace(g.Name)
|
||||||
|
id := strings.ToLower(strings.ReplaceAll(name, " ", "-"))
|
||||||
|
if id == "" {
|
||||||
|
id = strconv.Itoa(g.ID)
|
||||||
|
}
|
||||||
|
genres = append(genres, models.UnifiedGenre{ID: id, Name: name})
|
||||||
|
}
|
||||||
|
|
||||||
|
var imdb string
|
||||||
|
if external != nil {
|
||||||
|
imdb = external.IMDbID
|
||||||
|
}
|
||||||
|
|
||||||
|
var budgetPtr *int64
|
||||||
|
if movie.Budget > 0 {
|
||||||
|
v := movie.Budget
|
||||||
|
budgetPtr = &v
|
||||||
|
}
|
||||||
|
|
||||||
|
var revenuePtr *int64
|
||||||
|
if movie.Revenue > 0 {
|
||||||
|
v := movie.Revenue
|
||||||
|
revenuePtr = &v
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := models.UnifiedExternalIDs{
|
||||||
|
KP: nil,
|
||||||
|
TMDB: &movie.ID,
|
||||||
|
IMDb: imdb,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.UnifiedContent{
|
||||||
|
ID: strconv.Itoa(movie.ID),
|
||||||
|
SourceID: "tmdb_" + strconv.Itoa(movie.ID),
|
||||||
|
Title: movie.Title,
|
||||||
|
OriginalTitle: movie.OriginalTitle,
|
||||||
|
Description: movie.Overview,
|
||||||
|
ReleaseDate: movie.ReleaseDate,
|
||||||
|
EndDate: nil,
|
||||||
|
Type: "movie",
|
||||||
|
Genres: genres,
|
||||||
|
Rating: movie.VoteAverage,
|
||||||
|
PosterURL: movie.PosterPath,
|
||||||
|
BackdropURL: movie.BackdropPath,
|
||||||
|
Director: "",
|
||||||
|
Cast: []models.UnifiedCastMember{},
|
||||||
|
Duration: movie.Runtime,
|
||||||
|
Country: firstCountry(movie.ProductionCountries),
|
||||||
|
Language: movie.OriginalLanguage,
|
||||||
|
Budget: budgetPtr,
|
||||||
|
Revenue: revenuePtr,
|
||||||
|
IMDbID: imdb,
|
||||||
|
ExternalIDs: ext,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func firstCountry(countries []models.ProductionCountry) string {
|
||||||
|
if len(countries) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(countries[0].Name) != "" {
|
||||||
|
return countries[0].Name
|
||||||
|
}
|
||||||
|
return countries[0].ISO31661
|
||||||
|
}
|
||||||
|
|
||||||
func (s *TMDBService) GetGenres(mediaType string, language string) (*models.GenresResponse, error) {
|
func (s *TMDBService) GetGenres(mediaType string, language string) (*models.GenresResponse, error) {
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
|
|
||||||
|
|||||||
@@ -26,41 +26,39 @@ func (s *TVService) Search(query string, page int, language string, year int) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *TVService) GetByID(id int, language string, idType string) (*models.TVShow, error) {
|
func (s *TVService) GetByID(id int, language string, idType string) (*models.TVShow, error) {
|
||||||
// Если указан id_type, используем его; иначе определяем по языку
|
// Строго уважаем явный id_type, без скрытого fallback на TMDB
|
||||||
useKP := false
|
switch idType {
|
||||||
if idType == "kp" {
|
case "kp":
|
||||||
useKP = true
|
if s.kpService == nil {
|
||||||
} else if idType == "tmdb" {
|
return nil, fmt.Errorf("kinopoisk service not configured")
|
||||||
useKP = false
|
}
|
||||||
} else {
|
|
||||||
// Если id_type не указан, используем старую логику по языку
|
|
||||||
useKP = ShouldUseKinopoisk(language)
|
|
||||||
}
|
|
||||||
|
|
||||||
if useKP && s.kpService != nil {
|
// Сначала пробуем как Kinopoisk ID
|
||||||
// Сначала пробуем напрямую по KP ID
|
if kpFilm, err := s.kpService.GetFilmByKinopoiskId(id); err == nil && kpFilm != nil {
|
||||||
kpFilm, err := s.kpService.GetFilmByKinopoiskId(id)
|
return MapKPFilmToTVShow(kpFilm), nil
|
||||||
if err == nil && kpFilm != nil {
|
}
|
||||||
return MapKPFilmToTVShow(kpFilm), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Если не найдено и явно указан id_type=kp, возможно это TMDB ID
|
// Возможно пришел TMDB ID — пробуем конвертировать TMDB -> KP
|
||||||
// Пробуем конвертировать TMDB -> KP
|
if kpId, convErr := TmdbIdToKPId(s.tmdb, s.kpService, id); convErr == nil {
|
||||||
if idType == "kp" {
|
if kpFilm, err := s.kpService.GetFilmByKinopoiskId(kpId); err == nil && kpFilm != nil {
|
||||||
kpId, convErr := TmdbIdToKPId(s.tmdb, s.kpService, id)
|
return MapKPFilmToTVShow(kpFilm), nil
|
||||||
if convErr == nil {
|
}
|
||||||
kpFilm, err := s.kpService.GetFilmByKinopoiskId(kpId)
|
}
|
||||||
if err == nil && kpFilm != nil {
|
// Явно указан KP, но ничего не нашли — возвращаем ошибку
|
||||||
return MapKPFilmToTVShow(kpFilm), nil
|
return nil, fmt.Errorf("TV show not found in Kinopoisk with id %d", id)
|
||||||
}
|
|
||||||
}
|
|
||||||
// Если конвертация не удалась, возвращаем ошибку вместо fallback
|
|
||||||
return nil, fmt.Errorf("TV show not found in Kinopoisk with id %d", id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Для TMDB или если KP не указан
|
case "tmdb":
|
||||||
return s.tmdb.GetTVShow(id, language)
|
return s.tmdb.GetTVShow(id, language)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если id_type не указан — старая логика по языку
|
||||||
|
if ShouldUseKinopoisk(language) && s.kpService != nil {
|
||||||
|
if kpFilm, err := s.kpService.GetFilmByKinopoiskId(id); err == nil && kpFilm != nil {
|
||||||
|
return MapKPFilmToTVShow(kpFilm), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.tmdb.GetTVShow(id, language)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TVService) GetPopular(page int, language string) (*models.TMDBTVResponse, error) {
|
func (s *TVService) GetPopular(page int, language string) (*models.TMDBTVResponse, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user