Skip to content
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions motioneye/static/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4719,6 +4719,17 @@ function runMediaDialog(cameraId, mediaType) {

/* camera frames */

function hideMediaButtonIfEmpty(cameraId, mediaType, button) {
/* the list endpoint returns {mediaList: [...]} (empty when there are no
* files); with_stat=false keeps the request cheap. On error the button is
* left visible (safe default - don't hide when we are unsure). */
ajax('GET', basePath + mediaType + '/' + cameraId + '/list/?with_stat=false', null, function (data) {
if (data && data.mediaList && !data.mediaList.length) {
button.hide();
}
});
}
Comment on lines +4722 to +4733

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure we should call the media list endpoint just to decide whether to hide a button. Even with with_stat=false, this can still be resource-intensive for users with lots of saved files. Unfortunately, I don’t see an easy alternative solution either.

@JamBalaya56562 JamBalaya56562 Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, agreed that calling the full media list just to decide whether to hide the button would be too expensive for large media directories.

I addressed this in c23c3d8 by turning that request into a bounded existence check instead of a full listing:

  • the UI now calls /list/?with_stat=false&limit=1 for the hide-button check;
  • the picture/movie list handlers parse an optional positive limit query parameter;
  • local media listing stops walking as soon as the requested number of matching files is found, so limit=1 returns after the first actual media file;
  • the limit is also respected across recursive subdirectories;
  • remote camera list requests forward with_stat=false and limit=1 only when explicitly requested, while the default request remains unchanged for compatibility with older remote motionEye instances.

I also added tests for the early-stop behavior, handler query parsing, recursive limit handling, and remote query forwarding.

Tested with WSL/Linux:

pytest tests/test_mediafiles.py tests/test_remote.py tests/test_handlers/test_media_list_limit.py -q
65 passed, 123 subtests passed

pytest tests -q
146 passed, 10 warnings, 123 subtests passed


function addCameraFrameUi(cameraConfig) {
var cameraId = cameraConfig.id;

Expand Down Expand Up @@ -4835,6 +4846,17 @@ function addCameraFrameUi(cameraConfig) {
picturesButton.hide();
moviesButton.hide();
}
else {
/* when a capture mode is disabled, only hide its media-browser button
* if there are no existing files left to browse, so media recorded
* before disabling capture stays accessible (#2731) */
if (!cameraConfig['still_images']) {
hideMediaButtonIfEmpty(cameraId, 'picture', picturesButton);
}
if (!cameraConfig['movies']) {
hideMediaButtonIfEmpty(cameraId, 'movie', moviesButton);
}
}

cameraFrameDiv.attr('id', 'camera' + cameraId);
cameraFrameDiv[0].refreshDivider = 0;
Expand Down
Loading