mirror of
https://github.com/Radiquum/AniX.git
synced 2025-04-06 16:24:40 +00:00
feat(frontend): add a release page
add a video player to a release page without release info with ability to save and view wathed episodes
This commit is contained in:
parent
bbf8fc5436
commit
75dd8d83bd
4 changed files with 187 additions and 27 deletions
|
@ -1,7 +1,9 @@
|
||||||
|
import requests
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
from modules.proxy import apiRequest
|
from modules.proxy import apiRequest
|
||||||
from modules.proxy import ENDPOINTS
|
from modules.proxy import ENDPOINTS
|
||||||
|
from modules.proxy import USER_AGENT
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
@ -33,40 +35,39 @@ async def GetReleaseVoiceoverPlayer(
|
||||||
summary="Get available episodes for selected voiceover and a player of a release",
|
summary="Get available episodes for selected voiceover and a player of a release",
|
||||||
)
|
)
|
||||||
async def GetReleaseEpisodes(
|
async def GetReleaseEpisodes(
|
||||||
request: Request, release_id: str, voiceover_id: str, source_id: str
|
request: Request,
|
||||||
|
release_id: str,
|
||||||
|
voiceover_id: str,
|
||||||
|
source_id: str,
|
||||||
|
token: str = "",
|
||||||
):
|
):
|
||||||
return await apiRequest(
|
return await apiRequest(
|
||||||
request,
|
request,
|
||||||
ENDPOINTS["release"]["episode"],
|
ENDPOINTS["release"]["episode"],
|
||||||
f"{release_id}/{voiceover_id}/{source_id}",
|
f"{release_id}/{voiceover_id}/{source_id}",
|
||||||
|
query=f"?token={token}",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/{release_id}/{episode}/markWatched",
|
"/{release_id}/{source_id}/{episode}/saveToHistory",
|
||||||
summary="mark episode of a selected voiceover as watched",
|
summary="mark episode of a selected voiceover as watched and save it to watch history",
|
||||||
)
|
)
|
||||||
async def MarkEpisodeAsWatched(
|
async def MarkEpisodeAsWatched(
|
||||||
request: Request, release_id: str, source_id: str, episode: str, token: str
|
request: Request, release_id: str, source_id: str, episode: str, token: str
|
||||||
):
|
):
|
||||||
return await apiRequest(
|
headers = {
|
||||||
request,
|
"User-Agent": USER_AGENT,
|
||||||
ENDPOINTS["statistic"]["markWatched"],
|
"Content-Type": "application/json; charset=UTF-8",
|
||||||
f"${release_id}/${source_id}/${episode}",
|
}
|
||||||
query=f"?token={token}",
|
|
||||||
|
requests.get(
|
||||||
|
f"{ENDPOINTS['statistic']['markWatched']}/{release_id}/{source_id}/{episode}?token={token}",
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
requests.get(
|
||||||
|
f"{ENDPOINTS['statistic']['addHistory']}/{release_id}/{source_id}/{episode}?token={token}",
|
||||||
|
headers=headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return {"success"}
|
||||||
@router.get(
|
|
||||||
"/{release_id}/{episode}/addHistory",
|
|
||||||
summary="add episode of a selected voiceover to history",
|
|
||||||
)
|
|
||||||
async def AddEpisodeToHistory(
|
|
||||||
request: Request, release_id: str, source_id: str, episode: str, token: str
|
|
||||||
):
|
|
||||||
return await apiRequest(
|
|
||||||
request,
|
|
||||||
ENDPOINTS["statistic"]["addHistory"],
|
|
||||||
f"${release_id}/${source_id}/${episode}",
|
|
||||||
query=f"?token={token}",
|
|
||||||
)
|
|
||||||
|
|
|
@ -20,4 +20,5 @@ export const endpoints = {
|
||||||
abandoned: `${API_URL}/favorites/abandoned`,
|
abandoned: `${API_URL}/favorites/abandoned`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
release: `${API_URL}/release`,
|
||||||
};
|
};
|
||||||
|
|
162
frontend/app/release/[id]/page.js
Normal file
162
frontend/app/release/[id]/page.js
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { getData } from "@/app/api/api-utils";
|
||||||
|
import { endpoints } from "@/app/api/config";
|
||||||
|
import { useUserStore } from "@/app/store/user-store";
|
||||||
|
import { useSettingsStore } from "@/app/store/settings-store";
|
||||||
|
|
||||||
|
export default function Release(props) {
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
const [releaseInfo, setReleaseInfo] = useState();
|
||||||
|
const [voiceoverInfo, setVoiceoverInfo] = useState();
|
||||||
|
const [selectedVoiceover, setSelectedVoiceover] = useState();
|
||||||
|
const [sourcesInfo, setSourcesInfo] = useState();
|
||||||
|
const [selectedSources, setSelectedSources] = useState();
|
||||||
|
const [episodeInfo, setEpisodeInfo] = useState();
|
||||||
|
const [selectedEpisode, setSelectedEpisode] = useState();
|
||||||
|
const [episodeURL, setEpisodeURL] = useState();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function _fetchInfo() {
|
||||||
|
const release = await getData(`${endpoints.release}/${props.params.id}`);
|
||||||
|
const voiceover = await getData(
|
||||||
|
`${endpoints.release}/${props.params.id}/voiceover`,
|
||||||
|
);
|
||||||
|
setReleaseInfo(release);
|
||||||
|
setVoiceoverInfo(voiceover);
|
||||||
|
setSelectedVoiceover(voiceover.types[0].id);
|
||||||
|
}
|
||||||
|
if (props.params.id) {
|
||||||
|
_fetchInfo();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function _fetchInfo() {
|
||||||
|
const sources = await getData(
|
||||||
|
`${endpoints.release}/${props.params.id}/${selectedVoiceover}`,
|
||||||
|
);
|
||||||
|
setSourcesInfo(sources);
|
||||||
|
setSelectedSources(sources.sources[0].id);
|
||||||
|
}
|
||||||
|
if (selectedVoiceover) {
|
||||||
|
_fetchInfo();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [selectedVoiceover]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function _fetchInfo() {
|
||||||
|
let url = `${endpoints.release}/${props.params.id}/${selectedVoiceover}/${selectedSources}`;
|
||||||
|
if (userStore.token) {
|
||||||
|
url = `${endpoints.release}/${props.params.id}/${selectedVoiceover}/${selectedSources}?token=${userStore.token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const episodes = await getData(url);
|
||||||
|
|
||||||
|
setEpisodeInfo(episodes);
|
||||||
|
setSelectedEpisode(episodes.episodes[0].position);
|
||||||
|
setEpisodeURL(episodes.episodes[0].url);
|
||||||
|
}
|
||||||
|
if (selectedSources) {
|
||||||
|
_fetchInfo();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [selectedSources, userStore.token]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function _markAsWatched() {
|
||||||
|
const url = `${endpoints.release}/${props.params.id}/${selectedSources}/${selectedEpisode}`;
|
||||||
|
await getData(`${url}/saveToHistory?token=${userStore.token}`);
|
||||||
|
}
|
||||||
|
if (userStore.token && settingsStore.saveToHistory) {
|
||||||
|
_markAsWatched();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [selectedEpisode]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<article className="fill grid">
|
||||||
|
<iframe
|
||||||
|
src={episodeURL}
|
||||||
|
className="s9"
|
||||||
|
style={{ aspectRatio: "16/9", width: "100%", height: "auto" }}
|
||||||
|
/>
|
||||||
|
<div className="s3">
|
||||||
|
<div className="tabs">
|
||||||
|
<a data-ui="#vo" className="active">
|
||||||
|
озвучка
|
||||||
|
</a>
|
||||||
|
{sourcesInfo && <a data-ui="#src">плеер</a>}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="page padding active scroll"
|
||||||
|
style={{ Height: "438px" }}
|
||||||
|
id="vo"
|
||||||
|
>
|
||||||
|
{voiceoverInfo &&
|
||||||
|
voiceoverInfo.types.map((item) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={item.id}
|
||||||
|
className={`small responsive ${
|
||||||
|
item.id == selectedVoiceover ? "primary" : "secondary"
|
||||||
|
}`}
|
||||||
|
style={{ marginTop: "8px" }}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedVoiceover(item.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{sourcesInfo && (
|
||||||
|
<div className="page center-align padding" id="src">
|
||||||
|
{sourcesInfo.sources.map((item) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={item.id}
|
||||||
|
className={`small responsive ${
|
||||||
|
item.id == selectedSources ? "primary" : "secondary"
|
||||||
|
}`}
|
||||||
|
style={{ marginTop: "8px" }}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedSources(item.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="s12 scroll row" style={{ paddingBottom: "8px" }}>
|
||||||
|
{episodeInfo &&
|
||||||
|
episodeInfo.episodes.map((item) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={item.position}
|
||||||
|
className={`${
|
||||||
|
item.position == selectedEpisode ? "primary" : "secondary"
|
||||||
|
}`}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedEpisode(item.position);
|
||||||
|
setEpisodeURL(item.url);
|
||||||
|
item.is_watched = true;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.is_watched && <i className="small">check</i>}
|
||||||
|
{item.name}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
}
|
|
@ -23,11 +23,7 @@ export const useUserStore = create((set, get) => ({
|
||||||
`${endpoints.user.profile}/${jwt.user_id}`,
|
`${endpoints.user.profile}/${jwt.user_id}`,
|
||||||
jwt.jwt,
|
jwt.jwt,
|
||||||
);
|
);
|
||||||
if (me.is_my_profile) {
|
get().login(me, jwt.jwt, jwt.user_id);
|
||||||
get().login(me, jwt.jwt, jwt.user_id);
|
|
||||||
} else {
|
|
||||||
get().logout();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
get().logout();
|
get().logout();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue