return source selection

This commit is contained in:
Kentai Radiquum 2025-04-09 14:48:16 +05:00
parent 0168daa6cc
commit 0a5b8a59e6
Signed by: Radiquum
GPG key ID: 858E8EE696525EED
5 changed files with 128 additions and 224 deletions

View file

@ -42,6 +42,7 @@ import {
MediaSettingsMenuItem,
} from "media-chrome/react/menu";
import { VoiceoverSelectorMenu } from "./VoiceoverSelectorMenu";
import { SourceSelectorMenu } from "./SourceSelectorMenu";
export const ReleasePlayerCustom = (props: {
id: number;
@ -70,45 +71,6 @@ export const ReleasePlayerCustom = (props: {
// const [isErrorDetailsOpen, setIsErrorDetailsOpen] = useState(false);
// const [isLoading, setIsLoading] = useState(true);
// const playerPreferenceStore = useUserPlayerPreferencesStore();
// const preferredVO = playerPreferenceStore.getPreferredVoiceover(props.id);
// const preferredSource = playerPreferenceStore.getPreferredPlayer(props.id);
// old info fetching
// useEffect(() => {
// const __getInfo = async () => {
// let url = `${ENDPOINTS.release.episode}/${props.id}/${voiceover.selected.id}`;
// const src = await _fetchAPI(
// url,
// "Не удалось получить информацию о источниках"
// );
// if (src) {
// const selectedSrc =
// src.sources.find((source: any) => source.name === preferredSource) ||
// src.sources[0];
// if (selectedSrc.episodes_count == 0) {
// const remSources = src.sources.filter(
// (source: any) => source.id !== selectedSrc.id
// );
// setSource({
// selected: remSources[0],
// available: remSources,
// });
// return;
// }
// setSource({
// selected: selectedSrc,
// available: src.sources,
// });
// }
// };
// if (voiceover.selected) {
// __getInfo();
// }
// // eslint-disable-next-line react-hooks/exhaustive-deps
// }, [voiceover.selected]);
// useEffect(() => {
// const __getInfo = async () => {
// let url = `${ENDPOINTS.release.episode}/${props.id}/${voiceover.selected.id}/${source.selected.id}`;
@ -364,6 +326,14 @@ export const ReleasePlayerCustom = (props: {
setVoiceover={setVoiceover}
setPlayerError={setPlayerError}
/>
<SourceSelectorMenu
release_id={props.id}
voiceover={voiceover.selected}
source={source.selected}
sourceList={source.available}
setSource={setSource}
setPlayerError={setPlayerError}
/>
</div>
</MediaChromeDialog>
<MediaControlBar className={`${Styles["media-control-bar"]}`}>

View file

@ -1,76 +0,0 @@
"use client";
import { Dropdown, DropdownItem } from "flowbite-react";
import { numberDeclension } from "#/api/utils";
import { useUserPlayerPreferencesStore } from "#/store/player";
interface Source {
id: number;
name: string;
episodes_count: number;
}
const DropdownTrigger = ({ name }: Source) => {
return (
<div className="flex items-center gap-2 px-2 py-1 bg-black bg-opacity-75 rounded-lg cursor-pointer backdrop-blur-md">
<span className="w-6 h-6 text-white iconify material-symbols--motion-play"></span>
<p className="text-white">{name}</p>
<span className="w-6 h-6 -ml-2 text-white iconify material-symbols--arrow-drop-down"></span>
</div>
);
};
const DropdownItemInternal = ({ name, episodes_count }: Source) => {
return (
<div className="flex flex-col gap-2 cursor-pointer">
<div className="flex items-center gap-2">
<p>{name}</p>
</div>
<div className="flex items-center gap-2">
<p>
{episodes_count}{" "}
{numberDeclension(episodes_count, "серия", "серии", "серий")}
</p>
</div>
</div>
);
};
export const SourceSelector = (props: {
availableSource: Source[];
source: Source;
setSource: any;
release_id: any;
}) => {
const playerPreferenceStore = useUserPlayerPreferencesStore();
return (
<Dropdown
label=""
dismissOnClick={true}
renderTrigger={() => (
<span>
<DropdownTrigger {...props.source} />
</span>
)}
>
{props.availableSource.map((source: Source) => (
<DropdownItem
key={`source_${source.id}`}
onClick={() => {
playerPreferenceStore.setPreferredPlayer(
props.release_id,
source.name
);
props.setSource({
selected: source,
available: props.availableSource,
});
}}
>
<DropdownItemInternal {...source} />
</DropdownItem>
))}
</Dropdown>
);
};

View file

@ -0,0 +1,115 @@
"use client";
import { ENDPOINTS } from "#/api/config";
import { useEffect } from "react";
import { _fetchAPI } from "./PlayerParsing";
import { useUserPlayerPreferencesStore } from "#/store/player";
import { numberDeclension } from "#/api/utils";
import { Voiceover } from "./VoiceoverSelectorMenu";
export interface Source {
id: number;
name: string;
episodes_count: number;
}
interface SourceSelectorMenuProps {
release_id: number;
setSource: (state) => void;
voiceover: Voiceover;
source: Source;
sourceList: Source[];
setPlayerError: (state) => void;
}
export const SourceSelectorMenu = ({
release_id,
setSource,
voiceover,
source,
sourceList,
setPlayerError,
}: SourceSelectorMenuProps) => {
const playerPreferenceStore = useUserPlayerPreferencesStore();
const preferredSource = playerPreferenceStore.getPreferredPlayer(release_id);
useEffect(() => {
const __getInfo = async () => {
let url = `${ENDPOINTS.release.episode}/${release_id}/${voiceover.id}`;
const src = await _fetchAPI(
url,
"Не удалось получить информацию о источниках",
setPlayerError
);
if (src) {
const selectedSrc =
src.sources.find(
(source: Source) => source.name === preferredSource
) || src.sources[0];
if (selectedSrc.episodes_count == 0) {
const remSources = src.sources.filter(
(source: any) => source.id !== selectedSrc.id
);
setSource({
selected: remSources[0],
available: remSources,
});
return;
}
setSource({
selected: selectedSrc,
available: src.sources,
});
}
};
if (voiceover) {
__getInfo();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [voiceover]);
return (
<div className="flex flex-col items-start justify-start gap-4">
<p className="text-[20px] px-2 pt-2 pb-1 font-bold">Источник</p>
<div className="max-h-full flex flex-col gap-4 items-start justify-start overflow-x-hidden overflow-y-auto px-2 pb-2 scrollbar-thin scrollbar-thumb-[rgb(60_60_60_/_.8)] scrollbar-track-[rgb(30_30_30_/_.8)]">
{sourceList && sourceList.length > 0 ?
sourceList.map((src: Source) => {
return (
<button
key={`release-${release_id}-voiceover-${voiceover.id}-source-${src.id}`}
className={`h-fit justify-start items-start ${source.id == src.id ? "text-white" : "text-gray-500 hover:text-gray-300"} transition-colors`}
onClick={() => {
setSource({
selected: src,
available: sourceList,
});
playerPreferenceStore.setPreferredPlayer(
release_id,
src.name
);
}}
>
<div className="flex flex-col w-full gap-1">
<div className="flex items-center gap-2">
<span className="text-[16px] leading-none">{src.name}</span>
</div>
<div className="flex gap-2">
<span>
{src.episodes_count || 0}{" "}
{numberDeclension(
src.episodes_count || 0,
"серия",
"серии",
"серий"
)}
</span>
</div>
</div>
</button>
);
})
: ""}
</div>
</div>
);
};

View file

@ -1,104 +0,0 @@
"use client";
import { Dropdown, DropdownItem } from "flowbite-react";
import { numberDeclension } from "#/api/utils";
import { useUserPlayerPreferencesStore } from "#/store/player";
interface Voiceover {
id: number;
name: string;
icon: string;
episodes_count: number;
view_count: number;
pinned: boolean;
}
const DropdownTrigger = ({ icon, name, pinned }: Voiceover) => {
return (
<div className="flex items-center gap-2 px-2 py-1 bg-black bg-opacity-75 rounded-lg cursor-pointer backdrop-blur-md">
{/* eslint-disable-next-line @next/next/no-img-element */}
{icon && <img alt="" className="w-6 h-6 rounded-full" src={icon}></img>}
<p className="text-white">{name}</p>
{pinned && (
<span className="h-6 bg-gray-300 iconify material-symbols--push-pin"></span>
)}
<span className="w-6 h-6 -ml-2 text-white iconify material-symbols--arrow-drop-down"></span>
</div>
);
};
const DropdownItemInternal = ({
icon,
name,
pinned,
episodes_count,
view_count,
}: Voiceover) => {
return (
<div className="flex flex-col gap-2 cursor-pointer">
<div className="flex items-center gap-2">
{/* eslint-disable-next-line @next/next/no-img-element */}
{icon && <img alt="" className="w-6 h-6 rounded-full" src={icon}></img>}
<p>{name}</p>
{pinned && (
<span className="h-6 iconify material-symbols--push-pin"></span>
)}
</div>
<div className="flex items-center gap-2">
<p>
{episodes_count}{" "}
{numberDeclension(episodes_count, "серия", "серии", "серий")}
</p>
<p>
{view_count}{" "}
{numberDeclension(view_count, "просмотр", "просмотра", "просмотров")}
</p>
</div>
</div>
);
};
const DropdownTheme = {
content: "md:grid md:grid-cols-2 xl:grid-cols-4 gap-2 w-full container",
};
export const VoiceoverSelector = (props: {
availableVoiceover: Voiceover[];
voiceover: Voiceover;
setVoiceover: any;
release_id: number;
}) => {
const playerPreferenceStore = useUserPlayerPreferencesStore();
return (
<Dropdown
theme={DropdownTheme}
label=""
dismissOnClick={true}
renderTrigger={() => (
<span>
<DropdownTrigger {...props.voiceover} />
</span>
)}
>
{props.availableVoiceover.map((voiceover: Voiceover) => (
<DropdownItem
className="w-fit"
key={`voiceover_${voiceover.id}`}
onClick={() => {
playerPreferenceStore.setPreferredVoiceover(
props.release_id,
voiceover.name
);
props.setVoiceover({
selected: voiceover,
available: props.availableVoiceover,
});
}}
>
<DropdownItemInternal {...voiceover} />
</DropdownItem>
))}
</Dropdown>
);
};

View file

@ -4,10 +4,9 @@ import { ENDPOINTS } from "#/api/config";
import { useEffect } from "react";
import { _fetchAPI } from "./PlayerParsing";
import { useUserPlayerPreferencesStore } from "#/store/player";
import { Button } from "flowbite-react";
import { numberDeclension } from "#/api/utils";
interface Voiceover {
export interface Voiceover {
id: number;
name: string;
icon: string;
@ -50,7 +49,7 @@ export const VoiceoverSelectorMenu = ({
);
if (vo) {
const selectedVO =
vo.types.find((voiceover: any) => voiceover.name === preferredVO) ||
vo.types.find((voiceover: Voiceover) => voiceover.name === preferredVO) ||
vo.types[0];
setVoiceover({
selected: selectedVO,
@ -64,8 +63,8 @@ export const VoiceoverSelectorMenu = ({
return (
<div className="flex flex-col items-start justify-start gap-4">
<p className="text-[22px] px-2 py-2 font-bold">Озвучка</p>
<div className="max-h-full flex flex-col gap-4 items-start justify-start overflow-x-hidden overflow-y-auto px-2 scrollbar-thin scrollbar-thumb-[rgb(60_60_60_/_.8)] scrollbar-track-[rgb(30_30_30_/_.8)]">
<p className="text-[20px] px-2 pt-2 pb-1 font-bold">Озвучка</p>
<div className="max-h-full flex flex-col gap-4 items-start justify-start overflow-x-hidden overflow-y-auto px-2 pb-2 scrollbar-thin scrollbar-thumb-[rgb(60_60_60_/_.8)] scrollbar-track-[rgb(30_30_30_/_.8)]">
{voiceoverList && voiceoverList.length > 0 ?
voiceoverList.map((vo: Voiceover) => {
return (