feat: Update version to 1.7.16 and add CollapsibleSection
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
export const VERSION = {
|
||||
number: "1.7.15",
|
||||
number: "1.7.16",
|
||||
buildDate: new Date().toISOString().split("T")[0],
|
||||
name: "MyTube Backend Server",
|
||||
displayVersion: function () {
|
||||
|
||||
28
frontend/src/components/CollapsibleSection.tsx
Normal file
28
frontend/src/components/CollapsibleSection.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import { Accordion, AccordionDetails, AccordionSummary, Typography } from '@mui/material';
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
interface CollapsibleSectionProps {
|
||||
title: string;
|
||||
children: ReactNode;
|
||||
defaultExpanded?: boolean;
|
||||
}
|
||||
|
||||
const CollapsibleSection: React.FC<CollapsibleSectionProps> = ({ title, children, defaultExpanded = true }) => {
|
||||
return (
|
||||
<Accordion defaultExpanded={defaultExpanded} sx={{ width: '100%', mb: 2 }}>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-controls={`panel-${title.replace(/\s+/g, '-').toLowerCase()}-content`}
|
||||
id={`panel-${title.replace(/\s+/g, '-').toLowerCase()}-header`}
|
||||
>
|
||||
<Typography variant="h6">{title}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
{children}
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export default CollapsibleSection;
|
||||
@@ -19,7 +19,6 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({ debugMode, onDebugM
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>{t('debugMode')}</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||
{t('debugModeDescription')}
|
||||
</Typography>
|
||||
|
||||
@@ -288,7 +288,6 @@ const CloudDriveSettings: React.FC<CloudDriveSettingsProps> = ({ settings, onCha
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>{t('cloudDriveSettings')} (beta)</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||
{t('cloudDriveDescription')}
|
||||
</Typography>
|
||||
|
||||
@@ -57,9 +57,6 @@ const CloudflareSettings: React.FC<CloudflareSettingsProps> = ({ enabled, token,
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h6" >
|
||||
{t('cloudflaredTunnel')}
|
||||
</Typography>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
|
||||
@@ -83,7 +83,6 @@ const CookieSettings: React.FC<CookieSettingsProps> = ({ onSuccess, onError }) =
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>{t('cookieSettings') || 'Cookie Settings'}</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||
{t('cookieUploadDescription') || 'Upload cookies.txt to pass YouTube bot checks and enable Bilibili subtitle downloads. The file will be renamed to cookies.txt automatically. (Example: use "Get cookies.txt LOCALLY" extension to export cookies)'}
|
||||
</Typography>
|
||||
|
||||
@@ -129,7 +129,6 @@ const DatabaseSettings: React.FC<DatabaseSettingsProps> = ({
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>{t('database')}</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||
{t('migrateDataDescription')}
|
||||
</Typography>
|
||||
|
||||
@@ -22,7 +22,6 @@ const DownloadSettings: React.FC<DownloadSettingsProps> = ({
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>{t('downloadSettings')}</Typography>
|
||||
<Typography gutterBottom>
|
||||
{t('maxConcurrent')}: {settings.maxConcurrentDownloads}
|
||||
</Typography>
|
||||
|
||||
@@ -135,7 +135,6 @@ const GeneralSettings: React.FC<GeneralSettingsProps> = (props) => {
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>{t('general')}</Typography>
|
||||
<Box sx={{ maxWidth: 400, display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
{!isVisitorMode && (
|
||||
<>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, FormControlLabel, Switch, TextField, Typography } from '@mui/material';
|
||||
import { Box, FormControlLabel, Switch, TextField } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useLanguage } from '../../contexts/LanguageContext';
|
||||
import { Settings } from '../../types';
|
||||
@@ -13,7 +13,6 @@ const SecuritySettings: React.FC<SecuritySettingsProps> = ({ settings, onChange
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>{t('security')}</Typography>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, Button, Chip, TextField, Typography } from '@mui/material';
|
||||
import { Box, Button, Chip, TextField } from '@mui/material';
|
||||
import React, { useState } from 'react';
|
||||
import { useLanguage } from '../../contexts/LanguageContext';
|
||||
|
||||
@@ -27,7 +27,6 @@ const TagsSettings: React.FC<TagsSettingsProps> = ({ tags, onTagsChange }) => {
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>{t('tagsManagement') || 'Tags Management'}</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 1, mb: 2, flexWrap: 'wrap' }}>
|
||||
{tagsArray.length > 0 && tagsArray.map((tag) => (
|
||||
<Chip
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, FormControlLabel, Switch, Typography } from '@mui/material';
|
||||
import { Box, FormControlLabel, Switch } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useLanguage } from '../../contexts/LanguageContext';
|
||||
import { Settings } from '../../types';
|
||||
@@ -13,7 +13,6 @@ const VideoDefaultSettings: React.FC<VideoDefaultSettingsProps> = ({ settings, o
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>{t('videoDefaults')}</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
|
||||
@@ -242,9 +242,6 @@ const YtDlpSettings: React.FC<YtDlpSettingsProps> = ({ config, proxyOnlyYoutube
|
||||
<Box>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 2, mb: 2 }}>
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
{t('ytDlpConfiguration') || 'yt-dlp Configuration'}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{t('ytDlpConfigurationDescription') || 'Configure yt-dlp options. See '}
|
||||
<Link
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
Card,
|
||||
CardContent,
|
||||
Container,
|
||||
Divider,
|
||||
Grid,
|
||||
Snackbar,
|
||||
Typography
|
||||
@@ -14,6 +13,7 @@ import {
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import axios from 'axios';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import CollapsibleSection from '../components/CollapsibleSection';
|
||||
import ConfirmationModal from '../components/ConfirmationModal';
|
||||
import AdvancedSettings from '../components/Settings/AdvancedSettings';
|
||||
import CloudDriveSettings from '../components/Settings/CloudDriveSettings';
|
||||
@@ -169,142 +169,143 @@ const SettingsPage: React.FC = () => {
|
||||
<Grid container spacing={4}>
|
||||
{/* General Settings - Only show visitor mode toggle when visitor mode is enabled */}
|
||||
<Grid size={12}>
|
||||
<GeneralSettings
|
||||
language={settings.language}
|
||||
websiteName={settings.websiteName}
|
||||
itemsPerPage={settings.itemsPerPage}
|
||||
showYoutubeSearch={settings.showYoutubeSearch}
|
||||
visitorMode={settings.visitorMode}
|
||||
savedVisitorMode={settingsData?.visitorMode}
|
||||
infiniteScroll={settings.infiniteScroll}
|
||||
videoColumns={settings.videoColumns}
|
||||
<CollapsibleSection title={t('general')} defaultExpanded={true}>
|
||||
<GeneralSettings
|
||||
language={settings.language}
|
||||
websiteName={settings.websiteName}
|
||||
itemsPerPage={settings.itemsPerPage}
|
||||
showYoutubeSearch={settings.showYoutubeSearch}
|
||||
visitorMode={settings.visitorMode}
|
||||
savedVisitorMode={settingsData?.visitorMode}
|
||||
infiniteScroll={settings.infiniteScroll}
|
||||
videoColumns={settings.videoColumns}
|
||||
|
||||
onChange={(field, value) => handleChange(field as keyof Settings, value)}
|
||||
/>
|
||||
onChange={(field, value) => handleChange(field as keyof Settings, value)}
|
||||
/>
|
||||
</CollapsibleSection>
|
||||
</Grid>
|
||||
|
||||
<Grid size={12}><Divider /></Grid>
|
||||
|
||||
{/* Cloudflare Settings */}
|
||||
<Grid size={12}>
|
||||
<CloudflareSettings
|
||||
enabled={settings.cloudflaredTunnelEnabled}
|
||||
token={settings.cloudflaredToken}
|
||||
visitorMode={visitorMode}
|
||||
onChange={(field, value) => handleChange(field as keyof Settings, value)}
|
||||
/>
|
||||
<CollapsibleSection title={t('cloudflaredTunnel')} defaultExpanded={false}>
|
||||
<CloudflareSettings
|
||||
enabled={settings.cloudflaredTunnelEnabled}
|
||||
token={settings.cloudflaredToken}
|
||||
visitorMode={visitorMode}
|
||||
onChange={(field, value) => handleChange(field as keyof Settings, value)}
|
||||
/>
|
||||
</CollapsibleSection>
|
||||
</Grid>
|
||||
|
||||
{!visitorMode && (
|
||||
<>
|
||||
<Grid size={12}><Divider /></Grid>
|
||||
|
||||
{/* Cookie Upload Settings */}
|
||||
<Grid size={12}>
|
||||
<CookieSettings
|
||||
onSuccess={(msg) => setMessage({ text: msg, type: 'success' })}
|
||||
onError={(msg) => setMessage({ text: msg, type: 'error' })}
|
||||
/>
|
||||
<CollapsibleSection title={t('cookieSettings') || 'Cookie Settings'} defaultExpanded={false}>
|
||||
<CookieSettings
|
||||
onSuccess={(msg) => setMessage({ text: msg, type: 'success' })}
|
||||
onError={(msg) => setMessage({ text: msg, type: 'error' })}
|
||||
/>
|
||||
</CollapsibleSection>
|
||||
</Grid>
|
||||
|
||||
<Grid size={12}><Divider /></Grid>
|
||||
|
||||
{/* Security Settings */}
|
||||
<Grid size={12}>
|
||||
<SecuritySettings
|
||||
settings={settings}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<CollapsibleSection title={t('security')} defaultExpanded={false}>
|
||||
<SecuritySettings
|
||||
settings={settings}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</CollapsibleSection>
|
||||
</Grid>
|
||||
|
||||
<Grid size={12}><Divider /></Grid>
|
||||
|
||||
{/* Video Defaults */}
|
||||
<Grid size={12}>
|
||||
<VideoDefaultSettings
|
||||
settings={settings}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<CollapsibleSection title={t('videoDefaults')} defaultExpanded={false}>
|
||||
<VideoDefaultSettings
|
||||
settings={settings}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</CollapsibleSection>
|
||||
</Grid>
|
||||
|
||||
<Grid size={12}><Divider /></Grid>
|
||||
|
||||
{/* Tags Management */}
|
||||
<Grid size={12}>
|
||||
<TagsSettings
|
||||
tags={Array.isArray(settings.tags) ? settings.tags : []}
|
||||
onTagsChange={handleTagsChange}
|
||||
/>
|
||||
<CollapsibleSection title={t('tagsManagement') || 'Tags Management'} defaultExpanded={false}>
|
||||
<TagsSettings
|
||||
tags={Array.isArray(settings.tags) ? settings.tags : []}
|
||||
onTagsChange={handleTagsChange}
|
||||
/>
|
||||
</CollapsibleSection>
|
||||
</Grid>
|
||||
|
||||
<Grid size={12}><Divider /></Grid>
|
||||
|
||||
{/* Download Settings */}
|
||||
<Grid size={12}>
|
||||
<DownloadSettings
|
||||
settings={settings}
|
||||
onChange={handleChange}
|
||||
activeDownloadsCount={activeDownloads.length}
|
||||
onCleanup={() => setShowCleanupTempFilesModal(true)}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
<CollapsibleSection title={t('downloadSettings')} defaultExpanded={false}>
|
||||
<DownloadSettings
|
||||
settings={settings}
|
||||
onChange={handleChange}
|
||||
activeDownloadsCount={activeDownloads.length}
|
||||
onCleanup={() => setShowCleanupTempFilesModal(true)}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
</CollapsibleSection>
|
||||
</Grid>
|
||||
|
||||
<Grid size={12}><Divider /></Grid>
|
||||
|
||||
{/* Cloud Drive Settings */}
|
||||
<Grid size={12}>
|
||||
<CloudDriveSettings
|
||||
settings={settings}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<CollapsibleSection title={t('cloudDriveSettings')} defaultExpanded={false}>
|
||||
<CloudDriveSettings
|
||||
settings={settings}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</CollapsibleSection>
|
||||
</Grid>
|
||||
|
||||
<Grid size={12}><Divider /></Grid>
|
||||
|
||||
{/* Database Settings */}
|
||||
<Grid size={12}>
|
||||
<DatabaseSettings
|
||||
onMigrate={() => setShowMigrateConfirmModal(true)}
|
||||
onDeleteLegacy={() => setShowDeleteLegacyModal(true)}
|
||||
onFormatFilenames={() => setShowFormatConfirmModal(true)}
|
||||
onExportDatabase={handleExportDatabase}
|
||||
onImportDatabase={handleImportDatabase}
|
||||
onCleanupBackupDatabases={handleCleanupBackupDatabases}
|
||||
onRestoreFromLastBackup={handleRestoreFromLastBackup}
|
||||
isSaving={isSaving}
|
||||
lastBackupInfo={lastBackupInfo}
|
||||
moveSubtitlesToVideoFolder={settings.moveSubtitlesToVideoFolder || false}
|
||||
onMoveSubtitlesToVideoFolderChange={(checked) => handleChange('moveSubtitlesToVideoFolder', checked)}
|
||||
moveThumbnailsToVideoFolder={settings.moveThumbnailsToVideoFolder || false}
|
||||
onMoveThumbnailsToVideoFolderChange={(checked) => handleChange('moveThumbnailsToVideoFolder', checked)}
|
||||
/>
|
||||
<CollapsibleSection title={t('database')} defaultExpanded={false}>
|
||||
<DatabaseSettings
|
||||
onMigrate={() => setShowMigrateConfirmModal(true)}
|
||||
onDeleteLegacy={() => setShowDeleteLegacyModal(true)}
|
||||
onFormatFilenames={() => setShowFormatConfirmModal(true)}
|
||||
onExportDatabase={handleExportDatabase}
|
||||
onImportDatabase={handleImportDatabase}
|
||||
onCleanupBackupDatabases={handleCleanupBackupDatabases}
|
||||
onRestoreFromLastBackup={handleRestoreFromLastBackup}
|
||||
isSaving={isSaving}
|
||||
lastBackupInfo={lastBackupInfo}
|
||||
moveSubtitlesToVideoFolder={settings.moveSubtitlesToVideoFolder || false}
|
||||
onMoveSubtitlesToVideoFolderChange={(checked) => handleChange('moveSubtitlesToVideoFolder', checked)}
|
||||
moveThumbnailsToVideoFolder={settings.moveThumbnailsToVideoFolder || false}
|
||||
onMoveThumbnailsToVideoFolderChange={(checked) => handleChange('moveThumbnailsToVideoFolder', checked)}
|
||||
/>
|
||||
</CollapsibleSection>
|
||||
</Grid>
|
||||
|
||||
<Grid size={12}><Divider /></Grid>
|
||||
|
||||
{/* yt-dlp Configuration */}
|
||||
<Grid size={12}>
|
||||
<YtDlpSettings
|
||||
config={settings.ytDlpConfig || ''}
|
||||
proxyOnlyYoutube={settings.proxyOnlyYoutube || false}
|
||||
onChange={(config) => handleChange('ytDlpConfig', config)}
|
||||
onProxyOnlyYoutubeChange={(checked) => handleChange('proxyOnlyYoutube', checked)}
|
||||
/>
|
||||
<CollapsibleSection title={t('ytDlpConfiguration') || 'yt-dlp Configuration'} defaultExpanded={false}>
|
||||
<YtDlpSettings
|
||||
config={settings.ytDlpConfig || ''}
|
||||
proxyOnlyYoutube={settings.proxyOnlyYoutube || false}
|
||||
onChange={(config) => handleChange('ytDlpConfig', config)}
|
||||
onProxyOnlyYoutubeChange={(checked) => handleChange('proxyOnlyYoutube', checked)}
|
||||
/>
|
||||
</CollapsibleSection>
|
||||
</Grid>
|
||||
|
||||
<Grid size={12}><Divider /></Grid>
|
||||
|
||||
{/* Advanced Settings */}
|
||||
<Grid size={12}>
|
||||
<AdvancedSettings
|
||||
debugMode={debugMode}
|
||||
onDebugModeChange={setDebugMode}
|
||||
/>
|
||||
<CollapsibleSection title={t('debugMode') || 'Advanced Settings'} defaultExpanded={false}>
|
||||
<AdvancedSettings
|
||||
debugMode={debugMode}
|
||||
onDebugModeChange={setDebugMode}
|
||||
/>
|
||||
</CollapsibleSection>
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user