mirror of
https://git.vectorsigma.ru/public/tubearchivist.git
synced 2026-08-05 01:19:29 +00:00
398 lines
15 KiB
TypeScript
398 lines
15 KiB
TypeScript
import { useNavigate, useOutletContext, useParams } from 'react-router-dom';
|
|
import ChannelOverview from '../components/ChannelOverview';
|
|
import { useEffect, useState } from 'react';
|
|
import loadChannelById, { ChannelResponseType } from '../api/loader/loadChannelById';
|
|
import Linkify from '../components/Linkify';
|
|
import deleteChannel from '../api/actions/deleteChannel';
|
|
import Routes from '../configuration/routes/RouteList';
|
|
import queueReindex, { ReindexType, ReindexTypeEnum } from '../api/actions/queueReindex';
|
|
import formatDate from '../functions/formatDates';
|
|
import PaginationDummy from '../components/PaginationDummy';
|
|
import FormattedNumber from '../components/FormattedNumber';
|
|
import Button from '../components/Button';
|
|
import updateChannelOverwrites from '../api/actions/updateChannelOverwrite';
|
|
import useIsAdmin from '../functions/useIsAdmin';
|
|
import InputConfig from '../components/InputConfig';
|
|
import ToggleConfig from '../components/ToggleConfig';
|
|
import { useUserConfigStore } from '../stores/UserConfigStore';
|
|
import { ApiResponseType } from '../functions/APIClient';
|
|
|
|
export type ChannelBaseOutletContextType = {
|
|
currentPage: number;
|
|
setCurrentPage: (page: number) => void;
|
|
startNotification: boolean;
|
|
setStartNotification: (status: boolean) => void;
|
|
};
|
|
|
|
export type OutletContextType = {
|
|
currentPage: number;
|
|
setCurrentPage: (page: number) => void;
|
|
};
|
|
|
|
type ChannelAboutParams = {
|
|
channelId: string;
|
|
};
|
|
|
|
const ChannelAbout = () => {
|
|
const { channelId } = useParams() as ChannelAboutParams;
|
|
const { userConfig } = useUserConfigStore();
|
|
const { setStartNotification } = useOutletContext() as ChannelBaseOutletContextType;
|
|
const navigate = useNavigate();
|
|
const isAdmin = useIsAdmin();
|
|
|
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
|
const [descriptionExpanded, setDescriptionExpanded] = useState(false);
|
|
const [reindex, setReindex] = useState(false);
|
|
const [refresh, setRefresh] = useState(true);
|
|
|
|
const [channelResponse, setChannelResponse] = useState<ApiResponseType<ChannelResponseType>>();
|
|
|
|
const [downloadFormat, setDownloadFormat] = useState<string | null>(null);
|
|
const [autoDeleteAfter, setAutoDeleteAfter] = useState<number | null>(null);
|
|
const [indexPlaylists, setIndexPlaylists] = useState(false);
|
|
const [enableSponsorblock, setEnableSponsorblock] = useState<boolean | null>(null);
|
|
const [pageSizeVideo, setPageSizeVideo] = useState<number | null>(null);
|
|
const [pageSizeShorts, setPageSizeShorts] = useState<number | null>(null);
|
|
const [pageSizeStreams, setPageSizeStreams] = useState<number | null>(null);
|
|
|
|
const { data: channelResponseData } = channelResponse ?? {};
|
|
|
|
const channel = channelResponseData;
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
if (refresh) {
|
|
const channelResponse = await loadChannelById(channelId);
|
|
const { data: channelResponseData } = channelResponse;
|
|
|
|
setChannelResponse(channelResponse);
|
|
setDownloadFormat(channelResponseData?.channel_overwrites?.download_format ?? null);
|
|
setAutoDeleteAfter(channelResponseData?.channel_overwrites?.autodelete_days ?? null);
|
|
setIndexPlaylists(channelResponseData?.channel_overwrites?.index_playlists ?? false);
|
|
setEnableSponsorblock(
|
|
channelResponseData?.channel_overwrites?.integrate_sponsorblock ?? null,
|
|
);
|
|
setPageSizeVideo(
|
|
channelResponseData?.channel_overwrites?.subscriptions_channel_size ?? null,
|
|
);
|
|
setPageSizeShorts(
|
|
channelResponseData?.channel_overwrites?.subscriptions_shorts_channel_size ?? null,
|
|
);
|
|
setPageSizeStreams(
|
|
channelResponseData?.channel_overwrites?.subscriptions_live_channel_size ?? null,
|
|
);
|
|
|
|
setRefresh(false);
|
|
}
|
|
})();
|
|
}, [refresh, channelId]);
|
|
|
|
const handleUpdateConfig = async (
|
|
configKey: string,
|
|
configValue: string | boolean | number | null,
|
|
) => {
|
|
if (!channel) return;
|
|
await updateChannelOverwrites(channel.channel_id, configKey, configValue);
|
|
setRefresh(true);
|
|
};
|
|
|
|
const handleToggleSponsorBlock = async (isEnabled: boolean) => {
|
|
if (isEnabled) {
|
|
setEnableSponsorblock(true);
|
|
await handleUpdateConfig('integrate_sponsorblock', false);
|
|
} else {
|
|
await handleUpdateConfig('integrate_sponsorblock', null);
|
|
setEnableSponsorblock(null);
|
|
}
|
|
};
|
|
|
|
if (!channel) {
|
|
return 'Channel not found!';
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<title>{`TA | Channel: About ${channel.channel_name}`}</title>
|
|
<div className="boxed-content">
|
|
<div className="info-box info-box-3">
|
|
<ChannelOverview
|
|
channelId={channel.channel_id}
|
|
channelname={channel.channel_name}
|
|
channelSubs={channel.channel_subs}
|
|
channelSubscribed={channel.channel_subscribed}
|
|
channelThumbUrl={channel.channel_thumb_url}
|
|
setRefresh={setRefresh}
|
|
/>
|
|
|
|
<div className="info-box-item">
|
|
<div>
|
|
<p>Last refreshed: {formatDate(channel.channel_last_refresh)}</p>
|
|
{channel.channel_active && (
|
|
<p>
|
|
Youtube:{' '}
|
|
<a href={`https://www.youtube.com/channel/${channel.channel_id}`} target="_blank">
|
|
Active
|
|
</a>
|
|
</p>
|
|
)}
|
|
{!channel.channel_active && <p>Youtube: Deactivated</p>}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="info-box-item">
|
|
<div>
|
|
{channel.channel_views > 0 && (
|
|
<FormattedNumber text="Channel views:" number={channel.channel_views} />
|
|
)}
|
|
|
|
{isAdmin && (
|
|
<>
|
|
<div className="button-box">
|
|
{!showDeleteConfirm && (
|
|
<Button
|
|
label="Delete Channel"
|
|
id="delete-item"
|
|
onClick={() => setShowDeleteConfirm(!showDeleteConfirm)}
|
|
/>
|
|
)}
|
|
|
|
{showDeleteConfirm && (
|
|
<div className="delete-confirm">
|
|
<span>Delete {channel.channel_name} including all videos? </span>
|
|
|
|
<Button
|
|
label="Delete"
|
|
className="danger-button"
|
|
onClick={async () => {
|
|
await deleteChannel(channelId);
|
|
navigate(Routes.Channels);
|
|
}}
|
|
/>
|
|
|
|
<Button
|
|
label="Cancel"
|
|
onClick={() => setShowDeleteConfirm(!showDeleteConfirm)}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<br></br>
|
|
{reindex && <p>Reindex scheduled</p>}
|
|
{!reindex && (
|
|
<div id="reindex-button" className="button-box">
|
|
<Button
|
|
label="Reindex"
|
|
title={`Reindex Channel ${channel.channel_name}`}
|
|
onClick={async () => {
|
|
await queueReindex(channelId, ReindexTypeEnum.channel as ReindexType);
|
|
|
|
setReindex(true);
|
|
setStartNotification(true);
|
|
}}
|
|
/>{' '}
|
|
<Button
|
|
label="Reindex Videos"
|
|
title={`Reindex Videos of ${channel.channel_name}`}
|
|
onClick={async () => {
|
|
await queueReindex(
|
|
channelId,
|
|
ReindexTypeEnum.channel as ReindexType,
|
|
true,
|
|
);
|
|
|
|
setReindex(true);
|
|
setStartNotification(true);
|
|
}}
|
|
/>
|
|
</div>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{channel.channel_description && (
|
|
<div className="description-box">
|
|
<p
|
|
id={descriptionExpanded ? 'text-expand-expanded' : 'text-expand'}
|
|
className="description-text"
|
|
>
|
|
<Linkify>{channel.channel_description}</Linkify>
|
|
</p>
|
|
|
|
<Button
|
|
label="Show more"
|
|
id="text-expand-button"
|
|
onClick={() => setDescriptionExpanded(!descriptionExpanded)}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{channel?.channel_tags && channel.channel_tags.length > 0 && (
|
|
<div className="description-box">
|
|
<div className="video-tag-box">
|
|
{channel.channel_tags.map(tag => {
|
|
return (
|
|
<span key={tag} className="video-tag">
|
|
{tag}
|
|
</span>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{isAdmin && (
|
|
<div className="info-box">
|
|
<div className="info-box-item">
|
|
<h2>Channel Customization</h2>
|
|
{userConfig.show_help_text && (
|
|
<div className="help-text">
|
|
<ul>
|
|
<li>Overwrite the download format over the format set globally.</li>
|
|
<li>Autodelete watched after x days overwrites the global setting.</li>
|
|
<li>
|
|
Indexing playlists indexes all playlists from that channel.
|
|
<ul>
|
|
<li>
|
|
Only activate that for channels you care about the playlists. This is
|
|
slow.
|
|
</li>
|
|
<li>
|
|
More details{' '}
|
|
<a target="_blank" href="https://docs.tubearchivist.com/channels/#about">
|
|
here
|
|
</a>
|
|
.
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
Once you click on <i>Configure</i>, this will activate sponsorblock settings.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
)}
|
|
<div className="settings-box-wrapper">
|
|
<div>
|
|
<p>Download Format</p>
|
|
</div>
|
|
<InputConfig
|
|
type="text"
|
|
name="download_format"
|
|
value={downloadFormat}
|
|
setValue={setDownloadFormat}
|
|
oldValue={channel.channel_overwrites?.download_format ?? null}
|
|
updateCallback={handleUpdateConfig}
|
|
/>
|
|
</div>
|
|
<div className="settings-box-wrapper">
|
|
<div>
|
|
<p>
|
|
<span className="danger-zone">Danger Zone</span>: Auto delete watched
|
|
</p>
|
|
</div>
|
|
<InputConfig
|
|
type="number"
|
|
name="autodelete_days"
|
|
value={autoDeleteAfter}
|
|
setValue={setAutoDeleteAfter}
|
|
oldValue={channel.channel_overwrites?.autodelete_days ?? null}
|
|
updateCallback={handleUpdateConfig}
|
|
/>
|
|
</div>
|
|
<div className="settings-box-wrapper">
|
|
<div>
|
|
<p>Index playlists</p>
|
|
</div>
|
|
<ToggleConfig
|
|
name="index_playlists"
|
|
value={indexPlaylists}
|
|
updateCallback={handleUpdateConfig}
|
|
/>
|
|
</div>
|
|
<div className="settings-box-wrapper">
|
|
<div>
|
|
<p>
|
|
Overwrite{' '}
|
|
<a href="https://sponsor.ajay.app/" target="_blank">
|
|
SponsorBlock
|
|
</a>
|
|
</p>
|
|
</div>
|
|
<div>
|
|
{enableSponsorblock !== null ? (
|
|
<ToggleConfig
|
|
name="integrate_sponsorblock"
|
|
value={enableSponsorblock}
|
|
updateCallback={handleUpdateConfig}
|
|
resetCallback={handleToggleSponsorBlock}
|
|
/>
|
|
) : (
|
|
<button onClick={() => handleToggleSponsorBlock(true)}>Configure</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="info-box-item">
|
|
<h2>Page Size Overrides</h2>
|
|
{userConfig.show_help_text && (
|
|
<div className="help-text">
|
|
<p>
|
|
Disable standard videos, shorts, or streams for this channel by setting their
|
|
page size to 0 (zero).
|
|
</p>
|
|
</div>
|
|
)}
|
|
<div className="settings-box-wrapper">
|
|
<div>
|
|
<p>Videos page size</p>
|
|
</div>
|
|
<InputConfig
|
|
type="number"
|
|
name="subscriptions_channel_size"
|
|
value={pageSizeVideo}
|
|
setValue={setPageSizeVideo}
|
|
oldValue={channel.channel_overwrites?.subscriptions_channel_size ?? null}
|
|
updateCallback={handleUpdateConfig}
|
|
/>
|
|
</div>
|
|
<div className="settings-box-wrapper">
|
|
<div>
|
|
<p>Shorts page size</p>
|
|
</div>
|
|
<InputConfig
|
|
type="number"
|
|
name="subscriptions_shorts_channel_size"
|
|
value={pageSizeShorts}
|
|
setValue={setPageSizeShorts}
|
|
oldValue={channel.channel_overwrites?.subscriptions_shorts_channel_size ?? null}
|
|
updateCallback={handleUpdateConfig}
|
|
/>
|
|
</div>
|
|
<div className="settings-box-wrapper">
|
|
<div>
|
|
<p>Live streams page size</p>
|
|
</div>
|
|
<InputConfig
|
|
type="number"
|
|
name="subscriptions_live_channel_size"
|
|
value={pageSizeStreams}
|
|
setValue={setPageSizeStreams}
|
|
oldValue={channel.channel_overwrites?.subscriptions_live_channel_size ?? null}
|
|
updateCallback={handleUpdateConfig}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<PaginationDummy />
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default ChannelAbout;
|