2023-10-08 18:16:30 +02:00
|
|
|
import { ScrapeMedia } from "@movie-web/providers";
|
|
|
|
|
2023-07-23 16:30:22 +02:00
|
|
|
import { MakeSlice } from "@/stores/player/slices/types";
|
2023-10-14 16:06:25 +02:00
|
|
|
import {
|
|
|
|
SourceQuality,
|
|
|
|
SourceSliceSource,
|
|
|
|
selectQuality,
|
|
|
|
} from "@/stores/player/utils/qualities";
|
2023-10-18 20:31:03 +02:00
|
|
|
import { useQualityStore } from "@/stores/quality";
|
2023-07-23 16:30:22 +02:00
|
|
|
import { ValuesOf } from "@/utils/typeguard";
|
|
|
|
|
|
|
|
export const playerStatus = {
|
|
|
|
IDLE: "idle",
|
|
|
|
SCRAPING: "scraping",
|
|
|
|
PLAYING: "playing",
|
2023-10-23 23:06:24 +02:00
|
|
|
SCRAPE_NOT_FOUND: "scrapeNotFound",
|
2023-07-23 16:30:22 +02:00
|
|
|
} as const;
|
|
|
|
|
|
|
|
export type PlayerStatus = ValuesOf<typeof playerStatus>;
|
|
|
|
|
2023-10-20 17:29:10 +02:00
|
|
|
export interface PlayerMetaEpisode {
|
|
|
|
number: number;
|
|
|
|
tmdbId: string;
|
|
|
|
title: string;
|
|
|
|
}
|
|
|
|
|
2023-10-08 18:16:30 +02:00
|
|
|
export interface PlayerMeta {
|
|
|
|
type: "movie" | "show";
|
|
|
|
title: string;
|
|
|
|
tmdbId: string;
|
|
|
|
imdbId?: string;
|
|
|
|
releaseYear: number;
|
2023-10-17 16:01:26 +02:00
|
|
|
poster?: string;
|
2023-10-20 17:29:10 +02:00
|
|
|
episodes?: PlayerMetaEpisode[];
|
|
|
|
episode?: PlayerMetaEpisode;
|
2023-10-08 18:16:30 +02:00
|
|
|
season?: {
|
|
|
|
number: number;
|
|
|
|
tmdbId: string;
|
|
|
|
title: string;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-10-18 14:30:52 +02:00
|
|
|
export interface Caption {
|
|
|
|
language: string;
|
|
|
|
url?: string;
|
|
|
|
srtData: string;
|
|
|
|
}
|
|
|
|
|
2023-07-23 16:30:22 +02:00
|
|
|
export interface SourceSlice {
|
|
|
|
status: PlayerStatus;
|
2023-08-15 19:55:48 +02:00
|
|
|
source: SourceSliceSource | null;
|
2023-10-18 16:08:33 +02:00
|
|
|
sourceId: string | null;
|
2023-10-14 16:06:25 +02:00
|
|
|
qualities: SourceQuality[];
|
|
|
|
currentQuality: SourceQuality | null;
|
2023-10-18 14:30:52 +02:00
|
|
|
caption: {
|
|
|
|
selected: Caption | null;
|
|
|
|
asTrack: boolean;
|
|
|
|
};
|
2023-10-08 18:16:30 +02:00
|
|
|
meta: PlayerMeta | null;
|
2023-07-23 16:30:22 +02:00
|
|
|
setStatus(status: PlayerStatus): void;
|
2023-10-15 20:25:14 +02:00
|
|
|
setSource(stream: SourceSliceSource, startAt: number): void;
|
2023-10-14 16:06:25 +02:00
|
|
|
switchQuality(quality: SourceQuality): void;
|
2023-10-08 18:16:30 +02:00
|
|
|
setMeta(meta: PlayerMeta): void;
|
2023-10-18 14:30:52 +02:00
|
|
|
setCaption(caption: Caption | null): void;
|
2023-10-18 16:08:33 +02:00
|
|
|
setSourceId(id: string | null): void;
|
2023-10-19 19:27:21 +02:00
|
|
|
enableAutomaticQuality(): void;
|
2023-10-20 23:24:37 +02:00
|
|
|
redisplaySource(startAt: number): void;
|
2023-10-08 18:16:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function metaToScrapeMedia(meta: PlayerMeta): ScrapeMedia {
|
|
|
|
if (meta.type === "show") {
|
|
|
|
if (!meta.episode || !meta.season) throw new Error("missing show data");
|
|
|
|
return {
|
|
|
|
title: meta.title,
|
|
|
|
releaseYear: meta.releaseYear,
|
|
|
|
tmdbId: meta.tmdbId,
|
|
|
|
type: "show",
|
|
|
|
imdbId: meta.imdbId,
|
|
|
|
episode: meta.episode,
|
|
|
|
season: meta.season,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
title: meta.title,
|
|
|
|
releaseYear: meta.releaseYear,
|
|
|
|
tmdbId: meta.tmdbId,
|
|
|
|
type: "movie",
|
|
|
|
imdbId: meta.imdbId,
|
|
|
|
};
|
2023-07-23 16:30:22 +02:00
|
|
|
}
|
|
|
|
|
2023-10-14 16:06:25 +02:00
|
|
|
export const createSourceSlice: MakeSlice<SourceSlice> = (set, get) => ({
|
2023-07-23 16:30:22 +02:00
|
|
|
source: null,
|
2023-10-18 16:08:33 +02:00
|
|
|
sourceId: null,
|
2023-10-14 16:06:25 +02:00
|
|
|
qualities: [],
|
|
|
|
currentQuality: null,
|
2023-07-23 16:30:22 +02:00
|
|
|
status: playerStatus.IDLE,
|
2023-10-08 18:16:30 +02:00
|
|
|
meta: null,
|
2023-10-18 14:30:52 +02:00
|
|
|
caption: {
|
|
|
|
selected: null,
|
|
|
|
asTrack: false,
|
|
|
|
},
|
2023-10-18 16:08:33 +02:00
|
|
|
setSourceId(id) {
|
|
|
|
set((s) => {
|
|
|
|
s.sourceId = id;
|
|
|
|
});
|
|
|
|
},
|
2023-07-23 16:30:22 +02:00
|
|
|
setStatus(status: PlayerStatus) {
|
|
|
|
set((s) => {
|
|
|
|
s.status = status;
|
|
|
|
});
|
|
|
|
},
|
2023-10-08 18:16:30 +02:00
|
|
|
setMeta(meta) {
|
|
|
|
set((s) => {
|
|
|
|
s.meta = meta;
|
2023-10-20 15:54:10 +02:00
|
|
|
s.interface.hideNextEpisodeBtn = false;
|
2023-10-08 18:16:30 +02:00
|
|
|
});
|
|
|
|
},
|
2023-10-18 14:30:52 +02:00
|
|
|
setCaption(caption) {
|
|
|
|
set((s) => {
|
|
|
|
s.caption.selected = caption;
|
|
|
|
});
|
|
|
|
},
|
2023-10-15 20:25:14 +02:00
|
|
|
setSource(stream: SourceSliceSource, startAt: number) {
|
2023-10-14 16:06:25 +02:00
|
|
|
let qualities: string[] = [];
|
|
|
|
if (stream.type === "file") qualities = Object.keys(stream.qualities);
|
2023-10-18 20:31:03 +02:00
|
|
|
const qualityPreferences = useQualityStore.getState();
|
|
|
|
const loadableStream = selectQuality(stream, qualityPreferences.quality);
|
2023-10-14 16:06:25 +02:00
|
|
|
|
2023-07-23 16:30:22 +02:00
|
|
|
set((s) => {
|
2023-10-14 16:06:25 +02:00
|
|
|
s.source = stream;
|
|
|
|
s.qualities = qualities as SourceQuality[];
|
|
|
|
s.currentQuality = loadableStream.quality;
|
2023-07-23 16:30:22 +02:00
|
|
|
});
|
2023-10-20 23:24:37 +02:00
|
|
|
const store = get();
|
|
|
|
store.redisplaySource(startAt);
|
|
|
|
},
|
|
|
|
redisplaySource(startAt: number) {
|
|
|
|
const store = get();
|
|
|
|
const quality = store.currentQuality;
|
|
|
|
if (!store.source) return;
|
|
|
|
const qualityPreferences = useQualityStore.getState();
|
|
|
|
const loadableStream = selectQuality(store.source, {
|
|
|
|
automaticQuality: qualityPreferences.quality.automaticQuality,
|
|
|
|
lastChosenQuality: quality,
|
|
|
|
});
|
2023-10-14 16:06:25 +02:00
|
|
|
|
2023-10-19 19:27:21 +02:00
|
|
|
store.display?.load({
|
|
|
|
source: loadableStream.stream,
|
|
|
|
startAt,
|
|
|
|
automaticQuality: qualityPreferences.quality.automaticQuality,
|
|
|
|
preferredQuality: qualityPreferences.quality.lastChosenQuality,
|
|
|
|
});
|
2023-10-14 16:06:25 +02:00
|
|
|
},
|
|
|
|
switchQuality(quality) {
|
|
|
|
const store = get();
|
|
|
|
if (!store.source) return;
|
|
|
|
if (store.source.type === "file") {
|
|
|
|
const selectedQuality = store.source.qualities[quality];
|
|
|
|
if (!selectedQuality) return;
|
|
|
|
set((s) => {
|
|
|
|
s.currentQuality = quality;
|
|
|
|
});
|
2023-10-19 19:27:21 +02:00
|
|
|
store.display?.load({
|
|
|
|
source: selectedQuality,
|
|
|
|
startAt: store.progress.time,
|
|
|
|
automaticQuality: false,
|
|
|
|
preferredQuality: quality,
|
|
|
|
});
|
|
|
|
} else if (store.source.type === "hls") {
|
|
|
|
store.display?.changeQuality(false, quality);
|
2023-10-14 16:06:25 +02:00
|
|
|
}
|
2023-07-23 16:30:22 +02:00
|
|
|
},
|
2023-10-19 19:27:21 +02:00
|
|
|
enableAutomaticQuality() {
|
|
|
|
const store = get();
|
|
|
|
store.display?.changeQuality(true, null);
|
|
|
|
},
|
2023-07-23 16:30:22 +02:00
|
|
|
});
|