actions({...props, onDelete})}
/>
}
export function ArchiveDownloadForm({download, onCancel, onSuccess: propOnSuccess, submitter: propSubmitter, actions}) {
const [showMessage, setShowMessage] = React.useState(false);
const submitter = propSubmitter || (async (formData) => {
const downloadData = {
downloader: formData.downloader,
tag_names: formData.tag_names,
urls: formData.urls.split(/\r?\n/),
}
await postDownload(downloadData);
});
const emptyFormData = {
downloader: Downloaders.Archive,
urls: '',
tag_names: [],
};
const onSuccess = () => {
setShowMessage(true);
if (propOnSuccess) {
propOnSuccess();
}
}
const form = useForm({
submitter,
defaultFormData: mergeDeep(emptyFormData, download),
emptyFormData,
clearOnSuccess: !propOnSuccess,
onSuccess,
});
const localOnCancel = (e) => {
if (e) e.preventDefault();
if (onCancel) {
onCancel();
}
}
return
}
export function RSSDownloadForm({download, submitter, onDelete, onCancel, actions, clearOnSuccess = true}) {
const [showMessage, setShowMessage] = React.useState(false);
const [config, setConfig] = React.useState(null);
const [isConfigLoaded, setIsConfigLoaded] = React.useState(false);
const [userChangedResolutions, setUserChangedResolutions] = React.useState(false);
const [defaultVideoFormat, setDefaultVideoFormat] = useLocalStorage('video_format', defaultVideoFormatOption);
submitter = submitter || (async (formData) => {
const downloadData = {
destination: formData.destination,
downloader: formData.downloader,
frequency: formData.frequency,
settings: formData.settings,
sub_downloader: formData.sub_downloader,
tag_names: formData.tag_names || [],
urls: [formData.url],
}
await postDownload(downloadData);
});
// Use config video resolutions if available, otherwise use default
const configResolutions = config && config.video_resolutions ? config.video_resolutions : defaultVideoResolutionOptions;
const emptyFormData = {
destination: null,
downloader: Downloaders.RSS,
frequency: weeklyOption.value,
sub_downloader: null,
settings: {
excluded_urls: null,
title_exclude: null,
title_include: null,
video_resolutions: configResolutions,
video_format: defaultVideoFormat,
},
tag_names: [],
url: '',
};
const form = useForm({
submitter,
defaultFormData: mergeDeep(emptyFormData, download),
emptyFormData,
clearOnSuccess,
onSuccess: async () => setShowMessage(true),
});
// Fetch video downloader config when the modal opens
React.useEffect(() => {
const fetchConfig = async () => {
try {
const result = await fetchVideoDownloaderConfig();
setConfig(result);
setIsConfigLoaded(true);
// Update form with video resolutions from config only if user hasn't changed them
// and if no download object was provided (this is a new download, not an edit)
if (result && result.video_resolutions && !userChangedResolutions && !download) {
form.setValue('settings.video_resolutions', result.video_resolutions);
}
} catch (error) {
console.error('Failed to fetch video downloader config:', error);
setIsConfigLoaded(true);
}
};
fetchConfig();
}, []);
React.useEffect(() => {
const {video_format} = form.formData.settings;
if (video_format && video_format !== defaultVideoFormat) {
setDefaultVideoFormat(video_format);
}
}, [form.formData]);
// Default to "new" download buttons.
actions = actions || DownloadFormButtons;
const actionsElm = actions({onDelete, onCancel, form});
let downloaderRows;
if (form.formData.sub_downloader === Downloaders.Video) {
downloaderRows = <>
setUserChangedResolutions(true)}
/>
>;
} else if (form.formData.sub_downloader === Downloaders.Archive) {
downloaderRows =
;
}
return
}
export function EditRSSDownloadForm({download, onDelete, onCancel, actions = EditDownloadFormButtons}) {
const submitter = async (formData) => {
const downloadData = {
destination: formData.destination,
downloader: formData.downloader,
frequency: formData.frequency,
settings: formData.settings,
sub_downloader: formData.sub_downloader,
tag_names: formData.tag_names || [],
urls: [formData.url,],
}
await putDownload(download.id, downloadData);
}
return
}
export function EditZimDownloadForm({download, onDelete, onCancel, actions = EditDownloadFormButtons}) {
const submitter = async (formData) => {
const downloadData = {
downloader: formData.downloader,
frequency: formData.frequency,
settings: formData.settings,
sub_downloader: formData.sub_downloader,
tag_names: formData.tag_names,
urls: [formData.url,],
}
await putDownload(download.id, downloadData);
}
const [showMessage, setShowMessage] = React.useState(false);
const emptyFormData = {
downloader: Downloaders.RSS,
frequency: weeklyOption.value,
sub_downloader: null,
tag_names: [],
url: '',
};
const form = useForm({
submitter,
defaultFormData: mergeDeep(emptyFormData, download),
emptyFormData,
onSuccess: async () => setShowMessage(true),
});
// Default to "new" download buttons.
actions = actions || DownloadFormButtons;
const actionsElm = actions({onDelete, onCancel, form});
return
}
export function FilesDownloadForm({
download,
submitter,
onCancel,
actions = DownloadFormButtons,
clearOnSuccess = true,
}) {
const [showMessage, setShowMessage] = React.useState(false);
submitter = submitter || (async (formData) => {
const downloadData = {
downloader: Downloaders.File,
tag_names: formData.tag_names,
destination: formData.destination,
urls: formData.urls.split(/\r?\n/),
}
await postDownload(downloadData);
});
const emptyFormData = {
destination: null,
tag_names: [],
urls: '',
};
const form = useForm({
submitter,
defaultFormData: mergeDeep(emptyFormData, download),
emptyFormData,
clearOnSuccess,
onSuccess: async () => setShowMessage(true),
});
const actionsElm = actions({onCancel, form});
return
}
const suffixValidator = (value) => {
const error = commaSeparatedValidator(value);
if (error) {
return error;
}
const suffixes = value.split(',');
for (const suffix of suffixes) {
if (!suffix.startsWith('.')) {
return 'Suffix must start with .';
}
if (suffix.length === 1) {
return 'Suffix must have characters after .';
}
}
}
export function SuffixFormInput({form, name = 'suffix', path = 'settings.suffix'}) {
return
}
export function ScrapeFilesDownloadForm({
download,
submitter,
clearOnSuccess,
onDelete,
onCancel,
onSuccess,
actions = DownloadFormButtons,
singleDownload = false,
}) {
const [showMessage, setShowMessage] = React.useState(false);
submitter = submitter || (async (formData) => {
const downloadData = {
downloader: Downloaders.ScrapeHtml,
sub_downloader: Downloaders.File,
tag_names: formData.tag_names || [],
destination: formData.destination,
urls: formData.urls.split(/\r?\n/),
settings: formData.settings,
}
await postDownload(downloadData);
});
const emptyFormData = {
destination: null,
tag_names: [],
urls: '',
settings: {
depth: 1,
max_pages: 1,
suffix: '',
}
};
const localOnSuccess = async () => {
setShowMessage(true);
if (onSuccess) {
onSuccess();
}
}
const form = useForm({
submitter,
defaultFormData: mergeDeep(emptyFormData, download),
emptyFormData,
clearOnSuccess,
onSuccess: localOnSuccess,
});
const urlInput = singleDownload ?
: ;
// Default to "new" download buttons.
actions = actions || DownloadFormButtons;
const actionsElm = actions({onDelete, onCancel, form});
return
}
export function EditScrapeFilesDownloadForm({download, onDelete, onCancel, onSuccess}) {
const submitter = async (formData) => {
const downloadData = {
downloader: Downloaders.ScrapeHtml,
sub_downloader: Downloaders.File,
tag_names: formData.tag_names || [],
destination: formData.destination,
urls: [formData.url],
settings: formData.settings,
}
await postDownload(downloadData);
};
return
}
export function DownloadMenu({onOpen, disabled}) {
const [downloader, setDownloader] = useState();
const localOnOpen = (name) => {
setDownloader(name);
if (onOpen) {
onOpen(name);
}
}
let body = (<>