mirror of
https://github.com/p-stream/p-stream.git
synced 2026-03-11 09:45:33 +00:00
Speed up episodes carousel loading by making it async
Maps an array to a promise that resolves with an array of results, limiting the number of concurrent operations.
This commit is contained in:
parent
9aa6e4088a
commit
94f6ecf089
3 changed files with 61 additions and 18 deletions
|
|
@ -55,6 +55,24 @@ export function TMDBMediaToMediaItemType(
|
|||
throw new Error("unsupported type");
|
||||
}
|
||||
|
||||
export function formatTMDBEpisode(v: TMDBEpisodeShort): {
|
||||
id: string;
|
||||
number: number;
|
||||
title: string;
|
||||
air_date: string;
|
||||
still_path: string | null;
|
||||
overview: string;
|
||||
} {
|
||||
return {
|
||||
id: v.id.toString(),
|
||||
number: v.episode_number,
|
||||
title: v.title,
|
||||
air_date: v.air_date,
|
||||
still_path: v.still_path,
|
||||
overview: v.overview,
|
||||
};
|
||||
}
|
||||
|
||||
export function formatTMDBMeta(
|
||||
media: TMDBMediaResult,
|
||||
season?: TMDBSeasonMetaResult,
|
||||
|
|
@ -88,14 +106,7 @@ export function formatTMDBMeta(
|
|||
title: season.title,
|
||||
episodes: season.episodes
|
||||
.sort((a, b) => a.episode_number - b.episode_number)
|
||||
.map((v) => ({
|
||||
id: v.id.toString(),
|
||||
number: v.episode_number,
|
||||
title: v.title,
|
||||
air_date: v.air_date,
|
||||
still_path: v.still_path,
|
||||
overview: v.overview,
|
||||
})),
|
||||
.map(formatTMDBEpisode),
|
||||
}
|
||||
: (undefined as any),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
|
|||
import { useAsync } from "react-use";
|
||||
|
||||
import { getMetaFromId } from "@/backend/metadata/getmeta";
|
||||
import { formatTMDBEpisode, getEpisodes } from "@/backend/metadata/tmdb";
|
||||
import { MWMediaType, MWSeasonMeta } from "@/backend/metadata/types/mw";
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { ProgressRing } from "@/components/layout/ProgressRing";
|
||||
|
|
@ -20,6 +21,7 @@ import { PlayerMeta } from "@/stores/player/slices/source";
|
|||
import { usePlayerStore } from "@/stores/player/store";
|
||||
import { usePreferencesStore } from "@/stores/preferences";
|
||||
import { useProgressStore } from "@/stores/progress";
|
||||
import { concurrentMap } from "@/utils/async";
|
||||
import { scrollToElement } from "@/utils/scroll";
|
||||
|
||||
import { hasAired } from "../utils/aired";
|
||||
|
|
@ -594,23 +596,21 @@ export function EpisodesView({
|
|||
if (selectedSeason === "favorites" && meta?.tmdbId && seasons) {
|
||||
setAllSeasonsLoading(true);
|
||||
const loadAllSeasons = async () => {
|
||||
const seasonPromises = seasons.map(async (season) => {
|
||||
const results = await concurrentMap(seasons, 5, async (season) => {
|
||||
try {
|
||||
const data = await getMetaFromId(
|
||||
MWMediaType.SERIES,
|
||||
meta.tmdbId,
|
||||
season.id,
|
||||
);
|
||||
return data?.meta.type === MWMediaType.SERIES
|
||||
? data.meta.seasonData
|
||||
: null;
|
||||
const episodes = await getEpisodes(meta.tmdbId!, season.number);
|
||||
return {
|
||||
id: season.id,
|
||||
number: season.number,
|
||||
title: season.title,
|
||||
episodes: episodes.map(formatTMDBEpisode),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Failed to load season ${season.id}:`, error);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const results = await Promise.all(seasonPromises);
|
||||
setAllSeasonsData(results.filter(Boolean));
|
||||
setAllSeasonsLoading(false);
|
||||
};
|
||||
|
|
|
|||
32
src/utils/async.ts
Normal file
32
src/utils/async.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* Maps an array to a promise that resolves with an array of results,
|
||||
* limiting the number of concurrent operations.
|
||||
*
|
||||
* @param items The array of items to map
|
||||
* @param concurrency The maximum number of concurrent operations
|
||||
* @param fn The async function to apply to each item
|
||||
* @returns A promise that resolves with an array of results
|
||||
*/
|
||||
export async function concurrentMap<T, R>(
|
||||
items: T[],
|
||||
concurrency: number,
|
||||
fn: (item: T) => Promise<R>,
|
||||
): Promise<R[]> {
|
||||
const results: R[] = new Array(items.length);
|
||||
const queue = items.map((item, index) => ({ item, index }));
|
||||
|
||||
const workers = Array.from(
|
||||
{ length: Math.min(concurrency, items.length) },
|
||||
async () => {
|
||||
while (queue.length > 0) {
|
||||
const entry = queue.shift();
|
||||
if (!entry) break;
|
||||
const { item, index } = entry;
|
||||
results[index] = await fn(item);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
await Promise.all(workers);
|
||||
return results;
|
||||
}
|
||||
Loading…
Reference in a new issue