diff --git a/README.md b/README.md index 10560ea3..32a55d0d 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,10 @@ Check out [this project's issues](https://github.com/JamesHawkinss/movie-web/iss ## Rewrite TODO's -- [ ] Better provider errors (only fail if all failed, show individual fails somewhere) -- [ ] Better no results view +- [x] Better provider errors (only fail if all failed, show individual fails somewhere) +- [ ] Better search suffix view - [ ] Add back link of results view -- [ ] Add results list end +- [x] Add results list end - [ ] Add Brand tag top left - [ ] Add github and discord top right - [ ] Store watched percentage diff --git a/src/providers/index.ts b/src/providers/index.ts index 5eb00845..13342bf2 100644 --- a/src/providers/index.ts +++ b/src/providers/index.ts @@ -1,10 +1,12 @@ +import { tempScraper } from "./list/temp"; import { theFlixScraper } from "./list/theflix"; -import { MWMedia, MWMediaType, MWPortableMedia, MWQuery } from "./types"; +import { MWMassProviderOutput, MWMedia, MWMediaType, MWPortableMedia, MWQuery } from "./types"; import { MWWrappedMediaProvider, WrapProvider } from "./wrapper"; export * from "./types"; const mediaProvidersUnchecked: MWWrappedMediaProvider[] = [ WrapProvider(theFlixScraper), + WrapProvider(tempScraper), ]; export const mediaProviders: MWWrappedMediaProvider[] = mediaProvidersUnchecked.filter((v) => v.enabled); @@ -19,12 +21,38 @@ export function GetProvidersForType(type: MWMediaType) { /* ** Call search on all providers that matches query type */ -export async function SearchProviders(query: MWQuery): Promise { - const allQueries = GetProvidersForType(query.type).map((provider) => - provider.searchForMedia(query) - ); +export async function SearchProviders(query: MWQuery): Promise { + const allQueries = GetProvidersForType(query.type).map>(async (provider) => { + try { + return { + media: await provider.searchForMedia(query), + success: true, + id: provider.id, + } + } catch (err) { + console.error(`Failed running provider ${provider.id}`, err, query); + return { + media: [], + success: false, + id: provider.id, + } + } + }); const allResults = await Promise.all(allQueries); - return allResults.flatMap((results) => results); + const providerResults = allResults.map(provider => ({ success: provider.success, id: provider.id })); + const output = { + results: allResults.flatMap((results) => results.media), + providers: providerResults, + stats: { + total: providerResults.length, + failed: providerResults.filter(v=>!v.success).length, + succeeded: providerResults.filter(v=>v.success).length, + }, + }; + + if (output.stats.total === output.stats.failed) + throw new Error("All Scrapers failed"); + return output; } /* diff --git a/src/providers/list/temp/index.ts b/src/providers/list/temp/index.ts new file mode 100644 index 00000000..adb4e392 --- /dev/null +++ b/src/providers/list/temp/index.ts @@ -0,0 +1,30 @@ +import { + MWMediaProvider, + MWMediaType, + MWPortableMedia, + MWQuery, +} from "providers/types"; + +import { MWProviderMediaResult } from "providers"; + +export const tempScraper: MWMediaProvider = { + id: "temp", + enabled: true, + type: [MWMediaType.MOVIE, MWMediaType.SERIES], + displayName: "temp", + + async getMediaFromPortable( + media: MWPortableMedia + ): Promise { + return { + ...media, + year: "1234", + title: "temp", + }; + }, + + async searchForMedia(query: MWQuery): Promise { + + return []; + }, +}; diff --git a/src/providers/types.ts b/src/providers/types.ts index e362f856..39338bab 100644 --- a/src/providers/types.ts +++ b/src/providers/types.ts @@ -33,3 +33,16 @@ export interface MWMediaProvider { getMediaFromPortable(media: MWPortableMedia): Promise; searchForMedia(query: MWQuery): Promise; } + +export interface MWMassProviderOutput { + providers: { + id: string, + success: boolean, + }[]; + results: MWMedia[], + stats: { + total: number, + failed: number, + succeeded: number, + } +} diff --git a/src/views/SearchView.tsx b/src/views/SearchView.tsx index 0da9c806..0fbc0202 100644 --- a/src/views/SearchView.tsx +++ b/src/views/SearchView.tsx @@ -1,6 +1,12 @@ import { WatchedMediaCard } from "components/media/WatchedMediaCard"; import { SearchBarInput } from "components/SearchBar"; -import { MWMedia, MWMediaType, MWQuery, SearchProviders } from "providers"; +import { + MWMassProviderOutput, + MWMedia, + MWMediaType, + MWQuery, + SearchProviders, +} from "providers"; import { useEffect, useState } from "react"; import { ThinContainer } from "components/layout/ThinContainer"; import { SectionHeading } from "components/layout/SectionHeading"; @@ -15,8 +21,29 @@ function SearchLoading() { return ; } +function SearchSuffix(props: { + fails: number; + total: number; + resultsSize: number; +}) { + return ( +
+ {props.fails > 0 ? ( +

+ {props.fails}/{props.total} providers failed +

+ ) : null} + {props.resultsSize > 0 ? ( +

Thats all we have to show

+ ) : ( +

No results to show

+ )} +
+ ); +} + function SearchResultsView({ searchQuery }: { searchQuery: MWQuery }) { - const [results, setResults] = useState([]); + const [results, setResults] = useState(); const [runSearchQuery, loading, error, success] = useLoading( (query: MWQuery) => SearchProviders(query) ); @@ -34,9 +61,9 @@ function SearchResultsView({ searchQuery }: { searchQuery: MWQuery }) { return (
{/* results */} - {success && results.length > 0 ? ( + {success && results?.results.length ? ( - {results.map((v) => ( + {results.results.map((v) => ( ) : null} - {/* no results */} - {success && results.length === 0 ?

No results found

: null} + {/* search suffix */} + {success && results ? ( + + ) : null} {/* error */} {error ?

All scrapers failed

: null}