Merge branch 'main' into similar_index_clear
This commit is contained in:
@@ -1831,7 +1831,8 @@
|
||||
"videosProcessed": "Videos processed",
|
||||
"totalVideos": "Total videos",
|
||||
"skippedVideos": "Skipped videos",
|
||||
"videoStreamingDescription": "Play videos instantly on any device.\nEnable to process video streams on this device.",
|
||||
"videoStreamingDescriptionLine1": "Play videos instantly on any device.",
|
||||
"videoStreamingDescriptionLine2": "Enable to process video streams on this device.",
|
||||
"videoStreamingNote": "Only videos from last 60 days and under 1 minute are processed on this device. For older/longer videos, enable streaming in the desktop app.",
|
||||
"createStream": "Create stream",
|
||||
"recreateStream": "Recreate stream",
|
||||
|
||||
@@ -10,7 +10,7 @@ import "package:photos/core/event_bus.dart";
|
||||
import "package:photos/events/compute_control_event.dart";
|
||||
import "package:thermal/thermal.dart";
|
||||
|
||||
enum _ComputeRunState {
|
||||
enum ComputeRunState {
|
||||
idle,
|
||||
runningML,
|
||||
generatingStream,
|
||||
@@ -35,7 +35,7 @@ class ComputeController {
|
||||
bool interactionOverride = false;
|
||||
late Timer _userInteractionTimer;
|
||||
|
||||
_ComputeRunState _currentRunState = _ComputeRunState.idle;
|
||||
ComputeRunState _currentRunState = ComputeRunState.idle;
|
||||
bool _waitingToRunML = false;
|
||||
|
||||
bool get isDeviceHealthy => _isDeviceHealthy;
|
||||
@@ -70,10 +70,20 @@ class ComputeController {
|
||||
_logger.info('init done ');
|
||||
}
|
||||
|
||||
bool requestCompute({bool ml = false, bool stream = false}) {
|
||||
_logger.info("Requesting compute: ml: $ml, stream: $stream");
|
||||
if (!_isDeviceHealthy || !_canRunGivenUserInteraction()) {
|
||||
_logger.info("Device not healthy or user interacting, denying request.");
|
||||
bool requestCompute({
|
||||
bool ml = false,
|
||||
bool stream = false,
|
||||
bool bypassInteractionCheck = false,
|
||||
}) {
|
||||
_logger.info(
|
||||
"Requesting compute: ml: $ml, stream: $stream, bypassInteraction: $bypassInteractionCheck",
|
||||
);
|
||||
if (!_isDeviceHealthy) {
|
||||
_logger.info("Device not healthy, denying request.");
|
||||
return false;
|
||||
}
|
||||
if (!bypassInteractionCheck && !_canRunGivenUserInteraction()) {
|
||||
_logger.info("User interacting, denying request.");
|
||||
return false;
|
||||
}
|
||||
bool result = false;
|
||||
@@ -87,13 +97,17 @@ class ComputeController {
|
||||
return result;
|
||||
}
|
||||
|
||||
ComputeRunState get computeState {
|
||||
return _currentRunState;
|
||||
}
|
||||
|
||||
bool _requestML() {
|
||||
if (_currentRunState == _ComputeRunState.idle) {
|
||||
_currentRunState = _ComputeRunState.runningML;
|
||||
if (_currentRunState == ComputeRunState.idle) {
|
||||
_currentRunState = ComputeRunState.runningML;
|
||||
_waitingToRunML = false;
|
||||
_logger.info("ML request granted");
|
||||
return true;
|
||||
} else if (_currentRunState == _ComputeRunState.runningML) {
|
||||
} else if (_currentRunState == ComputeRunState.runningML) {
|
||||
return true;
|
||||
}
|
||||
_logger.info(
|
||||
@@ -104,12 +118,9 @@ class ComputeController {
|
||||
}
|
||||
|
||||
bool _requestStream() {
|
||||
if (_currentRunState == _ComputeRunState.idle && !_waitingToRunML) {
|
||||
if (_currentRunState == ComputeRunState.idle && !_waitingToRunML) {
|
||||
_logger.info("Stream request granted");
|
||||
_currentRunState = _ComputeRunState.generatingStream;
|
||||
return true;
|
||||
} else if (_currentRunState == _ComputeRunState.generatingStream &&
|
||||
!_waitingToRunML) {
|
||||
_currentRunState = ComputeRunState.generatingStream;
|
||||
return true;
|
||||
}
|
||||
_logger.info(
|
||||
@@ -124,13 +135,13 @@ class ComputeController {
|
||||
);
|
||||
|
||||
if (ml) {
|
||||
if (_currentRunState == _ComputeRunState.runningML) {
|
||||
_currentRunState = _ComputeRunState.idle;
|
||||
if (_currentRunState == ComputeRunState.runningML) {
|
||||
_currentRunState = ComputeRunState.idle;
|
||||
}
|
||||
_waitingToRunML = false;
|
||||
} else if (stream) {
|
||||
if (_currentRunState == _ComputeRunState.generatingStream) {
|
||||
_currentRunState = _ComputeRunState.idle;
|
||||
if (_currentRunState == ComputeRunState.generatingStream) {
|
||||
_currentRunState = ComputeRunState.idle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/file_magic_service.dart";
|
||||
import "package:photos/services/filedata/model/file_data.dart";
|
||||
import "package:photos/services/isolated_ffmpeg_service.dart";
|
||||
import "package:photos/services/machine_learning/compute_controller.dart";
|
||||
import "package:photos/ui/notification/toast.dart";
|
||||
import "package:photos/utils/exif_util.dart";
|
||||
import "package:photos/utils/file_key.dart";
|
||||
@@ -120,8 +121,9 @@ class VideoPreviewService {
|
||||
if (file.uploadedFileID == null) return false;
|
||||
|
||||
// Check if already in queue
|
||||
final bool alreadyInQueue =
|
||||
await uploadLocksDB.isInStreamQueue(file.uploadedFileID!);
|
||||
final bool alreadyInQueue = await uploadLocksDB.isInStreamQueue(
|
||||
file.uploadedFileID!,
|
||||
);
|
||||
if (alreadyInQueue) {
|
||||
return false; // Indicates file was already in queue
|
||||
}
|
||||
@@ -131,7 +133,7 @@ class VideoPreviewService {
|
||||
|
||||
// Start processing if not already processing
|
||||
if (uploadingFileId < 0) {
|
||||
queueFiles(duration: Duration.zero);
|
||||
queueFiles(duration: Duration.zero, isManual: true);
|
||||
} else {
|
||||
_items[file.uploadedFileID!] = PreviewItem(
|
||||
status: PreviewItemStatus.inQueue,
|
||||
@@ -252,10 +254,12 @@ class VideoPreviewService {
|
||||
BuildContext? ctx,
|
||||
EnteFile enteFile, [
|
||||
bool forceUpload = false,
|
||||
bool isManual = false,
|
||||
]) async {
|
||||
if (!_allowStream()) {
|
||||
final canStream = _isPermissionGranted();
|
||||
if (!canStream) {
|
||||
_logger.info(
|
||||
"Pause preview due to disabledSteaming($isVideoStreamingEnabled) or computeController permission)",
|
||||
"Pause preview due to disabledSteaming($isVideoStreamingEnabled) or computeController permission) - isManual: $isManual",
|
||||
);
|
||||
if (isVideoStreamingEnabled) _logger.info("No permission to run compute");
|
||||
clearQueue();
|
||||
@@ -295,7 +299,8 @@ class VideoPreviewService {
|
||||
"Starting video preview generation for ${enteFile.displayName}",
|
||||
);
|
||||
// elimination case for <=10 MB with H.264
|
||||
var (props, result, file) = await _checkFileForPreviewCreation(enteFile);
|
||||
var (props, result, file) =
|
||||
await _checkFileForPreviewCreation(enteFile, isManual);
|
||||
if (result) {
|
||||
removeFile = true;
|
||||
return;
|
||||
@@ -575,8 +580,9 @@ class VideoPreviewService {
|
||||
Future<void> _removeFromLocks(EnteFile enteFile) async {
|
||||
final bool isFailurePresent =
|
||||
_failureFiles?.contains(enteFile.uploadedFileID!) ?? false;
|
||||
final bool isInManualQueue =
|
||||
await uploadLocksDB.isInStreamQueue(enteFile.uploadedFileID!);
|
||||
final bool isInManualQueue = await uploadLocksDB.isInStreamQueue(
|
||||
enteFile.uploadedFileID!,
|
||||
);
|
||||
|
||||
if (isFailurePresent) {
|
||||
await uploadLocksDB.deleteStreamUploadErrorEntry(
|
||||
@@ -917,27 +923,35 @@ class VideoPreviewService {
|
||||
}
|
||||
|
||||
Future<(FFProbeProps?, bool, File?)> _checkFileForPreviewCreation(
|
||||
EnteFile enteFile,
|
||||
) async {
|
||||
EnteFile enteFile, [
|
||||
bool isManual = false,
|
||||
]) async {
|
||||
if ((enteFile.pubMagicMetadata?.sv ?? 0) == 1) {
|
||||
_logger.info("Skip Preview due to sv=1 for ${enteFile.displayName}");
|
||||
return (null, true, null);
|
||||
}
|
||||
if (enteFile.fileSize == null || enteFile.duration == null) {
|
||||
_logger.warning(
|
||||
"Skip Preview due to misisng size/duration for ${enteFile.displayName}",
|
||||
);
|
||||
return (null, true, null);
|
||||
}
|
||||
final int size = enteFile.fileSize!;
|
||||
final int duration = enteFile.duration!;
|
||||
if (size >= 500 * 1024 * 1024 || duration > 60) {
|
||||
_logger.info("Skip Preview due to size: $size or duration: $duration");
|
||||
return (null, true, null);
|
||||
if (!isManual) {
|
||||
if (enteFile.fileSize == null || enteFile.duration == null) {
|
||||
_logger.warning(
|
||||
"Skip Preview due to misisng size/duration for ${enteFile.displayName}",
|
||||
);
|
||||
return (null, true, null);
|
||||
}
|
||||
final int size = enteFile.fileSize!;
|
||||
final int duration = enteFile.duration!;
|
||||
if (size >= 500 * 1024 * 1024 || duration > 60) {
|
||||
_logger.info("Skip Preview due to size: $size or duration: $duration");
|
||||
return (null, true, null);
|
||||
}
|
||||
}
|
||||
FFProbeProps? props;
|
||||
File? file;
|
||||
bool skipFile = false;
|
||||
if (enteFile.fileSize == null && isManual) {
|
||||
return (props, skipFile, file);
|
||||
}
|
||||
|
||||
final size = enteFile.fileSize ?? 0;
|
||||
try {
|
||||
final isFileUnder10MB = size <= 10 * 1024 * 1024;
|
||||
if (isFileUnder10MB) {
|
||||
@@ -1025,8 +1039,9 @@ class VideoPreviewService {
|
||||
}
|
||||
|
||||
// First try to find the file in the 60-day list
|
||||
var queueFile =
|
||||
files.firstWhereOrNull((f) => f.uploadedFileID == queueFileId);
|
||||
var queueFile = files.firstWhereOrNull(
|
||||
(f) => f.uploadedFileID == queueFileId,
|
||||
);
|
||||
|
||||
// If not found in 60-day list, fetch it individually
|
||||
queueFile ??=
|
||||
@@ -1124,11 +1139,27 @@ class VideoPreviewService {
|
||||
computeController.requestCompute(stream: true);
|
||||
}
|
||||
|
||||
void queueFiles({Duration duration = const Duration(seconds: 5)}) {
|
||||
bool _allowManualStream() {
|
||||
return isVideoStreamingEnabled &&
|
||||
computeController.requestCompute(
|
||||
stream: true,
|
||||
bypassInteractionCheck: true,
|
||||
);
|
||||
}
|
||||
|
||||
bool _isPermissionGranted() {
|
||||
return isVideoStreamingEnabled &&
|
||||
computeController.computeState == ComputeRunState.generatingStream;
|
||||
}
|
||||
|
||||
void queueFiles({
|
||||
Duration duration = const Duration(seconds: 5),
|
||||
bool isManual = false,
|
||||
}) {
|
||||
Future.delayed(duration, () async {
|
||||
if (_hasQueuedFile) return;
|
||||
|
||||
final isStreamAllowed = _allowStream();
|
||||
final isStreamAllowed = isManual ? _allowManualStream() : _allowStream();
|
||||
if (!isStreamAllowed) return;
|
||||
|
||||
await _ensurePreviewIdsInitialized();
|
||||
|
||||
@@ -100,7 +100,12 @@ class _VideoStreamingSettingsPageState
|
||||
children: [
|
||||
TextSpan(
|
||||
text: AppLocalizations.of(context)
|
||||
.videoStreamingDescription,
|
||||
.videoStreamingDescriptionLine1,
|
||||
),
|
||||
const TextSpan(text: " "),
|
||||
TextSpan(
|
||||
text: AppLocalizations.of(context)
|
||||
.videoStreamingDescriptionLine2,
|
||||
),
|
||||
const TextSpan(text: " "),
|
||||
TextSpan(
|
||||
@@ -113,7 +118,6 @@ class _VideoStreamingSettingsPageState
|
||||
),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.justify,
|
||||
style: getEnteTextTheme(context).mini.copyWith(
|
||||
color: getEnteColorScheme(context).textMuted,
|
||||
),
|
||||
@@ -145,10 +149,17 @@ class _VideoStreamingSettingsPageState
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
text: AppLocalizations.of(context)
|
||||
.videoStreamingDescription +
|
||||
"\n",
|
||||
children: [
|
||||
TextSpan(
|
||||
text: AppLocalizations.of(context)
|
||||
.videoStreamingDescriptionLine1,
|
||||
),
|
||||
const TextSpan(text: "\n"),
|
||||
TextSpan(
|
||||
text: AppLocalizations.of(context)
|
||||
.videoStreamingDescriptionLine2,
|
||||
),
|
||||
const TextSpan(text: "\n"),
|
||||
TextSpan(
|
||||
text: AppLocalizations.of(context).moreDetails,
|
||||
style: TextStyle(
|
||||
|
||||
@@ -497,6 +497,7 @@ class FileAppBarState extends State<FileAppBar> {
|
||||
final userId = Configuration.instance.getUserID();
|
||||
return widget.file.fileType == FileType.video &&
|
||||
widget.file.isUploaded &&
|
||||
widget.file.fileSize != null &&
|
||||
(widget.file.pubMagicMetadata?.sv ?? 0) != 1 &&
|
||||
widget.file.ownerID == userId;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
- Similar images design changes. Also changed the vectorDB index file name, so internal users will have another migration (long loading time).
|
||||
- Prateek: Enable immediate manual video stream processing by bypassing user interaction timer
|
||||
- Prateek: Fix multiple concurrent streaming processes bug in ComputeController
|
||||
- Prateek: Fix video streaming description text display spacing in advanced settings
|
||||
- Ashil: Render cached thumbnails faster (noticeable in gallery scrolling)
|
||||
- Similar images UI changes
|
||||
- Neeraj: Fix for double enteries for local file
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
- Video streaming improvements
|
||||
- Added support for custom domain links
|
||||
- Image editor fixes:
|
||||
- Fixed bottom navigation bar color in light theme
|
||||
|
||||
Reference in New Issue
Block a user