From 95f03db5b2237cefc5b7126e3dc6648c19df8a18 Mon Sep 17 00:00:00 2001 From: castdrian Date: Fri, 30 Jun 2023 12:20:01 +0200 Subject: [PATCH] refactor search --- src/backend/metadata/getmeta.ts | 3 +- src/backend/metadata/search.ts | 15 ++---- src/backend/metadata/tmdb.ts | 82 ++++++++++-------------------- src/backend/metadata/types/tmdb.ts | 57 +++------------------ src/components/SearchBar.tsx | 44 +--------------- src/state/watched/migrations/v3.ts | 6 ++- 6 files changed, 45 insertions(+), 162 deletions(-) diff --git a/src/backend/metadata/getmeta.ts b/src/backend/metadata/getmeta.ts index 6081e5ad..b2a84433 100644 --- a/src/backend/metadata/getmeta.ts +++ b/src/backend/metadata/getmeta.ts @@ -19,6 +19,7 @@ import { } from "./types/justwatch"; import { MWMediaMeta, MWMediaType } from "./types/mw"; import { + TMDBContentTypes, TMDBMediaResult, TMDBMovieData, TMDBSeasonMetaResult, @@ -177,7 +178,7 @@ export async function convertLegacyUrl( const urlParts = url.split("/").slice(2); const [, type, id] = urlParts[0].split("-", 3); - const mediaType = TMDBMediaToMediaType(type); + const mediaType = TMDBMediaToMediaType(type as TMDBContentTypes); const meta = await getLegacyMetaFromId(mediaType, id); if (!meta) return undefined; diff --git a/src/backend/metadata/search.ts b/src/backend/metadata/search.ts index 0d8f561f..9ae985e7 100644 --- a/src/backend/metadata/search.ts +++ b/src/backend/metadata/search.ts @@ -1,11 +1,6 @@ import { SimpleCache } from "@/utils/cache"; -import { - formatTMDBMeta, - formatTMDBSearchResult, - mediaTypeToTMDB, - searchMedia, -} from "./tmdb"; +import { formatTMDBMeta, formatTMDBSearchResult, multiSearch } from "./tmdb"; import { MWMediaMeta, MWQuery } from "./types/mw"; const cache = new SimpleCache(); @@ -16,11 +11,11 @@ cache.initialize(); export async function searchForMedia(query: MWQuery): Promise { if (cache.has(query)) return cache.get(query) as MWMediaMeta[]; - const { searchQuery, type } = query; + const { searchQuery } = query; - const data = await searchMedia(searchQuery, mediaTypeToTMDB(type)); - const results = data.results.map((v) => { - const formattedResult = formatTMDBSearchResult(v, mediaTypeToTMDB(type)); + const data = await multiSearch(searchQuery); + const results = data.map((v) => { + const formattedResult = formatTMDBSearchResult(v, v.media_type); return formatTMDBMeta(formattedResult); }); diff --git a/src/backend/metadata/tmdb.ts b/src/backend/metadata/tmdb.ts index 64304900..9f42ce4d 100644 --- a/src/backend/metadata/tmdb.ts +++ b/src/backend/metadata/tmdb.ts @@ -11,29 +11,25 @@ import { TMDBMediaResult, TMDBMovieData, TMDBMovieExternalIds, - TMDBMovieResponse, - TMDBMovieResult, TMDBMovieSearchResult, TMDBSearchResult, TMDBSeason, TMDBSeasonMetaResult, TMDBShowData, TMDBShowExternalIds, - TMDBShowResponse, - TMDBShowResult, TMDBShowSearchResult, } from "./types/tmdb"; import { mwFetch } from "../helpers/fetch"; export function mediaTypeToTMDB(type: MWMediaType): TMDBContentTypes { - if (type === MWMediaType.MOVIE) return "movie"; - if (type === MWMediaType.SERIES) return "show"; + if (type === MWMediaType.MOVIE) return TMDBContentTypes.MOVIE; + if (type === MWMediaType.SERIES) return TMDBContentTypes.TV; throw new Error("unsupported type"); } -export function TMDBMediaToMediaType(type: string): MWMediaType { - if (type === "movie") return MWMediaType.MOVIE; - if (type === "show") return MWMediaType.SERIES; +export function TMDBMediaToMediaType(type: TMDBContentTypes): MWMediaType { + if (type === TMDBContentTypes.MOVIE) return MWMediaType.MOVIE; + if (type === TMDBContentTypes.TV) return MWMediaType.SERIES; throw new Error("unsupported type"); } @@ -103,7 +99,7 @@ export function decodeTMDBId( if (prefix !== "tmdb") return null; let mediaType; try { - mediaType = TMDBMediaToMediaType(type); + mediaType = TMDBMediaToMediaType(type as TMDBContentTypes); } catch { return null; } @@ -131,36 +127,6 @@ async function get(url: string, params?: object): Promise { return res; } -export async function searchMedia( - query: string, - type: TMDBContentTypes -): Promise { - let data; - - switch (type) { - case "movie": - data = await get("search/movie", { - query, - include_adult: false, - language: "en-US", - page: 1, - }); - break; - case "show": - data = await get("search/tv", { - query, - include_adult: false, - language: "en-US", - page: 1, - }); - break; - default: - throw new Error("Invalid media type"); - } - - return data; -} - export async function multiSearch( query: string ): Promise<(TMDBMovieSearchResult | TMDBShowSearchResult)[]> { @@ -172,7 +138,9 @@ export async function multiSearch( }); // filter out results that aren't movies or shows const results = data.results.filter( - (r) => r.media_type === "movie" || r.media_type === "tv" + (r) => + r.media_type === TMDBContentTypes.MOVIE || + r.media_type === TMDBContentTypes.TV ); return results; } @@ -183,31 +151,32 @@ export async function generateQuickSearchMediaUrl( const data = await multiSearch(query); if (data.length === 0) return undefined; const result = data[0]; - const type = result.media_type === "movie" ? "movie" : "show"; - const title = result.media_type === "movie" ? result.title : result.name; + const title = + result.media_type === TMDBContentTypes.MOVIE ? result.title : result.name; return `/media/${TMDBIdToUrlId( - TMDBMediaToMediaType(type), + TMDBMediaToMediaType(result.media_type), result.id.toString(), title )}`; } // Conditional type which for inferring the return type based on the content type -type MediaDetailReturn = T extends "movie" - ? TMDBMovieData - : T extends "show" - ? TMDBShowData - : never; +type MediaDetailReturn = + T extends TMDBContentTypes.MOVIE + ? TMDBMovieData + : T extends TMDBContentTypes.TV + ? TMDBShowData + : never; export function getMediaDetails< T extends TMDBContentTypes, TReturn = MediaDetailReturn >(id: string, type: T): Promise { - if (type === "movie") { + if (type === TMDBContentTypes.MOVIE) { return get(`/movie/${id}`); } - if (type === "show") { + if (type === TMDBContentTypes.TV) { return get(`/tv/${id}`); } throw new Error("Invalid media type"); @@ -236,10 +205,10 @@ export async function getExternalIds( let data; switch (type) { - case "movie": + case TMDBContentTypes.MOVIE: data = await get(`/movie/${id}/external_ids`); break; - case "show": + case TMDBContentTypes.TV: data = await get(`/tv/${id}/external_ids`); break; default: @@ -263,12 +232,12 @@ export async function getMovieFromExternalId( } export function formatTMDBSearchResult( - result: TMDBShowResult | TMDBMovieResult, + result: TMDBMovieSearchResult | TMDBShowSearchResult, mediatype: TMDBContentTypes ): TMDBMediaResult { const type = TMDBMediaToMediaType(mediatype); if (type === MWMediaType.SERIES) { - const show = result as TMDBShowResult; + const show = result as TMDBShowSearchResult; return { title: show.name, poster: getMediaPoster(show.poster_path), @@ -277,7 +246,8 @@ export function formatTMDBSearchResult( object_type: mediatype, }; } - const movie = result as TMDBMovieResult; + + const movie = result as TMDBMovieSearchResult; return { title: movie.title, diff --git a/src/backend/metadata/types/tmdb.ts b/src/backend/metadata/types/tmdb.ts index 8f6bf14b..12cc0d1a 100644 --- a/src/backend/metadata/types/tmdb.ts +++ b/src/backend/metadata/types/tmdb.ts @@ -1,4 +1,7 @@ -export type TMDBContentTypes = "movie" | "show"; +export enum TMDBContentTypes { + MOVIE = "movie", + TV = "tv", +} export type TMDBSeasonShort = { title: string; @@ -183,54 +186,6 @@ export interface TMDBEpisodeResult { }; } -export interface TMDBShowResult { - adult: boolean; - backdrop_path: string | null; - genre_ids: number[]; - id: number; - origin_country: string[]; - original_language: string; - original_name: string; - overview: string; - popularity: number; - poster_path: string | null; - first_air_date: string; - name: string; - vote_average: number; - vote_count: number; -} - -export interface TMDBShowResponse { - page: number; - results: TMDBShowResult[]; - total_pages: number; - total_results: number; -} - -export interface TMDBMovieResult { - adult: boolean; - backdrop_path: string | null; - genre_ids: number[]; - id: number; - original_language: string; - original_title: string; - overview: string; - popularity: number; - poster_path: string | null; - release_date: string; - title: string; - video: boolean; - vote_average: number; - vote_count: number; -} - -export interface TMDBMovieResponse { - page: number; - results: TMDBMovieResult[]; - total_pages: number; - total_results: number; -} - export interface TMDBEpisode { air_date: string; episode_number: number; @@ -316,7 +271,7 @@ export interface TMDBMovieSearchResult { original_title: string; overview: string; poster_path: string; - media_type: "movie"; + media_type: TMDBContentTypes.MOVIE; genre_ids: number[]; popularity: number; release_date: string; @@ -334,7 +289,7 @@ export interface TMDBShowSearchResult { original_name: string; overview: string; poster_path: string; - media_type: "tv"; + media_type: TMDBContentTypes.TV; genre_ids: number[]; popularity: number; first_air_date: string; diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index 431de337..1d2ce354 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -1,14 +1,9 @@ -import { useState } from "react"; -import { useTranslation } from "react-i18next"; +import { MWQuery } from "@/backend/metadata/types/mw"; -import { MWMediaType, MWQuery } from "@/backend/metadata/types/mw"; - -import { DropdownButton } from "./buttons/DropdownButton"; import { Icon, Icons } from "./Icon"; import { TextInputControl } from "./text-inputs/TextInputControl"; export interface SearchBarProps { - buttonText?: string; placeholder?: string; onChange: (value: MWQuery, force: boolean) => void; onUnFocus: () => void; @@ -16,9 +11,6 @@ export interface SearchBarProps { } export function SearchBarInput(props: SearchBarProps) { - const { t } = useTranslation(); - - const [dropdownOpen, setDropdownOpen] = useState(false); function setSearch(value: string) { props.onChange( { @@ -28,15 +20,6 @@ export function SearchBarInput(props: SearchBarProps) { false ); } - function setType(type: string) { - props.onChange( - { - ...props.value, - type: type as MWMediaType, - }, - true - ); - } return (
@@ -51,31 +34,6 @@ export function SearchBarInput(props: SearchBarProps) { className="w-full flex-1 bg-transparent px-4 py-4 pl-12 text-white placeholder-denim-700 focus:outline-none sm:py-4 sm:pr-2" placeholder={props.placeholder} /> - -
- setDropdownOpen(val)} - selectedItem={props.value.type} - setSelectedItem={(val) => setType(val)} - options={[ - { - id: MWMediaType.MOVIE, - name: t("searchBar.movie"), - icon: Icons.FILM, - }, - { - id: MWMediaType.SERIES, - name: t("searchBar.series"), - icon: Icons.CLAPPER_BOARD, - }, - ]} - onClick={() => setDropdownOpen((old) => !old)} - > - {props.buttonText || t("searchBar.search")} - -
); } diff --git a/src/state/watched/migrations/v3.ts b/src/state/watched/migrations/v3.ts index dffae637..f546c943 100644 --- a/src/state/watched/migrations/v3.ts +++ b/src/state/watched/migrations/v3.ts @@ -5,6 +5,7 @@ import { getMovieFromExternalId, } from "@/backend/metadata/tmdb"; import { MWMediaType } from "@/backend/metadata/types/mw"; +import { TMDBContentTypes } from "@/backend/metadata/types/tmdb"; import { BookmarkStoreData } from "@/state/bookmark/types"; import { isNotNull } from "@/utils/typeguard"; @@ -59,7 +60,10 @@ export async function migrateV3Videos( clone.item.meta.id = migratedId; if (clone.item.series) { const series = clone.item.series; - const details = await getMediaDetails(migratedId, "show"); + const details = await getMediaDetails( + migratedId, + TMDBContentTypes.TV + ); const season = details.seasons.find( (v) => v.season_number === series.season