diff --git a/backend/daos/movie_dao.go b/backend/daos/movie_dao.go
index 741c1a3..e918530 100644
--- a/backend/daos/movie_dao.go
+++ b/backend/daos/movie_dao.go
@@ -10,6 +10,7 @@ type MovieDAO interface {
FindByIDAndType(id int64, mediaType string) (*models.Movie, error)
FindByID(id int64) (*models.Movie, error)
CreateMovieWithGenres(movie *models.Movie, genres []models.Genre) error
+ UpdateImdbID(id int64, imdbID *string) error
}
type movieDAO struct {
@@ -55,3 +56,7 @@ func (d *movieDAO) CreateMovieWithGenres(movie *models.Movie, genres []models.Ge
return nil
})
}
+
+func (d *movieDAO) UpdateImdbID(id int64, imdbID *string) error {
+ return d.db.Model(&models.Movie{}).Where("id = ?", id).Update("imdb_id", imdbID).Error
+}
diff --git a/backend/models/movie.go b/backend/models/movie.go
index 7841320..0bec093 100644
--- a/backend/models/movie.go
+++ b/backend/models/movie.go
@@ -13,6 +13,7 @@ type Movie struct {
ReleaseDate *time.Time `gorm:"type:date;column:release_date" json:"release_date"`
PosterPath *string `gorm:"type:text;column:poster_path" json:"poster_path"`
Popularity *float64 `gorm:"type:numeric;column:popularity" json:"popularity"`
+ ImdbID *string `gorm:"size:20;column:imdb_id" json:"imdb_id"`
MediaType string `gorm:"type:enum('movie','tv');not null;default:'movie';column:media_type" json:"media_type"`
SeasonsCount *int `gorm:"column:seasons_count" json:"seasons_count"`
diff --git a/backend/services/list_service.go b/backend/services/list_service.go
index 8ad9b30..62e502b 100644
--- a/backend/services/list_service.go
+++ b/backend/services/list_service.go
@@ -264,6 +264,10 @@ type tmdbTVResponse struct {
} `json:"genres"`
}
+type tmdbExternalIDsResponse struct {
+ ImdbID *string `json:"imdb_id"`
+}
+
func (s *listService) AddMediaToList(ctx context.Context, listID int64, userID int64, mediaID int64, mediaType string) (*models.ListMovie, *models.Movie, error) {
if mediaType != "movie" && mediaType != "tv" {
return nil, nil, ErrInvalidMediaType
@@ -290,6 +294,17 @@ func (s *listService) AddMediaToList(ctx context.Context, listID int64, userID i
var movie *models.Movie
if existingMovie != nil {
movie = existingMovie
+ // If movie exists but has no IMDB ID, fetch and update it
+ if movie.ImdbID == nil {
+ if imdbID, _ := s.fetchIMDBID(ctx, mediaID, mediaType); imdbID != nil {
+ movie.ImdbID = imdbID
+ // Update the movie in the database with the IMDB ID
+ if updateErr := s.movies.UpdateImdbID(movie.ID, imdbID); updateErr != nil {
+ // Log error but don't fail the operation
+ fmt.Println("Warning: failed to update IMDB ID for movie", movie.ID, ":", updateErr)
+ }
+ }
+ }
} else {
var fetchErr error
movie, fetchErr = s.fetchAndStoreFromTMDB(ctx, mediaID, mediaType)
@@ -315,6 +330,36 @@ func (s *listService) AddMediaToList(ctx context.Context, listID int64, userID i
return lm, movie, nil
}
+func (s *listService) fetchIMDBID(ctx context.Context, id int64, mediaType string) (*string, error) {
+ var url string
+ if mediaType == "movie" {
+ url = fmt.Sprintf("https://api.themoviedb.org/3/movie/%d/external_ids", id)
+ } else {
+ url = fmt.Sprintf("https://api.themoviedb.org/3/tv/%d/external_ids", id)
+ }
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
+ if err != nil {
+ return nil, err
+ }
+ req.Header.Set("Accept", "application/json")
+ if s.tmdbToken != "" {
+ req.Header.Set("Authorization", "Bearer "+s.tmdbToken)
+ }
+ resp, err := s.httpClient.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ return nil, nil
+ }
+ var externalIDs tmdbExternalIDsResponse
+ if err := json.NewDecoder(resp.Body).Decode(&externalIDs); err != nil {
+ return nil, nil
+ }
+ return externalIDs.ImdbID, nil
+}
+
func (s *listService) fetchAndStoreFromTMDB(ctx context.Context, id int64, mediaType string) (*models.Movie, error) {
var url string
if mediaType == "movie" {
@@ -345,6 +390,8 @@ func (s *listService) fetchAndStoreFromTMDB(ctx context.Context, id int64, media
return nil, ErrTMDBUnavailable
}
+ imdbID, _ := s.fetchIMDBID(ctx, id, mediaType)
+
if mediaType == "movie" {
var m tmdbMovieResponse
if err := json.NewDecoder(resp.Body).Decode(&m); err != nil {
@@ -366,6 +413,7 @@ func (s *listService) fetchAndStoreFromTMDB(ctx context.Context, id int64, media
ReleaseDate: release,
PosterPath: m.PosterPath,
Popularity: m.Popularity,
+ ImdbID: imdbID,
}
genres := make([]models.Genre, 0, len(m.Genres))
for _, g := range m.Genres {
@@ -400,6 +448,7 @@ func (s *listService) fetchAndStoreFromTMDB(ctx context.Context, id int64, media
SeasonsCount: tv.NumberOfSeasons,
EpisodesCount: tv.NumberOfEpisodes,
SeriesStatus: tv.Status,
+ ImdbID: imdbID,
}
genres := make([]models.Genre, 0, len(tv.Genres))
for _, g := range tv.Genres {
diff --git a/frontend/src/components/movie/MovieOverlay.tsx b/frontend/src/components/movie/MovieOverlay.tsx
index 5ca0d76..ed4ed74 100644
--- a/frontend/src/components/movie/MovieOverlay.tsx
+++ b/frontend/src/components/movie/MovieOverlay.tsx
@@ -1,5 +1,5 @@
import { useState } from 'react'
-import { X, Film, Calendar, Languages, TrendingUp, Tv } from 'lucide-react'
+import { X, Film, Calendar, Languages, TrendingUp, Tv, ExternalLink } from 'lucide-react'
import { StarRating } from './StarRating'
import { CommentSection } from './CommentSection'
import { UserAvatar } from '@/components/UserAvatar'
@@ -31,6 +31,7 @@ interface MovieOverlayProps {
overview?: string | null
original_lang?: string | null
popularity?: number | null
+ imdb_id?: string | null
seasons_count?: number | null
episodes_count?: number | null
series_status?: string | null
@@ -160,6 +161,21 @@ export function MovieOverlay({
{media.media_type === 'movie' ?
diff --git a/frontend/src/services/lists.ts b/frontend/src/services/lists.ts index d69f3b0..0f32b8f 100644 --- a/frontend/src/services/lists.ts +++ b/frontend/src/services/lists.ts @@ -115,6 +115,7 @@ export interface MovieDTO { release_date?: string | null poster_path?: string | null popularity?: number | null + imdb_id?: string | null media_type: 'movie' | 'tv' seasons_count?: number | null episodes_count?: number | null