233 lines
6.5 KiB
TypeScript
233 lines
6.5 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { Link, useOutletContext, useSearchParams } from 'react-router-dom';
|
|
import Routes from '../configuration/routes/RouteList';
|
|
import Pagination from '../components/Pagination';
|
|
import loadVideoListByFilter, {
|
|
VideoListByFilterResponseType,
|
|
} from '../api/loader/loadVideoListByPage';
|
|
import VideoList from '../components/VideoList';
|
|
import { ChannelType } from './Channels';
|
|
import { OutletContextType } from './Base';
|
|
import Filterbar from '../components/Filterbar';
|
|
import { ViewStyleNames, ViewStyles } from '../configuration/constants/ViewStyle';
|
|
import ScrollToTopOnNavigate from '../components/ScrollToTop';
|
|
import EmbeddableVideoPlayer from '../components/EmbeddableVideoPlayer';
|
|
import { SponsorBlockType } from './Video';
|
|
import { useUserConfigStore } from '../stores/UserConfigStore';
|
|
|
|
export type PlayerType = {
|
|
watched: boolean;
|
|
duration: number;
|
|
duration_str: string;
|
|
progress: number;
|
|
position: number;
|
|
};
|
|
|
|
export type StatsType = {
|
|
view_count: number;
|
|
like_count: number;
|
|
dislike_count: number;
|
|
average_rating: number;
|
|
};
|
|
|
|
export type StreamType = {
|
|
type: string;
|
|
index: number;
|
|
codec: string;
|
|
width?: number;
|
|
height?: number;
|
|
bitrate: number;
|
|
};
|
|
|
|
export type Subtitles = {
|
|
ext: string;
|
|
url: string;
|
|
name: string;
|
|
lang: string;
|
|
source: string;
|
|
media_url: string;
|
|
};
|
|
|
|
export type VideoType = {
|
|
active: boolean;
|
|
category: string[];
|
|
channel: ChannelType;
|
|
date_downloaded: number;
|
|
description: string;
|
|
comment_count?: number;
|
|
media_size: number;
|
|
media_url: string;
|
|
player: PlayerType;
|
|
published: string;
|
|
sponsorblock?: SponsorBlockType;
|
|
playlist?: string[];
|
|
stats: StatsType;
|
|
streams: StreamType[];
|
|
subtitles: Subtitles[];
|
|
tags: string[];
|
|
title: string;
|
|
vid_last_refresh: string;
|
|
vid_thumb_base64: boolean;
|
|
vid_thumb_url: string;
|
|
vid_type: string;
|
|
youtube_id: string;
|
|
};
|
|
|
|
export type DownloadsType = {
|
|
limit_speed: boolean;
|
|
sleep_interval: number;
|
|
autodelete_days: boolean;
|
|
format: boolean;
|
|
format_sort: boolean;
|
|
add_metadata: boolean;
|
|
add_thumbnail: boolean;
|
|
subtitle: boolean;
|
|
subtitle_source: boolean;
|
|
subtitle_index: boolean;
|
|
comment_max: boolean;
|
|
comment_sort: string;
|
|
cookie_import: boolean;
|
|
throttledratelimit: boolean;
|
|
extractor_lang: boolean;
|
|
integrate_ryd: boolean;
|
|
integrate_sponsorblock: boolean;
|
|
};
|
|
|
|
export type ConfigType = {
|
|
enable_cast: boolean;
|
|
downloads: DownloadsType;
|
|
};
|
|
|
|
export type SortByType = 'published' | 'downloaded' | 'views' | 'likes' | 'duration' | 'mediasize';
|
|
export type SortOrderType = 'asc' | 'desc';
|
|
export type ViewLayoutType = 'grid' | 'list';
|
|
|
|
const Home = () => {
|
|
const { userConfig } = useUserConfigStore();
|
|
const { currentPage, setCurrentPage } = useOutletContext() as OutletContextType;
|
|
const [searchParams] = useSearchParams();
|
|
const videoId = searchParams.get('videoId');
|
|
|
|
const userMeConfig = userConfig.config;
|
|
|
|
const [refreshVideoList, setRefreshVideoList] = useState(false);
|
|
|
|
const [videoResponse, setVideoReponse] = useState<VideoListByFilterResponseType>();
|
|
const [continueVideoResponse, setContinueVideoResponse] =
|
|
useState<VideoListByFilterResponseType>();
|
|
|
|
const videoList = videoResponse?.data;
|
|
const pagination = videoResponse?.paginate;
|
|
const continueVideos = continueVideoResponse?.data;
|
|
|
|
const hasVideos = videoResponse?.data?.length !== 0;
|
|
const showEmbeddedVideo = videoId !== null;
|
|
|
|
const isGridView = userMeConfig.view_style_home === ViewStyles.grid;
|
|
const gridView = isGridView ? `boxed-${userMeConfig.grid_items}` : '';
|
|
const gridViewGrid = isGridView ? `grid-${userMeConfig.grid_items}` : '';
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
if (
|
|
refreshVideoList ||
|
|
pagination?.current_page === undefined ||
|
|
currentPage !== pagination?.current_page
|
|
) {
|
|
const videos = await loadVideoListByFilter({
|
|
page: currentPage,
|
|
watch: userMeConfig.hide_watched ? 'unwatched' : undefined,
|
|
sort: userMeConfig.sort_by,
|
|
order: userMeConfig.sort_order,
|
|
});
|
|
|
|
try {
|
|
const continueVideoResponse = await loadVideoListByFilter({ watch: 'continue' });
|
|
setContinueVideoResponse(continueVideoResponse);
|
|
} catch (error) {
|
|
console.log('Server error on continue vids?');
|
|
}
|
|
|
|
setVideoReponse(videos);
|
|
|
|
setRefreshVideoList(false);
|
|
}
|
|
})();
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [
|
|
refreshVideoList,
|
|
userMeConfig.sort_by,
|
|
userMeConfig.sort_order,
|
|
userMeConfig.hide_watched,
|
|
currentPage,
|
|
pagination?.current_page,
|
|
]);
|
|
|
|
return (
|
|
<>
|
|
<title>TubeArchivist</title>
|
|
<ScrollToTopOnNavigate />
|
|
|
|
{showEmbeddedVideo && <EmbeddableVideoPlayer videoId={videoId} />}
|
|
|
|
<div className={`boxed-content ${gridView}`}>
|
|
{continueVideos && continueVideos.length > 0 && (
|
|
<>
|
|
<div className="title-bar">
|
|
<h1>Continue Watching</h1>
|
|
</div>
|
|
<div className={`video-list ${userMeConfig.view_style_home} ${gridViewGrid}`}>
|
|
<VideoList
|
|
videoList={continueVideos}
|
|
viewLayout={userMeConfig.view_style_home}
|
|
refreshVideoList={setRefreshVideoList}
|
|
/>
|
|
</div>
|
|
</>
|
|
)}
|
|
|
|
<div className="title-bar">
|
|
<h1>Recent Videos</h1>
|
|
</div>
|
|
|
|
<Filterbar
|
|
hideToggleText="Hide watched:"
|
|
viewStyleName={ViewStyleNames.home}
|
|
setRefresh={setRefreshVideoList}
|
|
/>
|
|
</div>
|
|
|
|
<div className={`boxed-content ${gridView}`}>
|
|
<div className={`video-list ${userMeConfig.view_style_home} ${gridViewGrid}`}>
|
|
{!hasVideos && (
|
|
<>
|
|
<h2>No videos found...</h2>
|
|
<p>
|
|
If you've already added a channel or playlist, try going to the{' '}
|
|
<Link to={Routes.Downloads}>downloads page</Link> to start the scan and download
|
|
tasks.
|
|
</p>
|
|
</>
|
|
)}
|
|
|
|
{hasVideos && (
|
|
<VideoList
|
|
videoList={videoList}
|
|
viewLayout={userMeConfig.view_style_home}
|
|
refreshVideoList={setRefreshVideoList}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{pagination && (
|
|
<div className="boxed-content">
|
|
<Pagination pagination={pagination} setPage={setCurrentPage} />
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default Home;
|