feat: Add isVisitor check to BasicSettings and useVideoProgress

This commit is contained in:
Peifan Li
2026-01-04 23:35:45 -05:00
parent 91d53f04a4
commit 3e44960ce7
4 changed files with 153 additions and 149 deletions

View File

@@ -1,6 +1,12 @@
# Change Log # Change Log
## v1.7.34 (2026-01-04)
### Feat
- feat: Add refetchOnMount option to DownloadProvider (91d53f0)
## v1.7.33 (2026-01-04) ## v1.7.33 (2026-01-04)
### Feat ### Feat

View File

@@ -12,6 +12,7 @@ interface BasicSettingsProps {
const BasicSettings: React.FC<BasicSettingsProps> = ({ language, websiteName, onChange }) => { const BasicSettings: React.FC<BasicSettingsProps> = ({ language, websiteName, onChange }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const { userRole } = useAuth(); const { userRole } = useAuth();
const isVisitor = userRole === 'visitor';
return ( return (
<Box> <Box>
@@ -38,7 +39,7 @@ const BasicSettings: React.FC<BasicSettingsProps> = ({ language, websiteName, on
</Select> </Select>
</FormControl> </FormControl>
{userRole !== 'visitor' && ( {!isVisitor && (
<TextField <TextField
fullWidth fullWidth
label={t('websiteName')} label={t('websiteName')}

View File

@@ -16,6 +16,7 @@ interface UseVideoProgressProps {
*/ */
export function useVideoProgress({ videoId, video }: UseVideoProgressProps) { export function useVideoProgress({ videoId, video }: UseVideoProgressProps) {
const { userRole } = useAuth(); const { userRole } = useAuth();
const isVisitor = userRole === 'visitor';
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [hasViewed, setHasViewed] = useState<boolean>(false); const [hasViewed, setHasViewed] = useState<boolean>(false);
const lastProgressSave = useRef<number>(0); const lastProgressSave = useRef<number>(0);
@@ -31,7 +32,7 @@ export function useVideoProgress({ videoId, video }: UseVideoProgressProps) {
// Save progress on unmount // Save progress on unmount
useEffect(() => { useEffect(() => {
return () => { return () => {
if (videoId && currentTimeRef.current > 0 && !isDeletingRef.current && userRole !== 'visitor') { if (videoId && currentTimeRef.current > 0 && !isDeletingRef.current && !isVisitor) {
axios.put(`${API_URL}/videos/${videoId}/progress`, { axios.put(`${API_URL}/videos/${videoId}/progress`, {
progress: Math.floor(currentTimeRef.current) progress: Math.floor(currentTimeRef.current)
}) })
@@ -44,7 +45,7 @@ export function useVideoProgress({ videoId, video }: UseVideoProgressProps) {
currentTimeRef.current = currentTime; currentTimeRef.current = currentTime;
// Increment view count after 10 seconds // Increment view count after 10 seconds
if (currentTime > 10 && !hasViewed && videoId && userRole !== 'visitor') { if (currentTime > 10 && !hasViewed && videoId && !isVisitor) {
setHasViewed(true); setHasViewed(true);
axios.post(`${API_URL}/videos/${videoId}/view`) axios.post(`${API_URL}/videos/${videoId}/view`)
.then(res => { .then(res => {
@@ -59,7 +60,7 @@ export function useVideoProgress({ videoId, video }: UseVideoProgressProps) {
// Save progress every 5 seconds // Save progress every 5 seconds
const now = Date.now(); const now = Date.now();
if (now - lastProgressSave.current > 5000 && videoId && userRole !== 'visitor') { if (now - lastProgressSave.current > 5000 && videoId && !isVisitor) {
lastProgressSave.current = now; lastProgressSave.current = now;
axios.put(`${API_URL}/videos/${videoId}/progress`, { axios.put(`${API_URL}/videos/${videoId}/progress`, {
progress: Math.floor(currentTime) progress: Math.floor(currentTime)

View File

@@ -3,8 +3,6 @@ import {
Alert, Alert,
Box, Box,
Button, Button,
Card,
CardContent,
Container, Container,
Grid, Grid,
Snackbar, Snackbar,
@@ -172,161 +170,159 @@ const SettingsPage: React.FC = () => {
</Box> </Box>
{/* Settings Card */} {/* Settings Card */}
<Card variant="outlined">
<CardContent> <Grid container spacing={2}>
<Grid container spacing={2}> {/* 1. Basic Settings */}
{/* 1. Basic Settings */} <Grid size={12}>
<CollapsibleSection title={t('basicSettings')} defaultExpanded={true}>
<BasicSettings
language={settings.language}
websiteName={settings.websiteName}
onChange={(field, value) => handleChange(field as keyof Settings, value)}
/>
</CollapsibleSection>
</Grid>
{/* 2. Interface & Display */}
{!isVisitor && (
<Grid size={12}>
<CollapsibleSection title={t('interfaceDisplay')} defaultExpanded={false}>
<InterfaceDisplaySettings
itemsPerPage={settings.itemsPerPage}
showYoutubeSearch={settings.showYoutubeSearch}
infiniteScroll={settings.infiniteScroll}
videoColumns={settings.videoColumns}
onChange={(field, value) => handleChange(field as keyof Settings, value)}
/>
</CollapsibleSection>
</Grid>
)}
{/* 3. Security & Access */}
{!isVisitor && (
<Grid size={12}>
<CollapsibleSection title={t('securityAccess')} defaultExpanded={false}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
<Box>
<SecuritySettings
settings={settings}
onChange={handleChange}
/>
</Box>
<Box>
<CookieSettings
onSuccess={(msg) => setMessage({ text: msg, type: 'success' })}
onError={(msg) => setMessage({ text: msg, type: 'error' })}
/>
</Box>
<Box>
<CloudflareSettings
enabled={settings.cloudflaredTunnelEnabled}
token={settings.cloudflaredToken}
onChange={(field, value) => handleChange(field as keyof Settings, value)}
/>
</Box>
</Box>
</CollapsibleSection>
</Grid>
)}
{!isVisitor && (
<>
{/* 4. Video Playback */}
<Grid size={12}> <Grid size={12}>
<CollapsibleSection title={t('basicSettings')} defaultExpanded={true}> <CollapsibleSection title={t('videoPlayback')} defaultExpanded={false}>
<BasicSettings <VideoDefaultSettings
language={settings.language} settings={settings}
websiteName={settings.websiteName} onChange={handleChange}
onChange={(field, value) => handleChange(field as keyof Settings, value)}
/> />
</CollapsibleSection> </CollapsibleSection>
</Grid> </Grid>
{/* 2. Interface & Display */} {/* 5. Download & Storage */}
{!isVisitor && ( <Grid size={12}>
<Grid size={12}> <CollapsibleSection title={t('downloadStorage')} defaultExpanded={false}>
<CollapsibleSection title={t('interfaceDisplay')} defaultExpanded={false}> <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
<InterfaceDisplaySettings <Box>
itemsPerPage={settings.itemsPerPage} <Typography variant="h6" gutterBottom>{t('downloadSettings')}</Typography>
showYoutubeSearch={settings.showYoutubeSearch} <DownloadSettings
infiniteScroll={settings.infiniteScroll} settings={settings}
videoColumns={settings.videoColumns} onChange={handleChange}
onChange={(field, value) => handleChange(field as keyof Settings, value)} activeDownloadsCount={activeDownloads.length}
/> onCleanup={() => setShowCleanupTempFilesModal(true)}
</CollapsibleSection> isSaving={isSaving}
</Grid> />
)}
{/* 3. Security & Access */}
{!isVisitor && userRole !== 'visitor' && (
<Grid size={12}>
<CollapsibleSection title={t('securityAccess')} defaultExpanded={false}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
<Box>
<SecuritySettings
settings={settings}
onChange={handleChange}
/>
</Box>
<Box>
<CookieSettings
onSuccess={(msg) => setMessage({ text: msg, type: 'success' })}
onError={(msg) => setMessage({ text: msg, type: 'error' })}
/>
</Box>
<Box>
<CloudflareSettings
enabled={settings.cloudflaredTunnelEnabled}
token={settings.cloudflaredToken}
onChange={(field, value) => handleChange(field as keyof Settings, value)}
/>
</Box>
</Box> </Box>
</CollapsibleSection> <Box>
</Grid> <Typography variant="h6" gutterBottom sx={{ mt: 2 }}>{t('cloudDriveSettings')}</Typography>
)} <CloudDriveSettings
{!isVisitor && (
<>
{/* 4. Video Playback */}
<Grid size={12}>
<CollapsibleSection title={t('videoPlayback')} defaultExpanded={false}>
<VideoDefaultSettings
settings={settings} settings={settings}
onChange={handleChange} onChange={handleChange}
/> />
</CollapsibleSection> </Box>
</Grid> <Box>
<Typography variant="h6" gutterBottom sx={{ mt: 2 }}>{t('ytDlpConfiguration') || 'yt-dlp Configuration'}</Typography>
{/* 5. Download & Storage */} <YtDlpSettings
<Grid size={12}> config={settings.ytDlpConfig || ''}
<CollapsibleSection title={t('downloadStorage')} defaultExpanded={false}> proxyOnlyYoutube={settings.proxyOnlyYoutube || false}
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}> onChange={(config) => handleChange('ytDlpConfig', config)}
<Box> onProxyOnlyYoutubeChange={(checked) => handleChange('proxyOnlyYoutube', checked)}
<Typography variant="h6" gutterBottom>{t('downloadSettings')}</Typography>
<DownloadSettings
settings={settings}
onChange={handleChange}
activeDownloadsCount={activeDownloads.length}
onCleanup={() => setShowCleanupTempFilesModal(true)}
isSaving={isSaving}
/>
</Box>
<Box>
<Typography variant="h6" gutterBottom sx={{ mt: 2 }}>{t('cloudDriveSettings')}</Typography>
<CloudDriveSettings
settings={settings}
onChange={handleChange}
/>
</Box>
<Box>
<Typography variant="h6" gutterBottom sx={{ mt: 2 }}>{t('ytDlpConfiguration') || 'yt-dlp Configuration'}</Typography>
<YtDlpSettings
config={settings.ytDlpConfig || ''}
proxyOnlyYoutube={settings.proxyOnlyYoutube || false}
onChange={(config) => handleChange('ytDlpConfig', config)}
onProxyOnlyYoutubeChange={(checked) => handleChange('proxyOnlyYoutube', checked)}
/>
</Box>
</Box>
</CollapsibleSection>
</Grid>
{/* 6. Content Management */}
<Grid size={12}>
<CollapsibleSection title={t('contentManagement')} defaultExpanded={false}>
<TagsSettings
tags={Array.isArray(settings.tags) ? settings.tags : []}
onTagsChange={handleTagsChange}
/> />
</CollapsibleSection> </Box>
</Grid> </Box>
</CollapsibleSection>
</Grid>
{/* 7. Data Management */} {/* 6. Content Management */}
<Grid size={12}> <Grid size={12}>
<CollapsibleSection title={t('dataManagement')} defaultExpanded={false}> <CollapsibleSection title={t('contentManagement')} defaultExpanded={false}>
<DatabaseSettings <TagsSettings
onMigrate={() => setShowMigrateConfirmModal(true)} tags={Array.isArray(settings.tags) ? settings.tags : []}
onDeleteLegacy={() => setShowDeleteLegacyModal(true)} onTagsChange={handleTagsChange}
onFormatFilenames={() => setShowFormatConfirmModal(true)} />
onExportDatabase={handleExportDatabase} </CollapsibleSection>
onImportDatabase={handleImportDatabase} </Grid>
onCleanupBackupDatabases={handleCleanupBackupDatabases}
onRestoreFromLastBackup={handleRestoreFromLastBackup} {/* 7. Data Management */}
isSaving={isSaving} <Grid size={12}>
lastBackupInfo={lastBackupInfo} <CollapsibleSection title={t('dataManagement')} defaultExpanded={false}>
moveSubtitlesToVideoFolder={settings.moveSubtitlesToVideoFolder || false} <DatabaseSettings
onMoveSubtitlesToVideoFolderChange={(checked) => handleChange('moveSubtitlesToVideoFolder', checked)} onMigrate={() => setShowMigrateConfirmModal(true)}
moveThumbnailsToVideoFolder={settings.moveThumbnailsToVideoFolder || false} onDeleteLegacy={() => setShowDeleteLegacyModal(true)}
onMoveThumbnailsToVideoFolderChange={(checked) => handleChange('moveThumbnailsToVideoFolder', checked)} onFormatFilenames={() => setShowFormatConfirmModal(true)}
/> onExportDatabase={handleExportDatabase}
</CollapsibleSection> onImportDatabase={handleImportDatabase}
</Grid> 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>
{/* 8. Advanced */}
<Grid size={12}>
<CollapsibleSection title={t('advanced')} defaultExpanded={false}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
<AdvancedSettings
debugMode={debugMode}
onDebugModeChange={setDebugMode}
/>
<HookSettings
settings={settings}
onChange={handleChange}
/>
</Box>
</CollapsibleSection>
</Grid>
</>
)}
</Grid>
{/* 8. Advanced */}
<Grid size={12}>
<CollapsibleSection title={t('advanced')} defaultExpanded={false}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
<AdvancedSettings
debugMode={debugMode}
onDebugModeChange={setDebugMode}
/>
<HookSettings
settings={settings}
onChange={handleChange}
/>
</Box>
</CollapsibleSection>
</Grid>
</>
)}
</Grid>
</CardContent>
</Card>
{/* Save Button */} {/* Save Button */}
{/* Save Button Placeholder & Logic */} {/* Save Button Placeholder & Logic */}