import React, { useEffect, useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { Button, Form, InputGroup } from "react-bootstrap"; import * as GQL from "src/core/generated-graphql"; import { useConfiguration, useConfigureGeneral, useGenerateAPIKey, } from "src/core/StashService"; import { useToast } from "src/hooks"; import { Icon, LoadingIndicator } from "src/components/Shared"; import StashBoxConfiguration, { IStashBoxInstance, } from "./StashBoxConfiguration"; import StashConfiguration from "./StashConfiguration"; interface IExclusionPatternsProps { excludes: string[]; setExcludes: (value: string[]) => void; demo: string; } export const ExclusionPatterns: React.FC = (props) => { function excludeRegexChanged(idx: number, value: string) { const newExcludes = props.excludes.map((regex, i) => { const ret = idx !== i ? regex : value; return ret; }); props.setExcludes(newExcludes); } function excludeRemoveRegex(idx: number) { const newExcludes = props.excludes.filter((_regex, i) => i !== idx); props.setExcludes(newExcludes); } function excludeAddRegex() { const newExcludes = props.excludes.concat(props.demo); props.setExcludes(newExcludes); } return ( <> {props.excludes && props.excludes.map((regexp, i) => ( ) => excludeRegexChanged(i, e.currentTarget.value) } /> ))} ); }; export const SettingsConfigurationPanel: React.FC = () => { const intl = useIntl(); const Toast = useToast(); // Editing config state const [stashes, setStashes] = useState([]); const [databasePath, setDatabasePath] = useState( undefined ); const [generatedPath, setGeneratedPath] = useState( undefined ); const [cachePath, setCachePath] = useState(undefined); const [calculateMD5, setCalculateMD5] = useState(false); const [videoFileNamingAlgorithm, setVideoFileNamingAlgorithm] = useState< GQL.HashAlgorithm | undefined >(undefined); const [parallelTasks, setParallelTasks] = useState(0); const [previewAudio, setPreviewAudio] = useState(true); const [previewSegments, setPreviewSegments] = useState(0); const [previewSegmentDuration, setPreviewSegmentDuration] = useState( 0 ); const [previewExcludeStart, setPreviewExcludeStart] = useState< string | undefined >(undefined); const [previewExcludeEnd, setPreviewExcludeEnd] = useState< string | undefined >(undefined); const [previewPreset, setPreviewPreset] = useState( GQL.PreviewPreset.Slow ); const [maxTranscodeSize, setMaxTranscodeSize] = useState< GQL.StreamingResolutionEnum | undefined >(undefined); const [maxStreamingTranscodeSize, setMaxStreamingTranscodeSize] = useState< GQL.StreamingResolutionEnum | undefined >(undefined); const [writeImageThumbnails, setWriteImageThumbnails] = useState(true); const [username, setUsername] = useState(undefined); const [password, setPassword] = useState(undefined); const [maxSessionAge, setMaxSessionAge] = useState(0); const [logFile, setLogFile] = useState(); const [logOut, setLogOut] = useState(true); const [logLevel, setLogLevel] = useState("Info"); const [logAccess, setLogAccess] = useState(true); const [videoExtensions, setVideoExtensions] = useState(); const [imageExtensions, setImageExtensions] = useState(); const [galleryExtensions, setGalleryExtensions] = useState< string | undefined >(); const [ createGalleriesFromFolders, setCreateGalleriesFromFolders, ] = useState(false); const [excludes, setExcludes] = useState([]); const [imageExcludes, setImageExcludes] = useState([]); const [ customPerformerImageLocation, setCustomPerformerImageLocation, ] = useState(); const [stashBoxes, setStashBoxes] = useState([]); const { data, error, loading } = useConfiguration(); const [generateAPIKey] = useGenerateAPIKey(); const [updateGeneralConfig] = useConfigureGeneral({ stashes: stashes.map((s) => ({ path: s.path, excludeVideo: s.excludeVideo, excludeImage: s.excludeImage, })), databasePath, generatedPath, cachePath, calculateMD5, videoFileNamingAlgorithm: (videoFileNamingAlgorithm as GQL.HashAlgorithm) ?? undefined, parallelTasks, previewAudio, previewSegments, previewSegmentDuration, previewExcludeStart, previewExcludeEnd, previewPreset: (previewPreset as GQL.PreviewPreset) ?? undefined, maxTranscodeSize, maxStreamingTranscodeSize, writeImageThumbnails, username, password, maxSessionAge, logFile, logOut, logLevel, logAccess, createGalleriesFromFolders, videoExtensions: commaDelimitedToList(videoExtensions), imageExtensions: commaDelimitedToList(imageExtensions), galleryExtensions: commaDelimitedToList(galleryExtensions), excludes, imageExcludes, customPerformerImageLocation, stashBoxes: stashBoxes.map( (b) => ({ name: b?.name ?? "", api_key: b?.api_key ?? "", endpoint: b?.endpoint ?? "", } as GQL.StashBoxInput) ), }); useEffect(() => { if (!data?.configuration || error) return; const conf = data.configuration; if (conf.general) { setStashes(conf.general.stashes ?? []); setDatabasePath(conf.general.databasePath); setGeneratedPath(conf.general.generatedPath); setCachePath(conf.general.cachePath); setVideoFileNamingAlgorithm(conf.general.videoFileNamingAlgorithm); setCalculateMD5(conf.general.calculateMD5); setParallelTasks(conf.general.parallelTasks); setPreviewAudio(conf.general.previewAudio); setPreviewSegments(conf.general.previewSegments); setPreviewSegmentDuration(conf.general.previewSegmentDuration); setPreviewExcludeStart(conf.general.previewExcludeStart); setPreviewExcludeEnd(conf.general.previewExcludeEnd); setPreviewPreset(conf.general.previewPreset); setMaxTranscodeSize(conf.general.maxTranscodeSize ?? undefined); setMaxStreamingTranscodeSize( conf.general.maxStreamingTranscodeSize ?? undefined ); setWriteImageThumbnails(conf.general.writeImageThumbnails); setUsername(conf.general.username); setPassword(conf.general.password); setMaxSessionAge(conf.general.maxSessionAge); setLogFile(conf.general.logFile ?? undefined); setLogOut(conf.general.logOut); setLogLevel(conf.general.logLevel); setLogAccess(conf.general.logAccess); setCreateGalleriesFromFolders(conf.general.createGalleriesFromFolders); setVideoExtensions(listToCommaDelimited(conf.general.videoExtensions)); setImageExtensions(listToCommaDelimited(conf.general.imageExtensions)); setGalleryExtensions( listToCommaDelimited(conf.general.galleryExtensions) ); setExcludes(conf.general.excludes); setImageExcludes(conf.general.imageExcludes); setCustomPerformerImageLocation( conf.general.customPerformerImageLocation ?? "" ); setStashBoxes( conf.general.stashBoxes.map((box, i) => ({ name: box?.name ?? undefined, endpoint: box.endpoint, api_key: box.api_key, index: i, })) ?? [] ); } }, [data, error]); function commaDelimitedToList(value: string | undefined) { if (value) { return value.split(",").map((s) => s.trim()); } } function listToCommaDelimited(value: string[] | undefined) { if (value) { return value.join(", "); } } async function onGenerateAPIKey() { try { await generateAPIKey({ variables: { input: {}, }, }); } catch (e) { Toast.error(e); } } async function onClearAPIKey() { try { await generateAPIKey({ variables: { input: { clear: true, }, }, }); } catch (e) { Toast.error(e); } } async function onSave() { try { const result = await updateGeneralConfig(); // eslint-disable-next-line no-console console.log(result); Toast.success({ content: intl.formatMessage( { id: "toast.updated_entity" }, { entity: intl .formatMessage({ id: "configuration" }) .toLocaleLowerCase(), } ), }); } catch (e) { Toast.error(e); } } const transcodeQualities = [ GQL.StreamingResolutionEnum.Low, GQL.StreamingResolutionEnum.Standard, GQL.StreamingResolutionEnum.StandardHd, GQL.StreamingResolutionEnum.FullHd, GQL.StreamingResolutionEnum.FourK, GQL.StreamingResolutionEnum.Original, ].map(resolutionToString); function resolutionToString(r: GQL.StreamingResolutionEnum | undefined) { switch (r) { case GQL.StreamingResolutionEnum.Low: return "240p"; case GQL.StreamingResolutionEnum.Standard: return "480p"; case GQL.StreamingResolutionEnum.StandardHd: return "720p"; case GQL.StreamingResolutionEnum.FullHd: return "1080p"; case GQL.StreamingResolutionEnum.FourK: return "4k"; case GQL.StreamingResolutionEnum.Original: return "Original"; } return "Original"; } function translateQuality(quality: string) { switch (quality) { case "240p": return GQL.StreamingResolutionEnum.Low; case "480p": return GQL.StreamingResolutionEnum.Standard; case "720p": return GQL.StreamingResolutionEnum.StandardHd; case "1080p": return GQL.StreamingResolutionEnum.FullHd; case "4k": return GQL.StreamingResolutionEnum.FourK; case "Original": return GQL.StreamingResolutionEnum.Original; } return GQL.StreamingResolutionEnum.Original; } const namingHashAlgorithms = [ GQL.HashAlgorithm.Md5, GQL.HashAlgorithm.Oshash, ].map(namingHashToString); function namingHashToString(value: GQL.HashAlgorithm | undefined) { switch (value) { case GQL.HashAlgorithm.Oshash: return "oshash"; case GQL.HashAlgorithm.Md5: return "MD5"; } return "MD5"; } function translateNamingHash(value: string) { switch (value) { case "oshash": return GQL.HashAlgorithm.Oshash; case "MD5": return GQL.HashAlgorithm.Md5; } return GQL.HashAlgorithm.Md5; } if (error) return

{error.message}

; if (!data?.configuration || loading) return ; return ( <>

Stashes
setStashes(s)} /> {intl.formatMessage({ id: "config.general.directory_locations_to_your_content", })}
) => setDatabasePath(e.currentTarget.value) } /> {intl.formatMessage({ id: "config.general.sqlite_location" })}
) => setGeneratedPath(e.currentTarget.value) } /> {intl.formatMessage({ id: "config.general.generated_files_location", })}
) => setCachePath(e.currentTarget.value) } /> {intl.formatMessage({ id: "config.general.cache_location" })}
) => setVideoExtensions(e.currentTarget.value) } /> {intl.formatMessage({ id: "config.general.video_ext_desc" })}
) => setImageExtensions(e.currentTarget.value) } /> {intl.formatMessage({ id: "config.general.image_ext_desc" })}
{intl.formatMessage({ id: "config.general.gallery_ext_head" })}
) => setGalleryExtensions(e.currentTarget.value) } /> {intl.formatMessage({ id: "config.general.gallery_ext_desc" })}
{intl.formatMessage({ id: "config.general.excluded_video_patterns_head", })}
{intl.formatMessage({ id: "config.general.excluded_video_patterns_desc", })}
{intl.formatMessage({ id: "config.general.excluded_image_gallery_patterns_head", })}
{intl.formatMessage({ id: "config.general.excluded_image_gallery_patterns_desc", })}
setCreateGalleriesFromFolders(!createGalleriesFromFolders) } /> {intl.formatMessage({ id: "config.general.create_galleries_from_folders_desc", })}

{intl.formatMessage({ id: "config.general.hashing" })}

setCalculateMD5(!calculateMD5)} /> {intl.formatMessage({ id: "config.general.calculate_md5_and_ohash_desc", })}
{intl.formatMessage({ id: "config.general.generated_file_naming_hash_head", })}
) => setVideoFileNamingAlgorithm( translateNamingHash(e.currentTarget.value) ) } > {namingHashAlgorithms.map((q) => ( ))} {intl.formatMessage({ id: "config.general.generated_file_naming_hash_desc", })}

{intl.formatMessage({ id: "config.general.video_head" })}

{intl.formatMessage({ id: "config.general.maximum_transcode_size_head", })}
) => setMaxTranscodeSize(translateQuality(event.currentTarget.value)) } value={resolutionToString(maxTranscodeSize)} > {transcodeQualities.map((q) => ( ))} {intl.formatMessage({ id: "config.general.maximum_transcode_size_desc", })}
{intl.formatMessage({ id: "config.general.maximum_streaming_transcode_size_head", })}
) => setMaxStreamingTranscodeSize( translateQuality(event.currentTarget.value) ) } value={resolutionToString(maxStreamingTranscodeSize)} > {transcodeQualities.map((q) => ( ))} {intl.formatMessage({ id: "config.general.maximum_streaming_transcode_size_desc", })}

{intl.formatMessage({ id: "config.general.parallel_scan_head" })}

{intl.formatMessage({ id: "config.general.number_of_parallel_task_for_scan_generation_head", })}
) => setParallelTasks( Number.parseInt(e.currentTarget.value || "0", 10) ) } /> {intl.formatMessage({ id: "config.general.number_of_parallel_task_for_scan_generation_desc", })}

{intl.formatMessage({ id: "config.general.preview_generation" })}

{intl.formatMessage({ id: "dialogs.scene_gen.preview_preset_head", })}
) => setPreviewPreset(e.currentTarget.value) } > {Object.keys(GQL.PreviewPreset).map((p) => ( ))} {intl.formatMessage({ id: "dialogs.scene_gen.preview_preset_desc", })}
setPreviewAudio(!previewAudio)} /> {intl.formatMessage({ id: "config.general.include_audio_desc", })}
{intl.formatMessage({ id: "dialogs.scene_gen.preview_seg_count_head", })}
) => setPreviewSegments( Number.parseInt(e.currentTarget.value || "0", 10) ) } /> {intl.formatMessage({ id: "dialogs.scene_gen.preview_seg_count_desc", })}
{intl.formatMessage({ id: "dialogs.scene_gen.preview_seg_duration_head", })}
) => setPreviewSegmentDuration( Number.parseFloat(e.currentTarget.value || "0") ) } /> {intl.formatMessage({ id: "dialogs.scene_gen.preview_seg_duration_desc", })}
{intl.formatMessage({ id: "dialogs.scene_gen.preview_exclude_start_time_head", })}
) => setPreviewExcludeStart(e.currentTarget.value) } /> {intl.formatMessage({ id: "dialogs.scene_gen.preview_exclude_start_time_desc", })}
{intl.formatMessage({ id: "dialogs.scene_gen.preview_exclude_end_time_head", })}
) => setPreviewExcludeEnd(e.currentTarget.value) } /> {intl.formatMessage({ id: "dialogs.scene_gen.preview_exclude_end_time_desc", })}

{intl.formatMessage({ id: "images" })}

setWriteImageThumbnails(!writeImageThumbnails)} /> {intl.formatMessage({ id: "config.ui.images.options.write_image_thumbnails.description", })}

{intl.formatMessage({ id: "performers" })}

{intl.formatMessage({ id: "config.ui.performers.options.image_location.heading", })}
) => { setCustomPerformerImageLocation(e.currentTarget.value); }} /> {intl.formatMessage({ id: "config.ui.performers.options.image_location.description", })}

{intl.formatMessage({ id: "config.general.auth.stash-box_integration", })}


{intl.formatMessage({ id: "config.general.auth.authentication" })}

{intl.formatMessage({ id: "config.general.auth.username" })}
) => setUsername(e.currentTarget.value) } /> {intl.formatMessage({ id: "config.general.auth.username_desc" })}
{intl.formatMessage({ id: "config.general.auth.password" })}
) => setPassword(e.currentTarget.value) } /> {intl.formatMessage({ id: "config.general.auth.password_desc" })}
{intl.formatMessage({ id: "config.general.auth.api_key" })}
{intl.formatMessage({ id: "config.general.auth.api_key_desc" })}
{intl.formatMessage({ id: "config.general.auth.maximum_session_age", })}
) => setMaxSessionAge( Number.parseInt(e.currentTarget.value || "0", 10) ) } /> {intl.formatMessage({ id: "config.general.auth.maximum_session_age_desc", })}

{intl.formatMessage({ id: "config.general.logging" })}

{intl.formatMessage({ id: "config.general.auth.log_file" })}
) => setLogFile(e.currentTarget.value) } /> {intl.formatMessage({ id: "config.general.auth.log_file_desc" })}
setLogOut(!logOut)} /> {intl.formatMessage({ id: "config.general.auth.log_to_terminal_desc", })}
{intl.formatMessage({ id: "config.logs.log_level" })}
) => setLogLevel(event.currentTarget.value) } value={logLevel} > {["Trace", "Debug", "Info", "Warning", "Error"].map((o) => ( ))}
setLogAccess(!logAccess)} /> {intl.formatMessage({ id: "config.general.auth.log_http_desc" })}
); };