mirror of
https://git.vectorsigma.ru/public/tubearchivist.git
synced 2026-08-08 12:19:23 +00:00
DRAFT: Add Tubearchivist Frontend React dev docker setup (#768)
* Add development docker-compose file * Add /new/ path in nginx conf * Add frontend production setup * Fix lint * Refac move prod docker compose into non suffixed file * Fix run.sh fileendings on windows * Fix docker file naming consistancies * Add frontend dev setup * Add docker compose dev command * Refac remove docker network * Fix potential error causes * Chore update react-router-dom * Add redirect to login after logout * Refac allow basic auth for session login in api * Fix loginresponsetype optional property * Refac move isAdmin check into page Base * Refac use node lts for dev container * Refac remove old setup in readme * Refac move getisAdmin into loader and rename * Refac remove manual csrf cookie handing from actions and loader * Fix post requiring csrf header & cookie * Fix remove empty files * Refac revert dockerfile changes * Refac revert gitatrributes changes * Refac revert docker changes * Refac revert nginx changes * Refac revert docker change * Refac move frontend into frontend folder * Add production steps to dockerfile * Refac implement endpoint renaming * Refac remove frontend dockerfile * Add credentials include for dev env * Fix allow cors with credentials for dev environment * Fix images in dev mode * Add credentials for dev mode to all loader and actions, except signin * Revert cors config * Revert cors config * Fix nginx not serving /youtube/ * Fix video url missing api * Fix media url missing api * Add application settings page * Add continue vids * Add csrf to delete requests * Refac use api/video endpoint with filter to home, channel, playlist pages * Add channel nav request * Add channel playlists * Fix filterbar for playlist in channel * Add playlist_nav to video page * Add downloads aggs * Refac remove basic auth * Fix credentials include in signin * Refac user config to user me config * Add ApiToken get
This commit is contained in:
178
frontend/src/components/EmbeddableVideoPlayer.tsx
Normal file
178
frontend/src/components/EmbeddableVideoPlayer.tsx
Normal file
@@ -0,0 +1,178 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { SponsorBlockType, VideoResponseType } from '../pages/Video';
|
||||
import VideoPlayer, { VideoProgressType } from './VideoPlayer';
|
||||
import loadVideoById from '../api/loader/loadVideoById';
|
||||
import loadVideoProgressById from '../api/loader/loadVideoProgressById';
|
||||
import loadSponsorblockByVideoId from '../api/loader/loadSponsorblockByVideoId';
|
||||
import iconClose from '/img/icon-close.svg';
|
||||
import iconEye from '/img/icon-eye.svg';
|
||||
import iconThumb from '/img/icon-thumb.svg';
|
||||
import WatchedCheckBox from './WatchedCheckBox';
|
||||
import GoogleCast from './GoogleCast';
|
||||
import updateWatchedState from '../api/actions/updateWatchedState';
|
||||
import formatNumbers from '../functions/formatNumbers';
|
||||
import { Link, useSearchParams } from 'react-router-dom';
|
||||
import Routes from '../configuration/routes/RouteList';
|
||||
import loadPlaylistById from '../api/loader/loadPlaylistById';
|
||||
|
||||
type Playlist = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
type PlaylistList = Playlist[];
|
||||
|
||||
type EmbeddableVideoPlayerProps = {
|
||||
videoId: string;
|
||||
};
|
||||
|
||||
const EmbeddableVideoPlayer = ({ videoId }: EmbeddableVideoPlayerProps) => {
|
||||
const [, setSearchParams] = useSearchParams();
|
||||
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
|
||||
const [videoResponse, setVideoResponse] = useState<VideoResponseType>();
|
||||
const [videoProgress, setVideoProgress] = useState<VideoProgressType>();
|
||||
const [playlists, setPlaylists] = useState<PlaylistList>();
|
||||
const [sponsorblockResponse, setSponsorblockResponse] = useState<SponsorBlockType>();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const videoResponse = await loadVideoById(videoId);
|
||||
const videoProgress = await loadVideoProgressById(videoId);
|
||||
const sponsorblockReponse = await loadSponsorblockByVideoId(videoId);
|
||||
|
||||
const playlistIds = videoResponse.data.playlist;
|
||||
if (playlistIds !== undefined) {
|
||||
const playlists = await Promise.all(
|
||||
playlistIds.map(async (playlistid: string) => {
|
||||
const playlistResponse = await loadPlaylistById(playlistid);
|
||||
|
||||
return playlistResponse.data;
|
||||
}),
|
||||
);
|
||||
|
||||
const playlistsFiltered = playlists
|
||||
.filter(playlist => {
|
||||
return playlist.playlist_subscribed;
|
||||
})
|
||||
.map(playlist => {
|
||||
return {
|
||||
id: playlist.playlist_id,
|
||||
name: playlist.playlist_name,
|
||||
};
|
||||
});
|
||||
|
||||
setPlaylists(playlistsFiltered);
|
||||
}
|
||||
|
||||
setVideoResponse(videoResponse);
|
||||
setVideoProgress(videoProgress);
|
||||
setSponsorblockResponse(sponsorblockReponse);
|
||||
|
||||
setRefresh(false);
|
||||
})();
|
||||
}, [videoId, refresh]);
|
||||
|
||||
if (videoResponse === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const video = videoResponse.data;
|
||||
const name = video.title;
|
||||
const channelId = video.channel.channel_id;
|
||||
const channelName = video.channel.channel_name;
|
||||
const watched = video.player.watched;
|
||||
const views = formatNumbers(video.stats.view_count);
|
||||
const hasLikes = video.stats.like_count;
|
||||
const likes = formatNumbers(video.stats.like_count);
|
||||
const hasDislikes = video.stats.dislike_count > 0 && videoResponse.config.downloads.integrate_ryd;
|
||||
const dislikes = formatNumbers(video.stats.dislike_count);
|
||||
const config = videoResponse.config;
|
||||
const cast = config.enable_cast;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="player-wrapper">
|
||||
<div className="video-player">
|
||||
<VideoPlayer
|
||||
video={videoResponse}
|
||||
videoProgress={videoProgress}
|
||||
sponsorBlock={sponsorblockResponse}
|
||||
embed={true}
|
||||
/>
|
||||
|
||||
<div className="player-title boxed-content">
|
||||
<img
|
||||
className="close-button"
|
||||
src={iconClose}
|
||||
alt="close-icon"
|
||||
title="Close player"
|
||||
onClick={() => {
|
||||
setSearchParams({});
|
||||
}}
|
||||
/>
|
||||
<WatchedCheckBox
|
||||
watched={watched}
|
||||
onClick={async status => {
|
||||
await updateWatchedState({
|
||||
id: videoId,
|
||||
is_watched: status,
|
||||
});
|
||||
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
{cast && (
|
||||
<GoogleCast
|
||||
video={video}
|
||||
videoProgress={videoProgress}
|
||||
setRefresh={() => {
|
||||
setRefresh(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="thumb-icon player-stats">
|
||||
<img src={iconEye} alt="views icon" />
|
||||
<span>{views}</span>
|
||||
{hasLikes && (
|
||||
<>
|
||||
<span>|</span>
|
||||
<img src={iconThumb} alt="thumbs-up" />
|
||||
<span>{likes}</span>
|
||||
</>
|
||||
)}
|
||||
{hasDislikes && (
|
||||
<>
|
||||
<span>|</span>
|
||||
<img className="dislike" src={iconThumb} alt="thumbs-down" />
|
||||
<span>{dislikes}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="player-channel-playlist">
|
||||
<h3>
|
||||
<Link to={Routes.Channel(channelId)}>{channelName}</Link>
|
||||
</h3>
|
||||
|
||||
{playlists?.map(({ id, name }) => {
|
||||
return (
|
||||
<h5 key={id}>
|
||||
<Link to={Routes.Playlist(id)}>{name}</Link>
|
||||
</h5>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<Link to={Routes.Video(videoId)}>
|
||||
<h2 id="video-title">{name}</h2>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmbeddableVideoPlayer;
|
||||
Reference in New Issue
Block a user