From 707e8dbfcfc2a9d57c1d93529998570406c72643 Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Mon, 25 Aug 2025 20:57:18 +0530 Subject: [PATCH] fix: show processing status, fix when to not show popup buttons, update getFiles logic --- mobile/apps/photos/lib/db/files_db.dart | 34 +++++---- mobile/apps/photos/lib/l10n/intl_en.arb | 3 +- .../lib/services/video_preview_service.dart | 30 +++++--- .../lib/ui/viewer/file/file_app_bar.dart | 4 ++ .../ui/viewer/file/video_stream_change.dart | 69 ++++++++++++++++++- 5 files changed, 117 insertions(+), 23 deletions(-) diff --git a/mobile/apps/photos/lib/db/files_db.dart b/mobile/apps/photos/lib/db/files_db.dart index eea4697b4e..62101e7e60 100644 --- a/mobile/apps/photos/lib/db/files_db.dart +++ b/mobile/apps/photos/lib/db/files_db.dart @@ -1687,26 +1687,36 @@ class FilesDB with SqlDbBase { ); } - Future> getAllFilesAfterDate({ - required FileType fileType, - required DateTime beginDate, + Future> getStreamingEligibleVideoFiles({ + DateTime? beginDate, required int userID, + bool onlyFilesWithLocalId = false, }) async { final db = await instance.sqliteAsyncDB; - final results = await db.getAll( - ''' + + String query = ''' SELECT * FROM $filesTable WHERE $columnFileType = ? - AND $columnCreationTime > ? AND ($columnUploadedFileID IS NOT NULL AND $columnUploadedFileID != -1) - AND $columnOwnerID = $userID - AND $columnLocalID IS NOT NULL + AND $columnOwnerID = ? AND ($columnFileSize IS NOT NULL AND $columnFileSize <= 524288000) AND ($columnDuration IS NOT NULL AND ($columnDuration <= 60 AND $columnDuration > 0)) - ORDER BY $columnCreationTime DESC - ''', - [getInt(fileType), beginDate.microsecondsSinceEpoch], - ); + '''; + + final List queryArgs = [getInt(FileType.video), userID]; + + if (beginDate != null) { + query += ' AND $columnCreationTime > ?'; + queryArgs.add(beginDate.microsecondsSinceEpoch); + } + + if (onlyFilesWithLocalId) { + query += ' AND $columnLocalID IS NOT NULL'; + } + + query += ' ORDER BY $columnCreationTime DESC'; + + final results = await db.getAll(query, queryArgs); return convertToFiles(results); } diff --git a/mobile/apps/photos/lib/l10n/intl_en.arb b/mobile/apps/photos/lib/l10n/intl_en.arb index 0b0c8af5d0..6a947367a9 100644 --- a/mobile/apps/photos/lib/l10n/intl_en.arb +++ b/mobile/apps/photos/lib/l10n/intl_en.arb @@ -1839,5 +1839,6 @@ "addedToStreamRecreationQueue": "Added to stream recreation queue", "videoPreviewAlreadyExists": "Video preview already exists", "videoAlreadyInQueue": "Video file already present in the queue", - "addedToQueue": "Added to queue" + "addedToQueue": "Added to queue", + "creatingStream": "Creating stream" } \ No newline at end of file diff --git a/mobile/apps/photos/lib/services/video_preview_service.dart b/mobile/apps/photos/lib/services/video_preview_service.dart index 3d4b3eb6b6..18a00f1de5 100644 --- a/mobile/apps/photos/lib/services/video_preview_service.dart +++ b/mobile/apps/photos/lib/services/video_preview_service.dart @@ -158,6 +158,11 @@ class VideoPreviewService { } } + bool isCurrentlyProcessing(int? uploadedFileID) { + if (uploadedFileID == null) return false; + return uploadingFileId == uploadedFileID; + } + Future _isRecreateOperation(EnteFile file) async { if (file.uploadedFileID == null) return false; @@ -192,11 +197,14 @@ class VideoPreviewService { } } - Future> _getFiles() async { - return await FilesDB.instance.getAllFilesAfterDate( - fileType: FileType.video, - beginDate: DateTime.now().subtract(const Duration(days: 60)), + Future> _getFiles({ + DateTime? beginDate, + bool onlyFilesWithLocalId = true, + }) async { + return await FilesDB.instance.getStreamingEligibleVideoFiles( + beginDate: beginDate, userID: Configuration.instance.getUserID()!, + onlyFilesWithLocalId: onlyFilesWithLocalId, ); } @@ -204,7 +212,10 @@ class VideoPreviewService { try { // TODO: Should we consider all days we could have processed or last 60 days await _ensurePreviewIdsInitialized(); - final files = await _getFiles(); + final files = await _getFiles( + beginDate: null, + onlyFilesWithLocalId: false, + ); final Set totalProcessed = fileDataService.previewIds.keys.toSet(); final Set total = {}; final Set processed = {}; @@ -213,9 +224,7 @@ class VideoPreviewService { for (final file in files) { if (totalProcessed.contains(file.uploadedFileID)) { processed.add(file.uploadedFileID!); - continue; - } - if (file.pubMagicMetadata?.sv == 1) { + } else if (file.pubMagicMetadata?.sv == 1) { skipped++; continue; } @@ -958,7 +967,10 @@ class VideoPreviewService { manualQueueFiles = await UploadLocksDB.instance.getStreamQueue(); } catch (_) {} - final files = await _getFiles(); + final files = await _getFiles( + beginDate: DateTime.now().subtract(const Duration(days: 60)), + onlyFilesWithLocalId: true, + ); final previewIds = fileDataService.previewIds; _logger.info( diff --git a/mobile/apps/photos/lib/ui/viewer/file/file_app_bar.dart b/mobile/apps/photos/lib/ui/viewer/file/file_app_bar.dart index c57cbf98cf..5a978cc33c 100644 --- a/mobile/apps/photos/lib/ui/viewer/file/file_app_bar.dart +++ b/mobile/apps/photos/lib/ui/viewer/file/file_app_bar.dart @@ -481,17 +481,21 @@ class FileAppBarState extends State { bool _shouldShowCreateStreamOption() { // Show "Create Stream" option for uploaded video files without streams + // Skip if sv=1 (server indicates streaming not needed) return widget.file.fileType == FileType.video && widget.file.isUploaded && widget.file.uploadedFileID != null && + (widget.file.pubMagicMetadata?.sv ?? 0) != 1 && !fileDataService.previewIds.containsKey(widget.file.uploadedFileID!); } bool _shouldShowRecreateStreamOption() { // Show "Recreate Stream" option for uploaded video files with existing streams + // Skip if sv=1 (server indicates streaming not needed) return widget.file.fileType == FileType.video && widget.file.isUploaded && widget.file.uploadedFileID != null && + (widget.file.pubMagicMetadata?.sv ?? 0) != 1 && fileDataService.previewIds.containsKey(widget.file.uploadedFileID!); } diff --git a/mobile/apps/photos/lib/ui/viewer/file/video_stream_change.dart b/mobile/apps/photos/lib/ui/viewer/file/video_stream_change.dart index 0c06acb0d3..72e04f16bf 100644 --- a/mobile/apps/photos/lib/ui/viewer/file/video_stream_change.dart +++ b/mobile/apps/photos/lib/ui/viewer/file/video_stream_change.dart @@ -2,6 +2,7 @@ import "package:flutter/material.dart"; import "package:photos/generated/l10n.dart"; import "package:photos/models/file/file.dart"; import "package:photos/service_locator.dart"; +import "package:photos/services/video_preview_service.dart"; import "package:photos/theme/colors.dart"; class VideoStreamChangeWidget extends StatefulWidget { @@ -38,9 +39,75 @@ class _VideoStreamChangeWidgetState extends State { Widget build(BuildContext context) { final bool isPreviewAvailable = widget.file.uploadedFileID != null && (fileDataService.previewIds.containsKey(widget.file.uploadedFileID)); - if (!isPreviewAvailable) { + + // Check if this file is currently being processed for streaming + final bool isCurrentlyProcessing = VideoPreviewService.instance + .isCurrentlyProcessing(widget.file.uploadedFileID); + + if (!isPreviewAvailable && !isCurrentlyProcessing) { return const SizedBox(); } + + // If currently processing, show "Creating Stream" with spinner (not clickable) + if (isCurrentlyProcessing) { + return Align( + alignment: Alignment.centerRight, + child: AnimatedOpacity( + duration: const Duration( + milliseconds: 200, + ), + curve: Curves.easeInQuad, + opacity: widget._showControls ? 1 : 0, + child: Padding( + padding: const EdgeInsets.only( + right: 10, + bottom: 4, + ), + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + decoration: BoxDecoration( + color: Colors.green, + borderRadius: const BorderRadius.all( + Radius.circular(200), + ), + border: Border.all( + color: strokeFaintDark, + width: 1, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation(Colors.white), + ), + ), + const SizedBox(width: 4), + Text( + AppLocalizations.of(context).creatingStream, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), + ], + ), + ), + ), + ), + ); + } + + // Default case - show stream toggle return Align( alignment: Alignment.centerRight, child: AnimatedOpacity(