mirror of
https://github.com/p-stream/p-stream.git
synced 2026-03-11 17:55: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");
|
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(
|
export function formatTMDBMeta(
|
||||||
media: TMDBMediaResult,
|
media: TMDBMediaResult,
|
||||||
season?: TMDBSeasonMetaResult,
|
season?: TMDBSeasonMetaResult,
|
||||||
|
|
@ -88,14 +106,7 @@ export function formatTMDBMeta(
|
||||||
title: season.title,
|
title: season.title,
|
||||||
episodes: season.episodes
|
episodes: season.episodes
|
||||||
.sort((a, b) => a.episode_number - b.episode_number)
|
.sort((a, b) => a.episode_number - b.episode_number)
|
||||||
.map((v) => ({
|
.map(formatTMDBEpisode),
|
||||||
id: v.id.toString(),
|
|
||||||
number: v.episode_number,
|
|
||||||
title: v.title,
|
|
||||||
air_date: v.air_date,
|
|
||||||
still_path: v.still_path,
|
|
||||||
overview: v.overview,
|
|
||||||
})),
|
|
||||||
}
|
}
|
||||||
: (undefined as any),
|
: (undefined as any),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
|
||||||
import { useAsync } from "react-use";
|
import { useAsync } from "react-use";
|
||||||
|
|
||||||
import { getMetaFromId } from "@/backend/metadata/getmeta";
|
import { getMetaFromId } from "@/backend/metadata/getmeta";
|
||||||
|
import { formatTMDBEpisode, getEpisodes } from "@/backend/metadata/tmdb";
|
||||||
import { MWMediaType, MWSeasonMeta } from "@/backend/metadata/types/mw";
|
import { MWMediaType, MWSeasonMeta } from "@/backend/metadata/types/mw";
|
||||||
import { Icon, Icons } from "@/components/Icon";
|
import { Icon, Icons } from "@/components/Icon";
|
||||||
import { ProgressRing } from "@/components/layout/ProgressRing";
|
import { ProgressRing } from "@/components/layout/ProgressRing";
|
||||||
|
|
@ -20,6 +21,7 @@ import { PlayerMeta } from "@/stores/player/slices/source";
|
||||||
import { usePlayerStore } from "@/stores/player/store";
|
import { usePlayerStore } from "@/stores/player/store";
|
||||||
import { usePreferencesStore } from "@/stores/preferences";
|
import { usePreferencesStore } from "@/stores/preferences";
|
||||||
import { useProgressStore } from "@/stores/progress";
|
import { useProgressStore } from "@/stores/progress";
|
||||||
|
import { concurrentMap } from "@/utils/async";
|
||||||
import { scrollToElement } from "@/utils/scroll";
|
import { scrollToElement } from "@/utils/scroll";
|
||||||
|
|
||||||
import { hasAired } from "../utils/aired";
|
import { hasAired } from "../utils/aired";
|
||||||
|
|
@ -594,23 +596,21 @@ export function EpisodesView({
|
||||||
if (selectedSeason === "favorites" && meta?.tmdbId && seasons) {
|
if (selectedSeason === "favorites" && meta?.tmdbId && seasons) {
|
||||||
setAllSeasonsLoading(true);
|
setAllSeasonsLoading(true);
|
||||||
const loadAllSeasons = async () => {
|
const loadAllSeasons = async () => {
|
||||||
const seasonPromises = seasons.map(async (season) => {
|
const results = await concurrentMap(seasons, 5, async (season) => {
|
||||||
try {
|
try {
|
||||||
const data = await getMetaFromId(
|
const episodes = await getEpisodes(meta.tmdbId!, season.number);
|
||||||
MWMediaType.SERIES,
|
return {
|
||||||
meta.tmdbId,
|
id: season.id,
|
||||||
season.id,
|
number: season.number,
|
||||||
);
|
title: season.title,
|
||||||
return data?.meta.type === MWMediaType.SERIES
|
episodes: episodes.map(formatTMDBEpisode),
|
||||||
? data.meta.seasonData
|
};
|
||||||
: null;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to load season ${season.id}:`, error);
|
console.error(`Failed to load season ${season.id}:`, error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const results = await Promise.all(seasonPromises);
|
|
||||||
setAllSeasonsData(results.filter(Boolean));
|
setAllSeasonsData(results.filter(Boolean));
|
||||||
setAllSeasonsLoading(false);
|
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