diff --git a/src/backend/metadata/getmeta.ts b/src/backend/metadata/getmeta.ts index b2166c34..fe2ea62b 100644 --- a/src/backend/metadata/getmeta.ts +++ b/src/backend/metadata/getmeta.ts @@ -8,6 +8,7 @@ import { getExternalIds, getMediaDetails, getMediaPoster, + getMovieFromExternalId, mediaTypeToTMDB, } from "./tmdb"; import { @@ -206,11 +207,23 @@ export async function convertLegacyUrl( if (url.startsWith("/media/JW")) { const urlParts = url.split("/").slice(2); const [, type, id] = urlParts[0].split("-", 3); - const meta = await getLegacyMetaFromId(TMDBMediaToMediaType(type), id); + + const mediaType = TMDBMediaToMediaType(type); + const meta = await getLegacyMetaFromId(mediaType, id); + if (!meta) return undefined; - const tmdbId = meta.tmdbId; - if (!tmdbId) return undefined; - return `/media/tmdb-${type}-${tmdbId}`; + const { tmdbId, imdbId } = meta; + if (!tmdbId && !imdbId) return undefined; + + // movies always have an imdb id on tmdb + if (imdbId && mediaType === MWMediaType.MOVIE) { + const movieId = await getMovieFromExternalId(imdbId); + if (movieId) return `/media/tmdb-movie-${movieId}`; + } + + if (tmdbId) { + return `/media/tmdb-${type}-${tmdbId}`; + } } return undefined; } diff --git a/src/backend/metadata/tmdb.ts b/src/backend/metadata/tmdb.ts index f5d1e370..db665528 100644 --- a/src/backend/metadata/tmdb.ts +++ b/src/backend/metadata/tmdb.ts @@ -2,6 +2,7 @@ import { conf } from "@/setup/config"; import { MWMediaMeta, MWMediaType, MWSeasonMeta } from "./types/mw"; import { + ExternalIdMovieSearchResult, TMDBContentTypes, TMDBEpisodeShort, TMDBExternalIds, @@ -195,6 +196,19 @@ export async function getExternalIds( return data; } +export async function getMovieFromExternalId( + imdbId: string +): Promise { + const data = await get(`/find/${imdbId}`, { + external_source: "imdb_id", + }); + + const movie = data.movie_results[0]; + if (!movie) return undefined; + + return movie.id.toString(); +} + export function formatTMDBSearchResult( result: TMDBShowResult | TMDBMovieResult, mediatype: TMDBContentTypes diff --git a/src/backend/metadata/types/tmdb.ts b/src/backend/metadata/types/tmdb.ts index cb5e9aa4..843786f4 100644 --- a/src/backend/metadata/types/tmdb.ts +++ b/src/backend/metadata/types/tmdb.ts @@ -282,3 +282,27 @@ export interface TMDBMovieExternalIds { } export type TMDBExternalIds = TMDBShowExternalIds | TMDBMovieExternalIds; + +export interface ExternalIdMovieSearchResult { + movie_results: { + adult: boolean; + backdrop_path: string; + id: number; + title: string; + original_language: string; + original_title: string; + overview: string; + poster_path: string; + media_type: string; + genre_ids: number[]; + popularity: number; + release_date: string; + video: boolean; + vote_average: number; + vote_count: number; + }[]; + person_results: any[]; + tv_results: any[]; + tv_episode_results: any[]; + tv_season_results: any[]; +} diff --git a/src/setup/App.tsx b/src/setup/App.tsx index d0a0887f..0516eb48 100644 --- a/src/setup/App.tsx +++ b/src/setup/App.tsx @@ -1,11 +1,5 @@ -import { lazy } from "react"; -import { - Redirect, - Route, - Switch, - useHistory, - useLocation, -} from "react-router-dom"; +import { lazy, useEffect, useState } from "react"; +import { Redirect, Route, Switch, useLocation } from "react-router-dom"; import { convertLegacyUrl } from "@/backend/metadata/getmeta"; import { MWMediaType } from "@/backend/metadata/types/mw"; @@ -19,86 +13,105 @@ import { NotFoundPage } from "@/views/notfound/NotFoundView"; import { V2MigrationView } from "@/views/other/v2Migration"; import { SearchView } from "@/views/search/SearchView"; -function App() { +// eslint-disable-next-line react/function-component-definition, react/prop-types +const LegacyUrlView: React.FC = ({ children }) => { const location = useLocation(); - const history = useHistory(); + const [redirectUrl, setRedirectUrl] = useState(null); - // Call the conversion function and redirect if necessary - convertLegacyUrl(location.pathname).then((convertedUrl) => { - if (convertedUrl) { - history.replace(convertedUrl); - } - }); + useEffect(() => { + // Call the conversion function and set the redirect URL if necessary + convertLegacyUrl(location.pathname).then((convertedUrl) => { + if (convertedUrl) { + setRedirectUrl(convertedUrl); + } + }); + }, [location.pathname]); + + if (redirectUrl) { + return ; + } + + // eslint-disable-next-line react/jsx-no-useless-fragment + return <>{children}; +}; + +function App() { return ( - - {/* functional routes */} - - - - + + + {/* functional routes */} + + + + - {/* pages */} - - - + {/* pages */} + + + - {/* other */} - import("@/views/developer/DeveloperView") - )} - /> - import("@/views/developer/VideoTesterView") - )} - /> - {/* developer routes that can abuse workers are disabled in production */} - {process.env.NODE_ENV === "development" ? ( - <> - import("@/views/developer/TestView") - )} - /> + {/* other */} + import("@/views/developer/DeveloperView") + )} + /> + import("@/views/developer/VideoTesterView") + )} + /> + {/* developer routes that can abuse workers are disabled in production */} + {process.env.NODE_ENV === "development" ? ( + <> + import("@/views/developer/TestView") + )} + /> - import("@/views/developer/ProviderTesterView") - )} - /> - import("@/views/developer/EmbedTesterView") - )} - /> - - ) : null} - - + import("@/views/developer/ProviderTesterView") + )} + /> + import("@/views/developer/EmbedTesterView") + )} + /> + + ) : null} + + +