mirror of
https://github.com/movie-web/movie-web.git
synced 2025-01-01 11:05:59 +00:00
Error handling of providers + search suffix
This commit is contained in:
parent
8f76a5745f
commit
95ef071f59
|
@ -33,10 +33,10 @@ Check out [this project's issues](https://github.com/JamesHawkinss/movie-web/iss
|
||||||
|
|
||||||
## Rewrite TODO's
|
## Rewrite TODO's
|
||||||
|
|
||||||
- [ ] Better provider errors (only fail if all failed, show individual fails somewhere)
|
- [x] Better provider errors (only fail if all failed, show individual fails somewhere)
|
||||||
- [ ] Better no results view
|
- [ ] Better search suffix view
|
||||||
- [ ] Add back link of results view
|
- [ ] Add back link of results view
|
||||||
- [ ] Add results list end
|
- [x] Add results list end
|
||||||
- [ ] Add Brand tag top left
|
- [ ] Add Brand tag top left
|
||||||
- [ ] Add github and discord top right
|
- [ ] Add github and discord top right
|
||||||
- [ ] Store watched percentage
|
- [ ] Store watched percentage
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
import { tempScraper } from "./list/temp";
|
||||||
import { theFlixScraper } from "./list/theflix";
|
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";
|
import { MWWrappedMediaProvider, WrapProvider } from "./wrapper";
|
||||||
export * from "./types";
|
export * from "./types";
|
||||||
|
|
||||||
const mediaProvidersUnchecked: MWWrappedMediaProvider[] = [
|
const mediaProvidersUnchecked: MWWrappedMediaProvider[] = [
|
||||||
WrapProvider(theFlixScraper),
|
WrapProvider(theFlixScraper),
|
||||||
|
WrapProvider(tempScraper),
|
||||||
];
|
];
|
||||||
export const mediaProviders: MWWrappedMediaProvider[] =
|
export const mediaProviders: MWWrappedMediaProvider[] =
|
||||||
mediaProvidersUnchecked.filter((v) => v.enabled);
|
mediaProvidersUnchecked.filter((v) => v.enabled);
|
||||||
|
@ -19,12 +21,38 @@ export function GetProvidersForType(type: MWMediaType) {
|
||||||
/*
|
/*
|
||||||
** Call search on all providers that matches query type
|
** Call search on all providers that matches query type
|
||||||
*/
|
*/
|
||||||
export async function SearchProviders(query: MWQuery): Promise<MWMedia[]> {
|
export async function SearchProviders(query: MWQuery): Promise<MWMassProviderOutput> {
|
||||||
const allQueries = GetProvidersForType(query.type).map((provider) =>
|
const allQueries = GetProvidersForType(query.type).map<Promise<{ media: MWMedia[], success: boolean, id: string, }>>(async (provider) => {
|
||||||
provider.searchForMedia(query)
|
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);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
30
src/providers/list/temp/index.ts
Normal file
30
src/providers/list/temp/index.ts
Normal 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 [];
|
||||||
|
},
|
||||||
|
};
|
|
@ -33,3 +33,16 @@ export interface MWMediaProvider {
|
||||||
getMediaFromPortable(media: MWPortableMedia): Promise<MWProviderMediaResult>;
|
getMediaFromPortable(media: MWPortableMedia): Promise<MWProviderMediaResult>;
|
||||||
searchForMedia(query: MWQuery): Promise<MWProviderMediaResult[]>;
|
searchForMedia(query: MWQuery): Promise<MWProviderMediaResult[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MWMassProviderOutput {
|
||||||
|
providers: {
|
||||||
|
id: string,
|
||||||
|
success: boolean,
|
||||||
|
}[];
|
||||||
|
results: MWMedia[],
|
||||||
|
stats: {
|
||||||
|
total: number,
|
||||||
|
failed: number,
|
||||||
|
succeeded: number,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import { WatchedMediaCard } from "components/media/WatchedMediaCard";
|
import { WatchedMediaCard } from "components/media/WatchedMediaCard";
|
||||||
import { SearchBarInput } from "components/SearchBar";
|
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 { useEffect, useState } from "react";
|
||||||
import { ThinContainer } from "components/layout/ThinContainer";
|
import { ThinContainer } from "components/layout/ThinContainer";
|
||||||
import { SectionHeading } from "components/layout/SectionHeading";
|
import { SectionHeading } from "components/layout/SectionHeading";
|
||||||
|
@ -15,8 +21,29 @@ function SearchLoading() {
|
||||||
return <Loading className="my-12" text="Fetching your favourite shows..." />;
|
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 }) {
|
function SearchResultsView({ searchQuery }: { searchQuery: MWQuery }) {
|
||||||
const [results, setResults] = useState<MWMedia[]>([]);
|
const [results, setResults] = useState<MWMassProviderOutput | undefined>();
|
||||||
const [runSearchQuery, loading, error, success] = useLoading(
|
const [runSearchQuery, loading, error, success] = useLoading(
|
||||||
(query: MWQuery) => SearchProviders(query)
|
(query: MWQuery) => SearchProviders(query)
|
||||||
);
|
);
|
||||||
|
@ -34,9 +61,9 @@ function SearchResultsView({ searchQuery }: { searchQuery: MWQuery }) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{/* results */}
|
{/* results */}
|
||||||
{success && results.length > 0 ? (
|
{success && results?.results.length ? (
|
||||||
<SectionHeading title="Search results" icon={Icons.SEARCH}>
|
<SectionHeading title="Search results" icon={Icons.SEARCH}>
|
||||||
{results.map((v) => (
|
{results.results.map((v) => (
|
||||||
<WatchedMediaCard
|
<WatchedMediaCard
|
||||||
key={[v.mediaId, v.providerId].join("|")}
|
key={[v.mediaId, v.providerId].join("|")}
|
||||||
media={v}
|
media={v}
|
||||||
|
@ -45,8 +72,14 @@ function SearchResultsView({ searchQuery }: { searchQuery: MWQuery }) {
|
||||||
</SectionHeading>
|
</SectionHeading>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{/* no results */}
|
{/* search suffix */}
|
||||||
{success && results.length === 0 ? <p>No results found</p> : null}
|
{success && results ? (
|
||||||
|
<SearchSuffix
|
||||||
|
resultsSize={results.results.length}
|
||||||
|
fails={results.stats.failed}
|
||||||
|
total={results.stats.total}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{/* error */}
|
{/* error */}
|
||||||
{error ? <p>All scrapers failed</p> : null}
|
{error ? <p>All scrapers failed</p> : null}
|
||||||
|
|
Loading…
Reference in a new issue