mirror of
https://gitlab.com/foxixus/neomovies-api.git
synced 2025-10-27 17:38:51 +05:00
feat(api): support prefixed IDs in routes and add unified mappers scaffolding
- Handlers parse IDs like kp_123 / tmdb_456 and set id_type accordingly - Add KP->Unified and TMDB->Unified movie mappers (basic fields) - Keep backward compatibility for numeric IDs
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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) {
|
||||
vars := mux.Vars(r)
|
||||
id, err := strconv.Atoi(vars["id"])
|
||||
rawID := vars["id"]
|
||||
|
||||
// Support formats: "123" (old), "kp_123", "tmdb_123"
|
||||
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)
|
||||
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)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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) {
|
||||
vars := mux.Vars(r)
|
||||
id, err := strconv.Atoi(vars["id"])
|
||||
rawID := vars["id"]
|
||||
|
||||
// Support formats: "123" (old), "kp_123", "tmdb_123"
|
||||
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)
|
||||
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)
|
||||
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 {
|
||||
if kpSearch == nil {
|
||||
return &models.TMDBResponse{
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"neomovies-api/pkg/models"
|
||||
)
|
||||
@@ -179,6 +180,80 @@ func (s *TMDBService) GetTVShow(id int, language string) (*models.TVShow, error)
|
||||
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) {
|
||||
params := url.Values{}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user