Error handling of providers + search suffix

This commit is contained in:
Jelle van Snik 2022-02-17 21:48:35 +01:00
parent 8f76a5745f
commit 95ef071f59
5 changed files with 119 additions and 15 deletions

View file

@ -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

View file

@ -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<MWMedia[]> {
const allQueries = GetProvidersForType(query.type).map((provider) =>
provider.searchForMedia(query)
);
export async function SearchProviders(query: MWQuery): Promise<MWMassProviderOutput> {
const allQueries = GetProvidersForType(query.type).map<Promise<{ media: MWMedia[], success: boolean, id: string, }>>(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;
}
/*

View file

@ -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<MWProviderMediaResult> {
return {
...media,
year: "1234",
title: "temp",
};
},
async searchForMedia(query: MWQuery): Promise<MWProviderMediaResult[]> {
return [];
},
};

View file

@ -33,3 +33,16 @@ export interface MWMediaProvider {
getMediaFromPortable(media: MWPortableMedia): Promise<MWProviderMediaResult>;
searchForMedia(query: MWQuery): Promise<MWProviderMediaResult[]>;
}
export interface MWMassProviderOutput {
providers: {
id: string,
success: boolean,
}[];
results: MWMedia[],
stats: {
total: number,
failed: number,
succeeded: number,
}
}

View file

@ -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 <Loading className="my-12" text="Fetching your favourite shows..." />;
}
function SearchSuffix(props: {
fails: number;
total: number;
resultsSize: number;
}) {
return (
<div>
{props.fails > 0 ? (
<p>
{props.fails}/{props.total} providers failed
</p>
) : null}
{props.resultsSize > 0 ? (
<p>Thats all we have to show</p>
) : (
<p>No results to show</p>
)}
</div>
);
}
function SearchResultsView({ searchQuery }: { searchQuery: MWQuery }) {
const [results, setResults] = useState<MWMedia[]>([]);
const [results, setResults] = useState<MWMassProviderOutput | undefined>();
const [runSearchQuery, loading, error, success] = useLoading(
(query: MWQuery) => SearchProviders(query)
);
@ -34,9 +61,9 @@ function SearchResultsView({ searchQuery }: { searchQuery: MWQuery }) {
return (
<div>
{/* results */}
{success && results.length > 0 ? (
{success && results?.results.length ? (
<SectionHeading title="Search results" icon={Icons.SEARCH}>
{results.map((v) => (
{results.results.map((v) => (
<WatchedMediaCard
key={[v.mediaId, v.providerId].join("|")}
media={v}
@ -45,8 +72,14 @@ function SearchResultsView({ searchQuery }: { searchQuery: MWQuery }) {
</SectionHeading>
) : null}
{/* no results */}
{success && results.length === 0 ? <p>No results found</p> : null}
{/* search suffix */}
{success && results ? (
<SearchSuffix
resultsSize={results.results.length}
fails={results.stats.failed}
total={results.stats.total}
/>
) : null}
{/* error */}
{error ? <p>All scrapers failed</p> : null}