feat: Add isVisitor check to BasicSettings and useVideoProgress
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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')}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 */}
|
||||||
|
|||||||
Reference in New Issue
Block a user