Merge pull request #3 from Radiquum/feat__Saving-of-the-preferred-voiceover-and-player

Feat: Merge PR #2 saving of the preferred voiceover and player to the V3 branch
This commit is contained in:
Kentai Radiquum 2024-12-05 20:13:04 +05:00 committed by GitHub
commit d46075c88c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 175 additions and 73 deletions

View file

@ -2,6 +2,7 @@
import { Spinner } from "#/components/Spinner/Spinner"; import { Spinner } from "#/components/Spinner/Spinner";
import { useUserStore } from "#/store/auth"; import { useUserStore } from "#/store/auth";
import { useUserPlayerPreferencesStore } from "#/store/player";
import { Card, Dropdown, Button } from "flowbite-react"; import { Card, Dropdown, Button } from "flowbite-react";
import { ENDPOINTS } from "#/api/config"; import { ENDPOINTS } from "#/api/config";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
@ -38,27 +39,14 @@ const getAnonEpisodesWatched = (
) => { ) => {
const anonEpisodesWatched = const anonEpisodesWatched =
JSON.parse(localStorage.getItem("anonEpisodesWatched")) || {}; JSON.parse(localStorage.getItem("anonEpisodesWatched")) || {};
console.log("anonEpisodesWatched", anonEpisodesWatched);
if (!anonEpisodesWatched.hasOwnProperty(Release)) { if (!anonEpisodesWatched.hasOwnProperty(Release)) {
console.log(
`no key found for R: ${Release}`,
anonEpisodesWatched.hasOwnProperty(Release)
);
anonEpisodesWatched[Release] = {}; anonEpisodesWatched[Release] = {};
} }
if (!anonEpisodesWatched[Release].hasOwnProperty(Source)) { if (!anonEpisodesWatched[Release].hasOwnProperty(Source)) {
console.log(
`no key found for R: ${Release} S: ${Source}`,
anonEpisodesWatched.hasOwnProperty(Release)
);
anonEpisodesWatched[Release][Source] = {}; anonEpisodesWatched[Release][Source] = {};
} }
if (!anonEpisodesWatched[Release][Source].hasOwnProperty(Voiceover)) { if (!anonEpisodesWatched[Release][Source].hasOwnProperty(Voiceover)) {
console.log(
`no key found for R: ${Release} S: ${Source} V: ${Voiceover}`,
anonEpisodesWatched.hasOwnProperty(Release)
);
anonEpisodesWatched[Release][Source][Voiceover] = {}; anonEpisodesWatched[Release][Source][Voiceover] = {};
} }
@ -75,33 +63,17 @@ const getAnonCurrentEpisodeWatched = (
JSON.parse(localStorage.getItem("anonEpisodesWatched")) || {}; JSON.parse(localStorage.getItem("anonEpisodesWatched")) || {};
if (!anonEpisodesWatched.hasOwnProperty(Release)) { if (!anonEpisodesWatched.hasOwnProperty(Release)) {
console.log(
`no key found for R: ${Release}`,
anonEpisodesWatched.hasOwnProperty(Release)
);
return false; return false;
} }
if (!anonEpisodesWatched[Release].hasOwnProperty(Source)) { if (!anonEpisodesWatched[Release].hasOwnProperty(Source)) {
console.log(
`no key found for R: ${Release} S: ${Source}`,
anonEpisodesWatched.hasOwnProperty(Release)
);
return false; return false;
} }
if (!anonEpisodesWatched[Release][Source].hasOwnProperty(Voiceover)) { if (!anonEpisodesWatched[Release][Source].hasOwnProperty(Voiceover)) {
console.log(
`no key found for R: ${Release} S: ${Source} V: ${Voiceover}`,
anonEpisodesWatched.hasOwnProperty(Release)
);
return false; return false;
} }
if ( if (
!anonEpisodesWatched[Release][Source][Voiceover].hasOwnProperty(Episode) !anonEpisodesWatched[Release][Source][Voiceover].hasOwnProperty(Episode)
) { ) {
console.log(
`no key found for R: ${Release} S: ${Source} V: ${Voiceover} E: ${Episode}`,
anonEpisodesWatched.hasOwnProperty(Release)
);
return false; return false;
} }
@ -139,65 +111,148 @@ const saveAnonEpisodeWatched = (
export const ReleasePlayer = (props: { id: number }) => { export const ReleasePlayer = (props: { id: number }) => {
const userStore = useUserStore(); const userStore = useUserStore();
const preferredVoiceoverStore = useUserPlayerPreferencesStore();
const storedPreferredVoiceover =
preferredVoiceoverStore.getPreferredVoiceover(props.id);
const storedPreferredPlayer = preferredVoiceoverStore.getPreferredPlayer(
props.id
);
const [voiceoverInfo, setVoiceoverInfo] = useState(null); const [voiceoverInfo, setVoiceoverInfo] = useState(null);
const [selectedVoiceover, setSelectedVoiceover] = useState(null); const [selectedVoiceover, setSelectedVoiceover] = useState(null);
const [sourcesInfo, setSourcesInfo] = useState(null); const [sourcesInfo, setSourcesInfo] = useState(null);
const [selectedSource, setSelectedSource] = useState(null); const [selectedSource, setSelectedSource] = useState(null);
const [episodeInfo, setEpisodeInfo] = useState(null); const [episodeInfo, setEpisodeInfo] = useState(null);
const [selectedEpisode, setSelectedEpisode] = useState(null); const [selectedEpisode, setSelectedEpisode] = useState(null);
const [error, setError] = useState(null);
const setSelectedVoiceoverAndSaveAsPreferred = (voiceover: any) => {
setSelectedVoiceover(voiceover);
preferredVoiceoverStore.setPreferredVoiceover(props.id, voiceover.name);
};
const setSelectedPlayerAndSaveAsPreferred = (player: any) => {
setSelectedSource(player);
preferredVoiceoverStore.setPreferredPlayer(props.id, player.name);
};
useEffect(() => { function _setError(error: string) {
async function _fetchInfo() { setVoiceoverInfo(null);
const voiceover = await _fetch( setSelectedVoiceover(null);
`${ENDPOINTS.release.episode}/${props.id}` setSourcesInfo(null);
); setSelectedSource(null);
setVoiceoverInfo(voiceover.types); setEpisodeInfo(null);
setSelectedVoiceover(voiceover.types[0]); setSelectedEpisode(null);
setError(error);
}
async function _fetchInfo(
url: string,
type: "voiceover" | "sources" | "episodes"
) {
let data: any = {};
data = await fetch(url)
.then((res) => {
if (res.ok) {
return res.json();
} else {
throw new Error("Error fetching data");
}
})
.catch((err) => {
console.log(err);
_setError("Ошибка получение ответа от сервера");
return;
});
if (data && Object.keys(data).length == 0) {
_setError("Ошибка получение данных с сервера");
} }
_fetchInfo();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => { if (type == "voiceover") {
async function _fetchInfo() { if (data.types.length > 0) {
const sources = await _fetch( setVoiceoverInfo(data.types);
`${ENDPOINTS.release.episode}/${props.id}/${selectedVoiceover.id}` const preferredVoiceover =
); data.types.find(
setSourcesInfo(sources.sources); (voiceover: any) => voiceover.name === storedPreferredVoiceover
setSelectedSource(sources.sources[0]); ) || data.types[0];
} setSelectedVoiceover(preferredVoiceover);
if (selectedVoiceover) { } else {
_fetchInfo(); _setError("Ошибка получения озвучек");
} }
// eslint-disable-next-line react-hooks/exhaustive-deps } else if (type == "sources") {
}, [selectedVoiceover]); if (data.sources.length > 0) {
setSourcesInfo(data.sources);
useEffect(() => { const preferredSource =
async function _fetchInfo(url: string) { data.sources.find(
const episodes = await _fetch(url); (source: any) => source.name === storedPreferredPlayer
) || data.sources[0];
if (episodes.episodes.length === 0) { setSelectedSource(preferredSource);
} else {
_setError("Ошибка получения источников");
}
} else if (type == "episodes") {
if (data.episodes.length === 0) {
const remSources = sourcesInfo.filter( const remSources = sourcesInfo.filter(
(source) => source.id !== selectedSource.id (source) => source.id !== selectedSource.id
); );
setSourcesInfo(remSources); setSourcesInfo(remSources);
setSelectedSource(remSources[0]); setSelectedSource(remSources[0]);
return; return;
} } else if (data.episodes.length > 0) {
setEpisodeInfo(data.episodes);
setSelectedEpisode(data.episodes[0]);
setEpisodeInfo(episodes.episodes); const WatchedEpisodes = getAnonEpisodesWatched(
setSelectedEpisode(episodes.episodes[0]); props.id,
selectedSource.id,
selectedVoiceover.id
);
if (
Object.keys(
WatchedEpisodes[props.id][selectedSource.id][selectedVoiceover.id]
).length != 0
) {
const watchedEpisodes =
WatchedEpisodes[props.id][selectedSource.id][selectedVoiceover.id];
let lastWatchedEpisode = Number(Object.keys(watchedEpisodes).pop());
if (
!["Sibnet", "Sibnet (не работает)"].includes(selectedSource.name)
) {
lastWatchedEpisode = Number(lastWatchedEpisode) - 1;
}
setSelectedEpisode(data.episodes[lastWatchedEpisode]);
}
} else {
_setError("Ошибка получения эпизодов");
}
} else {
_setError("Неизвестный тип запроса");
} }
}
useEffect(() => {
_fetchInfo(`${ENDPOINTS.release.episode}/${props.id}`, "voiceover");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.id]);
useEffect(() => {
if (selectedVoiceover) {
_fetchInfo(
`${ENDPOINTS.release.episode}/${props.id}/${selectedVoiceover.id}`,
"sources"
);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.id, selectedVoiceover]);
useEffect(() => {
if (selectedSource) { if (selectedSource) {
let url = `${ENDPOINTS.release.episode}/${props.id}/${selectedVoiceover.id}/${selectedSource.id}`; let url = `${ENDPOINTS.release.episode}/${props.id}/${selectedVoiceover.id}/${selectedSource.id}`;
if (userStore.token) { if (userStore.token) {
url = `${ENDPOINTS.release.episode}/${props.id}/${selectedVoiceover.id}/${selectedSource.id}?token=${userStore.token}`; url = `${ENDPOINTS.release.episode}/${props.id}/${selectedVoiceover.id}/${selectedSource.id}?token=${userStore.token}`;
} }
_fetchInfo(url); _fetchInfo(url, "episodes");
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedSource, userStore.token]); }, [props.id, selectedSource, userStore.token]);
async function _addToHistory(episode: any) { async function _addToHistory(episode: any) {
if (episode && userStore.token) { if (episode && userStore.token) {
@ -214,7 +269,7 @@ export const ReleasePlayer = (props: { id: number }) => {
<Card> <Card>
{!voiceoverInfo || !sourcesInfo || !episodeInfo ? ( {!voiceoverInfo || !sourcesInfo || !episodeInfo ? (
<div className="flex items-center justify-center w-full aspect-video"> <div className="flex items-center justify-center w-full aspect-video">
<Spinner /> {!error ? <Spinner /> : <p>{error}</p>}
</div> </div>
) : ( ) : (
<> <>
@ -227,7 +282,9 @@ export const ReleasePlayer = (props: { id: number }) => {
{voiceoverInfo.map((voiceover: any) => ( {voiceoverInfo.map((voiceover: any) => (
<Dropdown.Item <Dropdown.Item
key={`voiceover_${voiceover.id}`} key={`voiceover_${voiceover.id}`}
onClick={() => setSelectedVoiceover(voiceover)} onClick={() =>
setSelectedVoiceoverAndSaveAsPreferred(voiceover)
}
> >
{voiceover.name} {voiceover.name}
</Dropdown.Item> </Dropdown.Item>
@ -241,7 +298,7 @@ export const ReleasePlayer = (props: { id: number }) => {
{sourcesInfo.map((source: any) => ( {sourcesInfo.map((source: any) => (
<Dropdown.Item <Dropdown.Item
key={`source_${source.id}`} key={`source_${source.id}`}
onClick={() => setSelectedSource(source)} onClick={() => setSelectedPlayerAndSaveAsPreferred(source)}
> >
{source.name} {source.name}
</Dropdown.Item> </Dropdown.Item>
@ -249,11 +306,15 @@ export const ReleasePlayer = (props: { id: number }) => {
</Dropdown> </Dropdown>
</div> </div>
<div className="aspect-video"> <div className="aspect-video">
<iframe {selectedEpisode ? (
allowFullScreen={true} <iframe
src={selectedEpisode.url} allowFullScreen={true}
className="w-full h-full rounded-md" src={selectedEpisode.url}
></iframe> className="w-full h-full rounded-md"
></iframe>
) : (
<p>Ошибка загрузки плеера</p>
)}
</div> </div>
<div> <div>
<Swiper <Swiper
@ -301,7 +362,9 @@ export const ReleasePlayer = (props: { id: number }) => {
{episode.name {episode.name
? episode.name ? episode.name
: `${ : `${
selectedSource.name != "Sibnet" !["Sibnet", "Sibnet (не работает)"].includes(
selectedSource.name
)
? episode.position ? episode.position
: episode.position + 1 : episode.position + 1
} серия`} } серия`}

39
app/store/player.ts Normal file
View file

@ -0,0 +1,39 @@
"use client";
import { create } from "zustand";
import { persist } from "zustand/middleware";
interface userPlayerPreferencesState {
voiceover: Record<number, string>;
player: Record<number, string>;
getPreferredVoiceover: (id: number) => string | undefined;
setPreferredVoiceover: (id: number, voiceover: string) => void;
getPreferredPlayer: (id: number) => string | undefined;
setPreferredPlayer: (id: number, player: string) => void;
}
export const useUserPlayerPreferencesStore =
create<userPlayerPreferencesState>()(
persist(
(set, get) => ({
voiceover: {},
player: {},
getPreferredVoiceover: (id: number) => get().voiceover[id],
setPreferredVoiceover: (id: number, voiceover: string) => {
set({
voiceover: { ...get().voiceover, [id]: voiceover },
player: get().player,
});
},
getPreferredPlayer: (id: number) => get().player[id],
setPreferredPlayer: (id: number, player: string) => {
set({
player: { ...get().player, [id]: player },
voiceover: get().voiceover,
});
},
}),
{
name: "player-preferences",
}
)
);