import { useEffect, useState } from 'react'; import loadSnapshots, { SnapshotListType } from '../api/loader/loadSnapshots'; import Notifications from '../components/Notifications'; import PaginationDummy from '../components/PaginationDummy'; import SettingsNavigation from '../components/SettingsNavigation'; import restoreSnapshot from '../api/actions/restoreSnapshot'; import queueSnapshot from '../api/actions/queueSnapshot'; // import updateCookie, { ValidatedCookieType } from '../api/actions/updateCookie'; import deleteApiToken from '../api/actions/deleteApiToken'; import Button from '../components/Button'; import loadAppsettingsConfig, { AppSettingsConfigType } from '../api/loader/loadAppsettingsConfig'; import updateAppsettingsConfig from '../api/actions/updateAppsettingsConfig'; import loadApiToken from '../api/loader/loadApiToken'; import InputConfig from '../components/InputConfig'; import ToggleConfig from '../components/ToggleConfig'; import updateCookie from '../api/actions/updateCookie'; import loadCookie, { CookieStateType } from '../api/loader/loadCookie'; import deleteCookie from '../api/actions/deleteCookie'; import validateCookie from '../api/actions/validateCookie'; import deletePoToken from '../api/actions/deletePoToken'; import updatePoToken from '../api/actions/updatePoToken'; import { useUserConfigStore } from '../stores/UserConfigStore'; type SettingsApplicationReponses = { snapshots?: SnapshotListType; appSettingsConfig?: AppSettingsConfigType; apiToken?: string; cookieState?: CookieStateType; }; const SettingsApplication = () => { const { userConfig } = useUserConfigStore(); const [response, setResponse] = useState(); const [refresh, setRefresh] = useState(false); const snapshots = response?.snapshots; const appSettingsConfig = response?.appSettingsConfig; const apiToken = response?.apiToken; // Subscriptions const [videoPageSize, setVideoPageSize] = useState(null); const [livePageSize, setLivePageSize] = useState(null); const [shortPageSize, setShortPageSize] = useState(null); const [playlistPageSize, setPlaylistPageSize] = useState(null); const [isAutostart, setIsAutostart] = useState(false); const [isExtractFlat, setIsExtractFlat] = useState(false); // Downloads const [currentDownloadSpeed, setCurrentDownloadSpeed] = useState(null); const [currentThrottledRate, setCurrentThrottledRate] = useState(null); const [currentScrapingSleep, setCurrentScrapingSleep] = useState(null); const [currentAutodelete, setCurrentAutodelete] = useState(null); // Download Format const [downloadsFormat, setDownloadsFormat] = useState(null); const [downloadsFormatSort, setDownloadsFormatSort] = useState(null); const [downloadsExtractorLang, setDownloadsExtractorLang] = useState(null); const [embedMetadata, setEmbedMetadata] = useState(false); const [embedThumbnail, setEmbedThumbnail] = useState(false); // Subtitles const [subtitleLang, setSubtitleLang] = useState(null); const [subtitleSource, setSubtitleSource] = useState(null); const [indexSubtitles, setIndexSubtitles] = useState(false); // Comments const [commentsMax, setCommentsMax] = useState(null); const [commentsSort, setCommentsSort] = useState(''); // Cookie const [cookieFormData, setCookieFormData] = useState(''); const [showCookieForm, setShowCookieForm] = useState(false); const [poTokenFormData, setPoTokenFormData] = useState('web+'); const [showPoTokenForm, setShowPoTokenForm] = useState(false); // Integrations const [showApiToken, setShowApiToken] = useState(false); const [downloadDislikes, setDownloadDislikes] = useState(false); const [enableSponsorBlock, setEnableSponsorBlock] = useState(false); const [enableCast, setEnableCast] = useState(false); // Snapshots const [enableSnapshots, setEnableSnapshots] = useState(false); const [isSnapshotQueued, setIsSnapshotQueued] = useState(false); const [restoringSnapshot, setRestoringSnapshot] = useState(false); const fetchData = async () => { const snapshotResponse = await loadSnapshots(); const appSettingsConfig = await loadAppsettingsConfig(); const apiTokenResponse = await loadApiToken(); const cookieStateResponse = await loadCookie(); const { data: snapshotResponseData } = snapshotResponse ?? {}; const { data: appSettingsConfigData } = appSettingsConfig ?? {}; const { data: apiTokenResponseData } = apiTokenResponse ?? {}; const { data: cookieStateResponseData } = cookieStateResponse ?? {}; // Subscriptions setVideoPageSize(appSettingsConfigData?.subscriptions.channel_size ?? null); setLivePageSize(appSettingsConfigData?.subscriptions.live_channel_size ?? null); setShortPageSize(appSettingsConfigData?.subscriptions.shorts_channel_size ?? null); setPlaylistPageSize(appSettingsConfigData?.subscriptions.playlist_size || null); setIsAutostart(appSettingsConfigData?.subscriptions.auto_start || false); setIsExtractFlat(appSettingsConfigData?.subscriptions.extract_flat || false); // Downloads setCurrentDownloadSpeed(appSettingsConfigData?.downloads.limit_speed || null); setCurrentThrottledRate(appSettingsConfigData?.downloads.throttledratelimit || null); setCurrentScrapingSleep(appSettingsConfigData?.downloads.sleep_interval || null); setCurrentAutodelete(appSettingsConfigData?.downloads.autodelete_days || null); // Download Format setDownloadsFormat(appSettingsConfigData?.downloads.format || null); setDownloadsFormatSort(appSettingsConfigData?.downloads.format_sort || null); setDownloadsExtractorLang(appSettingsConfigData?.downloads.extractor_lang || null); setEmbedMetadata(appSettingsConfigData?.downloads.add_metadata || false); setEmbedThumbnail(appSettingsConfigData?.downloads.add_thumbnail || false); // Subtitles setSubtitleLang(appSettingsConfigData?.downloads.subtitle || null); setSubtitleSource(appSettingsConfigData?.downloads.subtitle_source || null); setIndexSubtitles(appSettingsConfigData?.downloads.subtitle_index || false); // Comments setCommentsMax(appSettingsConfigData?.downloads.comment_max || null); setCommentsSort(appSettingsConfigData?.downloads.comment_sort || ''); // Integrations setDownloadDislikes(appSettingsConfigData?.downloads.integrate_ryd || false); setEnableSponsorBlock(appSettingsConfigData?.downloads.integrate_sponsorblock || false); setEnableCast(appSettingsConfigData?.application.enable_cast || false); // Snapshots setEnableSnapshots(appSettingsConfigData?.application.enable_snapshot || false); setResponse({ snapshots: snapshotResponseData, appSettingsConfig: appSettingsConfigData, apiToken: apiTokenResponseData?.token, cookieState: cookieStateResponseData, }); }; const handleUpdateConfig = async ( configKey: string, configValue: string | boolean | number | null, ) => { const [group, key] = configKey.split('.'); const updatedConfig = { [group]: { [key]: configValue } } as Partial; await updateAppsettingsConfig(updatedConfig); setRefresh(true); }; const handleCookieUpdate = async () => { await updateCookie(cookieFormData); setCookieFormData(''); setShowCookieForm(false); setRefresh(true); }; const handleCookieRevoke = async () => { await deleteCookie(); setRefresh(true); }; const handleCookieValidate = async () => { await validateCookie(); setRefresh(true); }; const handlePoTokenRevoke = async () => { await deletePoToken(); setRefresh(true); }; const handlePoTokenUpdate = async () => { await updatePoToken(poTokenFormData); setPoTokenFormData('web+'); setShowPoTokenForm(false); setRefresh(true); }; useEffect(() => { fetchData(); }, []); useEffect(() => { if (refresh) { fetchData(); setRefresh(false); } }, [refresh]); return ( <> TA | Application Settings

Application Configurations

{appSettingsConfig && (

Subscription Scan

{userConfig.show_help_text && (

Configure how a subscription Scan tracks videos.

  • The pagesize configures how many videos are checked.
  • Max recommended page size is 50.
  • Disable shorts or streams by setting their page size to 0 (zero).
  • Autostart automatically starts downloading videos from subscriptions with priority.
  • Fast add extracts and adds videos in bulk. That is much faster but is not able to extract as much metadata during adding to the queue.
)}

Videos page size

Live Streams page size

Shorts page size

Playlist page size

Autostart download subscriptions

Fast add

Downloads

{userConfig.show_help_text && (
  • Limit download speed, in KB/s. Can be helpful to avoid getting blocked by YT.
  • Throttle rate limit restarts a download if the speed falls below the defined limit.
  • The sleep interval slows down requests to YT.
    • That reduces the likelihood of getting blocked by YT.
    • The number in seconds is randomized +/- 50% from the value you enter.
    • Minimal recommended is 10.
  • Auto delete deletes videos marked as watched after x days.
    • The cleanup task triggers after the download finishes.
    • Can also be configured on a per channel basis.
)}

Download Speed limit

Throttled rate limit

Sleep interval

Danger Zone: Auto delete watched

Download Format

{userConfig.show_help_text && (
  • The download format is equivalent to -f yt-dlp argument. Examples:
    • {'bestvideo[height<=720]+bestaudio/best[height<=720]'} : best audio and max video height of 720p.
    • {'bestvideo[height<=1080]+bestaudio/best[height<=1080]'} : best audio and max video height of 1080p.
    • {'bestvideo[height<=1080][vcodec*=avc1]+bestaudio[acodec*=mp4a]/mp4'} : Max 1080p video height with iOS compatible video and audio codecs.
    • This can also be configured on a per channel basis.
    • More details{' '} here .
  • Change the criteria what is considered best by yt-dlp. That is equivalent to --format-sort argument. Examples:
    • res,codec:av1: prefer AV1 over all other video codecs.
    • Not all codecs are supported by all browsers.
    • More details{' '} here .
  • Extractor language will change how a video gets indexed. Index language configuration
    • That will only have an effect if the uploader provides translations.
    • Add as two letter ISO language code.
    • More details{' '} here .
  • Embedding metadata adds additional metadata directly to the mp4 file.
  • Embedding the thumbnail embeds the video thumbnail as a cover.jpg to the mp4 file.
)}

Select download format for yt-dlp.

Sort download formats

Extractor Language

Embed metadata

Embed Thumbnail

Subtitles

{userConfig.show_help_text && (

Additional subtitle options show once you choose a language.

  • Choose which subtitles to download, add comma separated language codes, e.g.{' '} en, de, zh-Hans
  • Enabling auto generated subtitles adds fallback to less accurate auto generated subtitles from YT.
  • Indexing subtitles add the fulltext to the ES index. Not recommended on low end hardware.
)}

Choose subtitle language

{appSettingsConfig?.downloads.subtitle && ( <>

Enable auto generated subtitles

{ handleUpdateConfig( 'downloads.subtitle_source', event.target.checked ? 'auto' : 'user', ); }} /> {subtitleSource === 'user' && ( )} {subtitleSource === 'auto' && ( )}

Enable subtitle index

)}

Comments

{userConfig.show_help_text && (

Additional options show once you set a comment index option.

  • Download and index comments. Browsable on the video detail page. Example:
    • all,100,all,30: Get 100 max-parents and 30 max-replies-per-thread.
    • 1000,all,all,50: Get a total of 1000 comments over all, 50 replies per thread.
    • Values are in the format:{' '} max-comments,max-parents,max-replies,max-replies-per-thread .
    • Choose wisely, as extracting comments is slow.
  • The sort order changes how comments are indexed.
)}

Index comments

{appSettingsConfig?.downloads.comment_max && (

Comment sort method

)}
{userConfig.show_help_text && (

Importing your cookie will authenticate requests to YT with your user account.

  • Adding your cookie can avoid your requests getting blocked.
  • This expects your cookie in Netscape format.
  • For automatic cookie import use Tube Archivist Companion{' '} browser extension .
  • The PO Token (Proof of origin token) can authenticate your request. Make sure to read the{' '} PO guide
)}

Use your cookie for yt-dlp

{response?.cookieState?.cookie_enabled ? ( <>

Cookie enabled. Last validation:{' '} {response.cookieState.validated_str} .

) : (

Cookie disabled

)} {showCookieForm ? ( <>