Files
yt-dlp-webui/frontend/src/atoms/settings.ts

189 lines
5.1 KiB
TypeScript

import { pipe } from 'fp-ts/lib/function'
import { matchW } from 'fp-ts/lib/TaskEither'
import { ffetch } from '../lib/httpClient'
import { prefersDarkMode } from '../utils'
import { atomWithStorage } from 'jotai/utils'
import { atom } from 'jotai'
export const languages = [
'catalan',
'chinese',
'english',
'french',
'german',
'italian',
'japanese',
'korean',
'polish',
'portuguese-br',
'russian',
'spanish',
'swedish',
'ukrainian',
'hungarian'
] as const
export type Language = (typeof languages)[number]
export type Theme = 'light' | 'dark' | 'system'
export type ThemeNarrowed = 'light' | 'dark'
export const accents = ['default', 'red'] as const
export type Accent = (typeof accents)[number]
export interface SettingsState {
serverAddr: string
serverPort: number
language: Language
theme: ThemeNarrowed
accent: Accent
cliArgs: string
formatSelection: boolean
fileRenaming: boolean
autoFileExtension: boolean
pathOverriding: boolean
enableCustomArgs: boolean
listView: boolean
servedFromReverseProxy: boolean
appTitle: string
}
export const languageState = atomWithStorage<Language>(
'language',
localStorage.getItem('language') as Language || 'english'
)
export const themeState = atomWithStorage<Theme>(
'theme',
localStorage.getItem('theme') as Theme || 'system'
)
export const serverAddressState = atomWithStorage<string>(
'server-addr',
localStorage.getItem('server-addr') || window.location.hostname
)
export const serverPortState = atomWithStorage<number>(
'server-port',
Number(localStorage.getItem('server-port')) || Number(window.location.port)
)
export const latestCliArgumentsState = atomWithStorage<string>(
'cli-args',
localStorage.getItem('cli-args') || '--no-mtime'
)
export const formatSelectionState = atomWithStorage(
'format-selection',
localStorage.getItem('format-selection') === 'true'
)
export const fileRenamingState = atomWithStorage(
'file-renaming',
localStorage.getItem('file-renaming') === 'true'
)
export const autoFileExtensionState = atomWithStorage(
'auto-file-extension',
localStorage.getItem('auto-file-extension') === 'true'
)
export const pathOverridingState = atomWithStorage(
'path-overriding',
localStorage.getItem('path-overriding') === 'true'
)
export const enableCustomArgsState = atomWithStorage(
'enable-custom-args',
localStorage.getItem('enable-custom-args') === 'true'
)
export const listViewState = atomWithStorage(
'listview',
localStorage.getItem('listview') === 'true'
)
export const servedFromReverseProxyState = atomWithStorage(
'reverseProxy',
localStorage.getItem('reverseProxy') === 'true' || window.location.port == ''
)
export const servedFromReverseProxySubDirState = atomWithStorage<string>(
'reverseProxySubDir',
localStorage.getItem('reverseProxySubDir') ?? ''
)
export const appTitleState = atomWithStorage(
'appTitle',
localStorage.getItem('appTitle') ?? 'yt-dlp Web UI'
)
export const serverAddressAndPortState = atom((get) => {
if (get(servedFromReverseProxySubDirState)) {
return `${get(serverAddressState)}/${get(servedFromReverseProxySubDirState)}/`
.replaceAll('"', '') // XXX: atomWithStorage uses JSON.stringify to serialize
.replaceAll('//', '/') // which puts extra double quotes.
}
if (get(servedFromReverseProxyState)) {
return `${get(serverAddressState)}`
.replaceAll('"', '')
}
const sap = `${get(serverAddressState)}:${get(serverPortState)}`
.replaceAll('"', '')
return sap.endsWith('/') ? sap.slice(0, -1) : sap
})
export const serverURL = atom((get) =>
`${window.location.protocol}//${get(serverAddressAndPortState)}`
)
export const rpcWebSocketEndpoint = atom((get) => {
const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
return `${proto}//${get(serverAddressAndPortState)}/rpc/ws`
})
export const rpcHTTPEndpoint = atom((get) => {
const proto = window.location.protocol
return `${proto}//${get(serverAddressAndPortState)}/rpc/http`
})
export const serverSideCookiesState = atom<Promise<string>>(async (get) => await pipe(
ffetch<Readonly<{ cookies: string }>>(`${get(serverURL)}/api/v1/cookies`),
matchW(
() => '',
(r) => r.cookies
)
)())
const themeSelector = atom<ThemeNarrowed>((get) => {
const theme = get(themeState)
if ((theme === 'system' && prefersDarkMode()) || theme === 'dark') {
return 'dark'
}
return 'light'
})
export const accentState = atomWithStorage<Accent>(
'accent-color',
localStorage.getItem('accent-color') as Accent ?? 'default',
)
export const settingsState = atom<SettingsState>((get) => ({
serverAddr: get(serverAddressState),
serverPort: get(serverPortState),
language: get(languageState),
theme: get(themeSelector),
accent: get(accentState),
cliArgs: get(latestCliArgumentsState),
formatSelection: get(formatSelectionState),
fileRenaming: get(fileRenamingState),
autoFileExtension: get(autoFileExtensionState),
pathOverriding: get(pathOverridingState),
enableCustomArgs: get(enableCustomArgsState),
listView: get(listViewState),
servedFromReverseProxy: get(servedFromReverseProxyState),
appTitle: get(appTitleState)
}))