diff --git a/app/client/src/common/darkTheme.js b/app/client/src/common/darkTheme.js index ce7cea1b..5109bc44 100644 --- a/app/client/src/common/darkTheme.js +++ b/app/client/src/common/darkTheme.js @@ -183,7 +183,7 @@ const theme = { }, '&.MuiTypography-body1 > svg': { marginTop: 2, - }, + }, '& svg:last-child': { marginLeft: 2, }, diff --git a/app/client/src/common/utils.js b/app/client/src/common/utils.js index 8c53daac..075f1182 100644 --- a/app/client/src/common/utils.js +++ b/app/client/src/common/utils.js @@ -213,9 +213,9 @@ export const getVideoSources = (videoId, videoInfo, extension) => { // When a cropped version exists, point "Source" at the cropped file instead of the original const sourceUrl = hasCrop - ? (SERVED_BY === 'nginx' - ? `${URL}/_content/derived/${videoId}/${videoId}-cropped.mp4` - : `${URL}/api/video?id=${videoId}&quality=cropped`) + ? SERVED_BY === 'nginx' + ? `${URL}/_content/derived/${videoId}/${videoId}-cropped.mp4` + : `${URL}/api/video?id=${videoId}&quality=cropped` : getVideoUrl(videoId, 'original', extension) sources.push({ diff --git a/app/client/src/components/admin/ImageFileManager.js b/app/client/src/components/admin/ImageFileManager.js index 8440ad82..e1944bdb 100644 --- a/app/client/src/components/admin/ImageFileManager.js +++ b/app/client/src/components/admin/ImageFileManager.js @@ -164,7 +164,10 @@ const ImageFileRow = React.memo(function ImageFileRow({ file, isSelected, onTogg { e.stopPropagation(); onToggle(file.image_id) }} + onClick={(e) => { + e.stopPropagation() + onToggle(file.image_id) + }} > - + {displayName} { e.stopPropagation(); window.open(`/i/${file.image_id}`, '_blank') }} + onClick={(e) => { + e.stopPropagation() + window.open(`/i/${file.image_id}`, '_blank') + }} sx={{ color: '#FFFFFF33', p: 0.25, flexShrink: 0, '&:hover': { color: '#FFFFFF99' } }} > @@ -196,7 +211,19 @@ const ImageFileRow = React.memo(function ImageFileRow({ file, isSelected, onTogg {/* Size */} - 0 ? Derived: {formatSize(file.derived_size)} : ''}> + 0 ? ( + + Derived: {formatSize(file.derived_size)} + + ) : ( + '' + ) + } + > {formatSize(file.size)} @@ -204,7 +231,9 @@ const ImageFileRow = React.memo(function ImageFileRow({ file, isSelected, onTogg {/* Total Size */} {!hiddenColumns.has('Total Size') && ( - {formatSize((file.size || 0) + (file.derived_size || 0))} + + {formatSize((file.size || 0) + (file.derived_size || 0))} + )} @@ -212,7 +241,17 @@ const ImageFileRow = React.memo(function ImageFileRow({ file, isSelected, onTogg {!hiddenColumns.has('Resolution') && ( - + )} @@ -225,7 +264,8 @@ const ImageFileRow = React.memo(function ImageFileRow({ file, isSelected, onTogg label={file.private ? 'Private' : 'Public'} size="small" sx={{ - height: 17, fontSize: 10, + height: 17, + fontSize: 10, bgcolor: file.private ? '#FFFFFF12' : '#1DB95418', color: file.private ? '#FFFFFF66' : '#1DB954', border: '1px solid', diff --git a/app/client/src/components/cards/ImageCards.js b/app/client/src/components/cards/ImageCards.js index 445c34a2..0df3b787 100644 --- a/app/client/src/components/cards/ImageCards.js +++ b/app/client/src/components/cards/ImageCards.js @@ -107,126 +107,128 @@ const LazyImageCard = ({ ) } -const ImageCards = React.memo(({ - images, - loadingIcon = null, - feedView = false, - authenticated, - size, - onImageOpen, - editMode = false, - selectedImages, - onImageSelect, -}) => { - const [imgs, setImages] = React.useState(images || []) - const [alert, setAlert] = React.useState({ open: false }) - const [columnCount, setColumnCount] = React.useState(3) - const containerRef = React.useRef() - - React.useEffect(() => { - setImages(images || []) - }, [images]) - - React.useEffect(() => { - if (!imgs || imgs.length === 0) { - setColumnCount(3) - return - } - - const el = containerRef.current - if (!el) return - - const observer = new ResizeObserver(([entry]) => { - const width = entry?.contentRect?.width || 0 - if (!width) return - const colWidth = size || 300 - const cols = Math.max(1, Math.floor((width + 16) / (colWidth + 16))) - setColumnCount(cols) - }) - - observer.observe(el) - return () => observer.disconnect() - }, [size, imgs]) +const ImageCards = React.memo( + ({ + images, + loadingIcon = null, + feedView = false, + authenticated, + size, + onImageOpen, + editMode = false, + selectedImages, + onImageSelect, + }) => { + const [imgs, setImages] = React.useState(images || []) + const [alert, setAlert] = React.useState({ open: false }) + const [columnCount, setColumnCount] = React.useState(3) + const containerRef = React.useRef() + + React.useEffect(() => { + setImages(images || []) + }, [images]) + + React.useEffect(() => { + if (!imgs || imgs.length === 0) { + setColumnCount(3) + return + } + + const el = containerRef.current + if (!el) return + + const observer = new ResizeObserver(([entry]) => { + const width = entry?.contentRect?.width || 0 + if (!width) return + const colWidth = size || 300 + const cols = Math.max(1, Math.floor((width + 16) / (colWidth + 16))) + setColumnCount(cols) + }) - const memoizedHandleAlert = useCallback((a) => setAlert(a), []) + observer.observe(el) + return () => observer.disconnect() + }, [size, imgs]) - const handleDelete = (id) => { - setImages((prev) => prev.filter((img) => img.image_id !== id)) - } + const memoizedHandleAlert = useCallback((a) => setAlert(a), []) - const openImage = (img) => { - if (onImageOpen) onImageOpen(img) - } + const handleDelete = (id) => { + setImages((prev) => prev.filter((img) => img.image_id !== id)) + } - const EMPTY_STATE = () => ( - - {!loadingIcon && ( - <> - - - No images found - {!feedView && ( - - Upload images or scan your image library - - )} - - - )} - {loadingIcon} - - ) + const openImage = (img) => { + if (onImageOpen) onImageOpen(img) + } - return ( - - setAlert({ ...alert, open })} + const EMPTY_STATE = () => ( + - {alert.message} - - - {imgs.length === 0 && EMPTY_STATE()} - {imgs.length > 0 && ( - + + + No images found + {!feedView && ( + + Upload images or scan your image library + + )} + + + )} + {loadingIcon} + + ) + + return ( + + setAlert({ ...alert, open })} > - {imgs.map((img) => ( - (editMode ? onImageSelect?.(img.image_id) : openImage(img))} - alertHandler={memoizedHandleAlert} - authenticated={authenticated} - onRemoveFromView={handleDelete} - editMode={editMode} - selected={selectedImages?.has(img.image_id)} - onSelect={onImageSelect} - /> - ))} - - )} - - ) -}) + {alert.message} + + + {imgs.length === 0 && EMPTY_STATE()} + {imgs.length > 0 && ( + + {imgs.map((img) => ( + (editMode ? onImageSelect?.(img.image_id) : openImage(img))} + alertHandler={memoizedHandleAlert} + authenticated={authenticated} + onRemoveFromView={handleDelete} + editMode={editMode} + selected={selectedImages?.has(img.image_id)} + onSelect={onImageSelect} + /> + ))} + + )} + + ) + }, +) export default ImageCards diff --git a/app/client/src/components/cards/VideoCards.js b/app/client/src/components/cards/VideoCards.js index b9a9a730..28663137 100644 --- a/app/client/src/components/cards/VideoCards.js +++ b/app/client/src/components/cards/VideoCards.js @@ -131,13 +131,9 @@ const VideoCards = ({ <> - - No videos found - + No videos found {!feedView && ( - - Scan your library to discover videos - + Scan your library to discover videos )} {!feedView && ( @@ -200,7 +196,7 @@ const VideoCards = ({ width: isSingleColumn ? 'calc(100% + 48px)' : '100%', mx: isSingleColumn ? '-24px' : 0, gridTemplateColumns: `repeat(auto-fill, minmax(min(100%, ${size}px), 1fr))`, - gap: '24px', + gap: 2, }} > {vids.slice(0, visibleCount).map((v, index) => ( diff --git a/app/client/src/components/game/GameSearch.js b/app/client/src/components/game/GameSearch.js index a27569fd..f4328eef 100644 --- a/app/client/src/components/game/GameSearch.js +++ b/app/client/src/components/game/GameSearch.js @@ -6,7 +6,14 @@ import { GameService } from '../../services' * Reusable game search autocomplete component * Searches SteamGridDB, creates game if needed, and calls onGameLinked callback */ -const GameSearch = ({ onGameLinked, onError, onWarning, disabled = false, placeholder = 'Search for a game...', sx = {} }) => { +const GameSearch = ({ + onGameLinked, + onError, + onWarning, + disabled = false, + placeholder = 'Search for a game...', + sx = {}, +}) => { const [selectedGame, setSelectedGame] = React.useState(null) const [gameOptions, setGameOptions] = React.useState([]) const [gameSearchLoading, setGameSearchLoading] = React.useState(false) @@ -60,9 +67,7 @@ const GameSearch = ({ onGameLinked, onError, onWarning, disabled = false, placeh const labels = { heroes: 'hero art', logos: 'logo', icons: 'icon' } const missing = created.missing_assets.map((k) => labels[k] || k) const missingStr = - missing.length > 1 - ? missing.slice(0, -1).join(', ') + ' and ' + missing[missing.length - 1] - : missing[0] + missing.length > 1 ? missing.slice(0, -1).join(', ') + ' and ' + missing[missing.length - 1] : missing[0] const verb = missing.length > 1 ? 'were' : 'was' pendingWarning = `No ${missingStr} ${verb} available on SteamGridDB.` } diff --git a/app/client/src/components/game/GameVideosHeader.js b/app/client/src/components/game/GameVideosHeader.js index fe6ff4c2..cbc5ba79 100644 --- a/app/client/src/components/game/GameVideosHeader.js +++ b/app/client/src/components/game/GameVideosHeader.js @@ -17,7 +17,7 @@ const GameVideosHeader = ({ game, height = 200, editMode, onEditAssets }) => { width: '100%', height, overflow: 'hidden', - mb: 3, + mb: 2, }} > {bgUrl && ( diff --git a/app/client/src/components/misc/LoadingSpinner.js b/app/client/src/components/misc/LoadingSpinner.js index 5d8de181..63a03f63 100644 --- a/app/client/src/components/misc/LoadingSpinner.js +++ b/app/client/src/components/misc/LoadingSpinner.js @@ -37,7 +37,15 @@ function LoadingProgress({ size, ...rest }) { export default function LoadingSpinner({ size }) { return ( - + ) diff --git a/app/client/src/components/misc/VideoJSPlayer.js b/app/client/src/components/misc/VideoJSPlayer.js index ee506c41..868f68cd 100644 --- a/app/client/src/components/misc/VideoJSPlayer.js +++ b/app/client/src/components/misc/VideoJSPlayer.js @@ -52,7 +52,9 @@ function PlayerEffects({ sources, onSourceChange, onTimeUpdate, onReady, startTi paused: () => mediaRef.current?.paused ?? true, play: () => store.play?.(), pause: () => store.pause?.(), - seek: (time) => { if (mediaRef.current) mediaRef.current.currentTime = time }, + seek: (time) => { + if (mediaRef.current) mediaRef.current.currentTime = time + }, el: () => mediaRef.current, }), [store], diff --git a/app/client/src/components/modal/DeleteVideoModal.js b/app/client/src/components/modal/DeleteVideoModal.js index 8c010f1e..29724b9a 100644 --- a/app/client/src/components/modal/DeleteVideoModal.js +++ b/app/client/src/components/modal/DeleteVideoModal.js @@ -46,7 +46,8 @@ const DeleteVideoModal = ({ open, onClose, videoId, videoIds, alertHandler }) => {isBulk ? `Permanently delete ${count} video${count > 1 ? 's' : ''}?` : 'Permanently delete this video?'} - Deleting this clip will also remove all related data, including thumbnails, transcoded versions, and any edits to its title or date. Are you sure? + Deleting this clip will also remove all related data, including thumbnails, transcoded versions, and any edits + to its title or date. Are you sure? diff --git a/app/client/src/components/modal/EditImageModal.js b/app/client/src/components/modal/EditImageModal.js index 04ae0001..1ebe8eac 100644 --- a/app/client/src/components/modal/EditImageModal.js +++ b/app/client/src/components/modal/EditImageModal.js @@ -269,7 +269,13 @@ const EditImageModal = ({ open, onClose, image, alertHandler, authenticated, onN > - + - + )} diff --git a/app/client/src/components/modal/MoveVideoModal.js b/app/client/src/components/modal/MoveVideoModal.js index a25af274..530f0fc0 100644 --- a/app/client/src/components/modal/MoveVideoModal.js +++ b/app/client/src/components/modal/MoveVideoModal.js @@ -20,11 +20,7 @@ const MoveVideoModal = ({ open, onClose, video, alertHandler }) => { VideoService.getUploadFolders() .then((res) => { const all = res.data?.folders || [] - setFolders( - all - .filter((f) => f !== currentFolder) - .map((f) => ({ value: f, label: `/videos/${f}/` })), - ) + setFolders(all.filter((f) => f !== currentFolder).map((f) => ({ value: f, label: `/videos/${f}/` }))) }) .catch(() => setFolders([])) }, [open, currentFolder]) diff --git a/app/client/src/components/modal/UpdateDetailsModal.js b/app/client/src/components/modal/UpdateDetailsModal.js index 70c67995..64f5e430 100644 --- a/app/client/src/components/modal/UpdateDetailsModal.js +++ b/app/client/src/components/modal/UpdateDetailsModal.js @@ -397,7 +397,6 @@ const UpdateDetailsModal = ({ inputSx={inputSx} /> - diff --git a/app/client/src/components/nav/FolderSuggestionInline.js b/app/client/src/components/nav/FolderSuggestionInline.js index a409e3f2..218f8fb8 100644 --- a/app/client/src/components/nav/FolderSuggestionInline.js +++ b/app/client/src/components/nav/FolderSuggestionInline.js @@ -16,7 +16,7 @@ const FolderSuggestionInline = ({ open, suggestion, folderName, onApplied, onDis setLoading(true) try { const gamesRes = await GameService.getGames() - let game = gamesRes.data.find(g => g.steamgriddb_id === suggestion.steamgriddb_id) + let game = gamesRes.data.find((g) => g.steamgriddb_id === suggestion.steamgriddb_id) if (!game) { const createRes = await GameService.createGame({ @@ -27,9 +27,7 @@ const FolderSuggestionInline = ({ open, suggestion, folderName, onApplied, onDis game = createRes.data } - const linkPromises = suggestion.video_ids.map(videoId => - GameService.linkVideoToGame(videoId, game.id) - ) + const linkPromises = suggestion.video_ids.map((videoId) => GameService.linkVideoToGame(videoId, game.id)) await Promise.all(linkPromises) // Create folder rule for auto-tagging future videos @@ -141,11 +139,7 @@ const FolderSuggestionInline = ({ open, suggestion, folderName, onApplied, onDis // Collapsed view return ( - + { @@ -87,9 +87,8 @@ const GameScanStatus = ({ open, onComplete, authenticated }) => { ) } - const tooltipText = scanStatus.total === 0 - ? 'Preparing scan...' - : `Scanning: ${scanStatus.current}/${scanStatus.total}` + const tooltipText = + scanStatus.total === 0 ? 'Preparing scan...' : `Scanning: ${scanStatus.current}/${scanStatus.total}` return ( diff --git a/app/client/src/components/nav/TranscodingStatus.js b/app/client/src/components/nav/TranscodingStatus.js index 9575db94..c84ae4f6 100644 --- a/app/client/src/components/nav/TranscodingStatus.js +++ b/app/client/src/components/nav/TranscodingStatus.js @@ -69,9 +69,7 @@ const TranscodingStatus = ({ open, authenticated }) => { - - {stoppedMessage} - + {stoppedMessage} @@ -91,15 +89,14 @@ const TranscodingStatus = ({ open, authenticated }) => { <> Transcoding:{' '} - {status.current + status.completed_tasks}/{status.total + status.completed_tasks + status.queue_tasks} + {status.current + status.completed_tasks}/ + {status.total + status.completed_tasks + status.queue_tasks} )} {status.current_video && ( - - {status.current_video} - + {status.current_video} )} {typeof status.percent === 'number' && ( @@ -134,22 +131,25 @@ const TranscodingStatus = ({ open, authenticated }) => { ) } - const tooltipText = status.total === 0 - ? 'Preparing transcode...' - : `Transcoding: ${status.current + status.completed_tasks}/${status.total + status.completed_tasks + status.queue_tasks}${status.current_video ? `\n${status.current_video}` : ''}` + const tooltipText = + status.total === 0 + ? 'Preparing transcode...' + : `Transcoding: ${status.current + status.completed_tasks}/${status.total + status.completed_tasks + status.queue_tasks}${status.current_video ? `\n${status.current_video}` : ''}` return ( - + diff --git a/app/client/src/services/AdminSSE.js b/app/client/src/services/AdminSSE.js index a529e5c7..8db46b29 100644 --- a/app/client/src/services/AdminSSE.js +++ b/app/client/src/services/AdminSSE.js @@ -1,7 +1,9 @@ import { getUrl } from '../common/utils' const getAdminStreamBaseUrl = () => { - const isLocalhost = (window.location.hostname.indexOf('localhost') >= 0 || window.location.hostname.indexOf('127.0.0.1') >= 0) && window.location.port !== '' + const isLocalhost = + (window.location.hostname.indexOf('localhost') >= 0 || window.location.hostname.indexOf('127.0.0.1') >= 0) && + window.location.port !== '' if (isLocalhost) { return `http://${window.location.hostname}:${import.meta.env.VITE_SERVER_PORT || '3001'}` } @@ -50,10 +52,7 @@ class AdminSSEManager { // ═══════════════════════════════════════════════════════════════════════════ connect() { - this.eventSource = new EventSource( - `${getAdminStreamBaseUrl()}/api/admin/stream`, - { withCredentials: true } - ) + this.eventSource = new EventSource(`${getAdminStreamBaseUrl()}/api/admin/stream`, { withCredentials: true }) this.eventSource.addEventListener('transcoding', (e) => { const data = JSON.parse(e.data) @@ -61,13 +60,13 @@ class AdminSSEManager { window.dispatchEvent(new CustomEvent('transcodingStarted')) } this.lastTranscodingStatus = data - this.transcodingSubscribers.forEach(cb => cb(data)) + this.transcodingSubscribers.forEach((cb) => cb(data)) }) this.eventSource.addEventListener('gameScan', (e) => { const data = JSON.parse(e.data) this.lastGameScanStatus = data - this.gameScanSubscribers.forEach(cb => cb(data)) + this.gameScanSubscribers.forEach((cb) => cb(data)) }) } diff --git a/app/client/src/services/StatsService.js b/app/client/src/services/StatsService.js index ce8e47ad..17038da1 100644 --- a/app/client/src/services/StatsService.js +++ b/app/client/src/services/StatsService.js @@ -1,18 +1,18 @@ -import Api from './Api'; // Uses axios setup with baseURL +import Api from './Api' // Uses axios setup with baseURL const StatsService = { async getFolderSize() { try { - const res = await Api().get('/api/folder-size'); // No need for full localhost URL - return res.data; // Return raw data, let component decide how to use + const res = await Api().get('/api/folder-size') // No need for full localhost URL + return res.data // Return raw data, let component decide how to use } catch (error) { - console.error('Failed to fetch folder size:', error); - throw error; + console.error('Failed to fetch folder size:', error) + throw error } }, getGameScanStatus() { - return Api().get('/api/scan-games/status'); - } -}; + return Api().get('/api/scan-games/status') + }, +} -export default StatsService; +export default StatsService diff --git a/app/client/src/services/VideoService.js b/app/client/src/services/VideoService.js index 1e3eafcc..a6f7ba48 100644 --- a/app/client/src/services/VideoService.js +++ b/app/client/src/services/VideoService.js @@ -98,7 +98,7 @@ const service = { }, onUploadProgress: (progressEvent) => { const progressTotal = (progressEvent.loaded + alreadyUploaded) / totalSize - const progress = (progressEvent.loaded) / progressEvent.total + const progress = progressEvent.loaded / progressEvent.total uploadProgress(progress, progressTotal, { loaded: (progressEvent.loaded + alreadyUploaded) / Math.pow(10, 6), total: totalSize / Math.pow(10, 6), @@ -114,7 +114,7 @@ const service = { }, onUploadProgress: (progressEvent) => { const progressTotal = (progressEvent.loaded + alreadyUploaded) / totalSize - const progress = (progressEvent.loaded) / progressEvent.total + const progress = progressEvent.loaded / progressEvent.total uploadProgress(progress, progressTotal, { loaded: (progressEvent.loaded + alreadyUploaded) / Math.pow(10, 6), total: totalSize / Math.pow(10, 6), diff --git a/app/client/src/views/Dashboard.js b/app/client/src/views/Dashboard.js index bcc6dc69..6f9a01d4 100644 --- a/app/client/src/views/Dashboard.js +++ b/app/client/src/views/Dashboard.js @@ -475,7 +475,12 @@ const Dashboard = ({ height: 38, flexShrink: 1, minWidth: 0, - '& .MuiButton-root': { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', px: { xs: 1, sm: 2 } }, + '& .MuiButton-root': { + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + px: { xs: 1, sm: 2 }, + }, }} >