Commit Graph
28742 Commits
Author SHA1 Message Date
Isaac ConnorandClaude Opus 4.8 c5f24a1d1f fix: loop the :// strip in detaintPath so it cannot be re-formed
detaintPath() and detaintPathAllowAbsolute() removed '://' with a single
str_replace() while the '../' removal below them already looped. One
pass is not enough, because removing a match can join its neighbours
into a fresh match: '::////' collapses to '://'. So
'php::////filter/read=string.rot13/resource=/etc/passwd' came back out
of the filter as 'php://filter/read=string.rot13/resource=/etc/passwd',
reinstating exactly the wrapper the strip exists to remove.

Loop the '://' removal the same way the '../' removal is looped. These
functions guard $view, $request, $action, the modal name and skin file
paths.

Refs GHSA-wgqf-6fjf-7gxw.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 13:13:06 -04:00
Isaac Connor 6daa135e53 Merge branch 'master' of github.com:ZoneMinder/zoneminder 2026-07-19 06:23:21 -04:00
Isaac ConnorandClaude Opus 4.8 bd3a5ad4ec refactor: share filter-panel inline decision in a skin helper
The rule for whether the monitor filter panel is shown inline (with its
own toggle icon) or embedded in the left sidebar was duplicated across
console, montage, montagereview and watch, plus the body-class logic in
getBodyTopHTML. Each copy only checked ZM_WEB_FILTER_SETTINGS_POSITION and
ignored the navbar, so the toggle was missing whenever the panel was shown
inline.

The sidebar can only embed into a left navbar, so without one the filters
must be inline and keep their toggle. The effective navbar position is not
the ZM_WEB_NAVBAR_TYPE default but the per-user value that index.php
resolves from the request/cookie/session into the global $navbar_type;
skin.js uses that same value to decide whether to relocate the panel.

Add filterSettingsInline() to the skin functions and use it in all five
places. It returns true when $navbar_type is not 'left', or when the
position is explicitly inline, matching skin.js.

Verified live by flipping the zmNavbar_type cookie: with 'normal' the
views render inline and show the working toggle (console, montage,
montagereview); with 'left' and a sidebar position they render in sidebar
mode with no inline toggle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 22:36:52 -04:00
Isaac Connor 147b3988af Merge pull request #4986 from anton-vinogradov/feature/hwaccel-list
feat(decoder): DecoderHWAccelName accepts a comma-separated priority list
2026-07-18 19:52:14 -04:00
АнтонandClaude Opus 4.8 572af3449d feat(decoder): DecoderHWAccelName accepts a comma-separated priority list
Follow-up to #4984 as suggested by @connortechnology: besides "auto"
(probe everything) and a single device-type name, DecoderHWAccelName now
accepts a comma-separated priority list such as "cuda,vaapi" - each
name is tried in order and the first type the decoder supports and whose
device can be created wins, with the existing transparent software
fallback when none do. A single name is the one-element case and behaves
exactly as before. Unknown names are skipped with a Warning. Whitespace
around commas is ignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 18:49:39 +03:00
Isaac Connor 3b255b5937 Merge pull request #4985 from anton-vinogradov/docs/event-commands
docs: explain how Event Start/End Command are actually executed
2026-07-18 11:35:40 -04:00
АнтонandClaude Opus 4.8 d01fc48848 docs: explain how Event Start/End Command are actually executed
The Recording tab docs said only that 'the parameters to the command
will be the event id and the monitor id', which is true only when the
command contains no '%' - and hides several surprises discovered by
reading zm_monitor.cpp:

- without '%' the whole string is exec'd as ONE executable path with
  event id / monitor id appended; a command with inline arguments
  ('/path/script.sh start') is treated as a single file name and fails;
- with '%' the string runs via /bin/sh -c after %EID%/%MID%/%EC%
  substitution, and nothing is appended automatically (%EC% exists for
  the start command only);
- the child runs in the background as the capture-process user with all
  file descriptors closed, so any output silently disappears;
- the end command fires after the event is finalized, so the video file
  is complete by then.

Document all of that where the two options are described.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 18:29:04 +03:00
Isaac ConnorandClaude Opus 4.8 53f6a67fdc fix: restore filter toggle icon in montage review inline mode
The ZM_WEB_FILTER_SETTINGS_POSITION feature wrapped the filter-panel flip
icon in "if (!$filter_inline)", so the icon rendered only in sidebar mode
and disappeared in inline mode, leaving no way to hide the always-visible
filter panel at the top of montage review.

That condition is backwards. In sidebar mode the panel lives in the
sidebar extruder, which has its own show/hide control, so the flip icon is
redundant there. In inline mode the flip icon is the only control that
hides/shows the panel. Render it when inline instead.

Verified in inline mode: the icon appears and toggles the filter panel
(height 200 to 0 and back, icon filter_alt_off to filter_alt).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 14:21:34 -04:00
Isaac ConnorandClaude Opus 4.8 4b94ce2d77 fix: round montage stream scale up so images are not upscaled
streamScaleForMonitor rounded the needed percentage down to the nearest
10, so a monitor whose canvas needed e.g. 16.6% of native was streamed at
10%. zms then sent an image smaller than the canvas (384px into a 637px
canvas) and the browser upscaled it, which looked fuzzy.

Round up to the next 10 instead, so the streamed image is always at least
the canvas size and is only ever downscaled. On the test monitors this
takes Front Yard from scale 10 to 20 and Back Yard from 20 to 30, and the
images render sharp.

getImageSource (the paused / slow-motion per-frame path) had the same
round-down calculation inline, including rounding to 0 for small canvases;
point it at streamScaleForMonitor so both paths share one correct rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 14:04:49 -04:00
Isaac Connor 942f1cb47e Merge branch 'master' of github.com:ZoneMinder/zoneminder 2026-07-17 13:38:10 -04:00
Isaac Connor c3dfa1d966 Merge pull request #4984 from anton-vinogradov/feature/hwaccel-auto
feat(decoder): "auto" hardware-decode detection with software fallback
2026-07-16 17:09:36 -04:00
Isaac ConnorandClaude Opus 4.8 50a60b107d fix: report empty events as zero-span in events ajax and montage query
web/ajax/events.php and the montagereview.php event query carried the same
EndTimeSecs fallback just fixed in the Event API model: an event with no
EndDateTime and Length 0 (an empty crash-orphaned event) reported its end
as NOW(), so it appeared to span from its start until now and overlapped
every later event.

Change the ELSE branch of both the EndDateTime and EndTimeSecs CASE
expressions from NOW() to StartDateTime, so an empty event has no span.
Both columns are changed together so a single row stays consistent.

Verified against the database: for a Length-0 event with no EndDateTime the
CASE now yields EndTimeSecs == StartTimeSecs, while length-based and
properly-closed events are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 15:55:45 -04:00
Isaac ConnorandClaude Opus 4.8 0462bcd4ba fix: keep empty crash-orphaned events out of montage review selection
A capture crash can leave events with no frames, no Length and no
EndDateTime. The API's EndTimeSecs virtual field reported these with an
end of NOW(), the fallback meant for a genuinely in-progress recording,
so every orphan appeared to span from its start until now.

On a monitor with hundreds of these, the events all overlapped: at a
typical timestamp 313 events covered the same instant. findEventByTime is
a binary search that assumes non-overlapping ordered events, so it
returned an essentially arbitrary one, and zms then streamed a different
event than the JS believed was playing. That is the erratic playback and
event/stream mismatch seen in montage review.

Two changes:

- Event model EndTimeSecs: for an event with no EndDateTime and Length 0,
  report end = StartDateTime (zero duration) instead of NOW(). An event
  with no length has no span to occupy, so it no longer overlaps others.

- montagereview receive_events: skip events with no frames. There is
  nothing to review in an empty event, and dropping them keeps the
  timeline and event selection to real recordings.

The same NOW() fallback exists in web/ajax/events.php and
web/skins/classic/views/montagereview.php; those paths are not used by
montage review's event load and are left unchanged.

Verified on a monitor with 521 orphaned events: overlapping events at a
given instant dropped from 313 to 2, and the streamed event now matches
the selected event with sub-second drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 15:14:19 -04:00
Isaac ConnorandClaude Opus 4.8 9be6ce706c fix: advance montage review clock by real elapsed time
timerFire() advanced currentTimeSecs by a nominal per-tick amount
(speed * displayInterval), which is only correct if setInterval fires
exactly on schedule. Under load it fires late, so the JS clock fell
behind real time while the zms streams kept playing at true 1x. The
drift corrector then saw the streams running ahead and seeked them
backward every few seconds, a visible jump.

The JS clock is intentionally the master so multiple zms processes (which
may each have their own clock) stay aligned to one wall-clock instant, so
keep it as master and just make it track real time: advance by the actual
elapsed Date.now() delta each tick. Reset the reference on play and on
speed change so a resume or speed change does not replay skipped time.

Removes the now-unused playSecsPerInterval.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 09:15:56 -04:00
Isaac ConnorandClaude Opus 4.8 801cbf21e5 fix: stream montage review events at canvas scale, not native
EventStream requested scale=100, so zms encoded and sent full native-
resolution JPEGs (e.g. 3840px) that the browser then decoded and shrank
into a ~400px canvas. That is orders of magnitude more bytes and decode
work per frame than the display needs, starving the draw loop and the
stream's command channel and making playback stutter.

The scale was computed in initPage from the canvas width, but at that
point the canvas is still at the monitor's native size (the width PHP
renders it with); redrawScreen() only shrinks it later, so the result was
always ~100. And once set, the scale was never revisited, so the scale
slider, fit mode and zoom never reached a running stream.

Compute the stream scale from the current canvas size in a shared
streamScaleForMonitor() helper, and add updateStreamScales() to push the
size to live streams. Call it from setScale() and from redrawScreen()
(fit mode resizes canvases via maxfit2, bypassing setScale). The
EventStream constructor uses the same helper.

Also keeps the existing per-server Url for the command channel (monitor.Url)
so commands reach the zms that owns the stream in multi-server setups.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 09:14:24 -04:00
Isaac ConnorandClaude Opus 4.8 e43d9db759 fix: stop EventStream restart storm in montage review
Recovery from a transient stream error could spiral into a permanent
loop that spawned and abandoned zms processes and produced endless
"giving up" errors.

Three faults are fixed:

- recover() dropped the dead connection only on the retry path, so the
  give-up branch returned with the status-poll timer and rAF loop still
  running. Each poll reply was another Error, each Error re-entered
  recover(), which gave up again forever. Tear down before the give-up
  check so giving up leaves nothing running.

- recover() had no guard against being called while a retry was already
  scheduled. Errors arriving during the backoff wait inflated the attempt
  counter until it gave up without ever retrying once. Ignore recover()
  while a recoveryTimer is pending.

- The recovery timer and start() both reused the existing <img>, and a
  consumer polling for a live stream (montagereview's outputUpdate) would
  call start() during the backoff window. The delayed recovery then
  overwrote the freshly started stream's src, aborting a healthy stream
  and orphaning its zms, which fired onerror straight back into recover().
  start() now tears down any existing connection first, and the recovery
  timer bails if the stream was already restarted.

Factor the duplicated teardown in stop()/recover()/switchEvent() into a
teardown() helper. switchEvent() now restarts via start() directly instead
of a timer, closing the same started=false race.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 09:11:37 -04:00
Anton VinogradovandClaude Opus 4.8 d9784c73de feat(decoder): "auto" hardware-decode detection with software fallback
DecoderHWAccelName previously required naming a specific device type and
only fell back to software if creating that one device failed. This adds
an "auto" value that iterates over every hwaccel type libav exposes and,
for each, checks that the decoder advertises a matching hw config and
that the device can be created — using the first that works and falling
back to software when none do. Explicit names (e.g. "vaapi", "cuda")
behave as before. Makes hardware decoding safe to enable out of the box
across unknown hardware.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 03:46:56 +03:00
Isaac ConnorandClaude Opus 4.8 d9ee83b4b0 fix: make Width/Height inputs update the dimensions dropdown
updateMonitorDimensions never ran when typing in the capture Width/Height
inputs. initPage() bound it via el.oninput, then a later querySelectorAll
whose selector list also matches newMonitor[Width]/[Height] overwrote that
same property with buffer_setting_oninput. Bind both with addEventListener
so they coexist.

The function would have thrown even once it fired:

- option.size() was removed in jQuery 3.0 and the classic skin ships
  jQuery 3.7.1, so the lookup that picks between a matching resolution and
  Custom raised a TypeError. Use option.length.
- form.elements['preserveAspectRatio'] is undefined for the WebSite monitor
  type, which renders Width/Height without that checkbox. Guard the access.

Drop the inline onkeyup handlers from _monitor_source_nvsocket.php. That
file is included server-side before initPage() runs, so its inputs are
already covered by the binding and the inline handler only made
updateMonitorDimensions fire twice.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 19:18:49 -04:00
Isaac Connor b8b3e2736e Merge pull request #4978 from IgorA100/patch-850068
Fix: Changed the hlsDestroy() function and cleared video._fallbackTimer before executing fallbackToMjpeg() for RTSP2Web type HLS
2026-07-14 16:18:59 -07:00
Isaac Connor 37c51dbb71 Merge branch 'console-columns-dropdown-short-viewport' 2026-07-14 19:02:06 -04:00
Isaac Connor d62af0aa63 Upgrade bootstrap-table to 1.27.3 2026-07-14 19:01:48 -04:00
Isaac Connor d0d56f2b33 fix: keep console Columns dropdown inside the viewport in short windows
#monitorList and its .bootstrap-table are overflow:hidden so the table body
scrolls independently of the toolbar. That container starts below the filter
bar, so anything Popper positions above the toolbar is clipped away.

In a short window Popper did exactly that. It flipped the Columns menu to
top-end placement, and once flipping was disabled it still shifted the menu up
to fit all 300px of it inside the viewport. Either way the menu's top crossed
the container edge and the first entries (Id, Thumbnail) became invisible.

Pin the menu below the toggle with data-flip=false and set its max-height to the
space actually available beneath the button, so Popper never needs to move it
upward. The menu keeps bootstrap-table's overflow:auto, so every column stays
reachable by scrolling. Size it on show and again on resize, since Popper
repositions an open menu but keeps the height it was given when it opened.

Bind to the toggle's parent rather than .columns: bootstrap-table puts the menu
and the show class on an inner .keep-open element, so .columns never carries it
and the open check would never have matched.

Deliberately no minimum height. A floor taller than the room available makes
Popper shove the menu back up over the clipped area, which is the truncation
being fixed here; a short menu still reaches every column by scrolling.

Verified in Chromium against a live console, resizing an open menu between 700px
and 180px: placement stays bottom-end, max-height tracks the space available,
and the first entry stays visible with no clipping throughout. Below ~160px the
sticky header plus toolbar exceed the window and the toggle itself falls outside
the viewport, which is a console-wide layout limit rather than a dropdown one.
2026-07-14 19:01:48 -04:00
Isaac Connor 6fb03348a7 Upgrade bootstrap-table to 1.27.3 2026-07-14 19:01:12 -04:00
Isaac Connor 7e90a40cbe fix: keep console Columns dropdown inside the viewport in short windows
#monitorList and its .bootstrap-table are overflow:hidden so the table body
scrolls independently of the toolbar. That container starts below the filter
bar, so anything Popper positions above the toolbar is clipped away.

In a short window Popper did exactly that. It flipped the Columns menu to
top-end placement, and once flipping was disabled it still shifted the menu up
to fit all 300px of it inside the viewport. Either way the menu's top crossed
the container edge and the first entries (Id, Thumbnail) became invisible.

Pin the menu below the toggle with data-flip=false and set its max-height to the
space actually available beneath the button, so Popper never needs to move it
upward. The menu keeps bootstrap-table's overflow:auto, so every column stays
reachable by scrolling. Size it on show and again on resize, since Popper
repositions an open menu but keeps the height it was given when it opened.

Bind to the toggle's parent rather than .columns: bootstrap-table puts the menu
and the show class on an inner .keep-open element, so .columns never carries it
and the open check would never have matched.

Deliberately no minimum height. A floor taller than the room available makes
Popper shove the menu back up over the clipped area, which is the truncation
being fixed here; a short menu still reaches every column by scrolling.

Verified in Chromium against a live console, resizing an open menu between 700px
and 180px: placement stays bottom-end, max-height tracks the space available,
and the first entry stays visible with no clipping throughout. Below ~160px the
sticky header plus toolbar exceed the window and the toggle itself falls outside
the viewport, which is a console-wide layout limit rather than a dropdown one.
2026-07-14 17:50:26 -04:00
Isaac Connor a443803c91 Merge commit from fork
fix(auth): prevent token authentication bypass using instanceof check
2026-07-11 13:50:47 -07:00
Isaac ConnorandClaude Opus 4.8 79dc3949a5 fix: display montagereview timeline indicator in server timezone refs #4977
The timeline works in epoch seconds but secs2dbstr/secs2inputstr formatted
them with moment() in the browser timezone, so the indicator and the
minTime/maxTime/current URL params drifted whenever the browser timezone
differed from the server. The click_last* and clicknav helpers carried
ad-hoc timezone shifts to partially compensate, while zoom/pan did not.

Format epoch seconds in the server timezone via luxon instead, resolving the
zone from ZM_TIMEZONE (DST-correct), then a fixed UTC offset from
server_utc_offset, then local. Move serverTimeZone, inputstr2dt,
secs2inputstr and secs2dbstr into skin.js so they can be reused. Drop the
now-redundant timezone shifts in clicknav and click_lastHour/Eight/24, and
route changeFilters through inputstr2dt, which also fixes its parse silently
bailing when ZM_TIMEZONE is unset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 12:43:37 -07:00
Isaac Connor 9424399ed0 Improve timed out log message to add ttl 2026-07-11 12:43:37 -07:00
Isaac Connor 181e99e9eb Merge commit from fork
Ghsa 23fj frfw hmcr auth bypass
2026-07-11 15:36:11 -04:00
Isaac Connor 4256c3d06d Merge branch 'master' into ghsa-23fj-frfw-hmcr-auth-bypass 2026-07-11 15:19:19 -04:00
Isaac Connor 6a85f9dac5 Merge branch 'master' of github.com:ZoneMinder/zoneminder 2026-07-11 15:17:44 -04:00
Isaac Connor 9e4c325c95 Merge pull request #4982 from connortechnology/4981-logger-zm-prefix
Rename JS logger globals to zm-prefixed names (fixes #4981)
2026-07-11 15:13:16 -04:00
Isaac Connor 1c878be614 refs #4981 log a stack trace from the error-level JS loggers
Now that zmError/zmFatal/zmPanic no longer shadow the native Error
constructor, build a real Error from the message and report its stack
via logReport. This records where the log call originated instead of
just the bare message. An Error passed in directly is used as-is.
The console output and alert() behaviour are unchanged.
2026-07-11 11:48:08 -07:00
Isaac Connor a503c993bb refs #4981 rename JS logger globals to zm-prefixed names
The JS logger defined seven global functions with very common names:
Panic, Fatal, Error, Warning, Info, Debug and Dump. The Error() one
shadowed the native window.Error constructor, so every `new Error()`
in the bundled third-party libraries (hls.js, jquery, moment,
bootstrap, ...) was silently hijacked once logger.js loaded. The rest
polluted the global namespace and could collide with plugins.

Rename the definitions to zmPanic, zmFatal, zmError, zmWarning, zmInfo,
zmDebug and zmDump, and update the only ZM call sites (all calls to the
former Error()): montage_common.js, event.js and skin.js. Native
`new Error()`, `throw Error()` and `reject(Error())` uses are left
untouched. No back-compat alias for Error() is added because that would
reintroduce the shadowing bug.
2026-07-11 11:48:08 -07:00
IgorA100 e989d79c31 Merge branch 'master' into patch-850068 2026-07-11 12:02:35 +03:00
IgorA100 54785d704f Update skin.js 2026-07-11 11:57:22 +03:00
Isaac ConnorandClaude Opus 4.8 6b94903e63 fix: prevent auth bypass when token validation fails
validateToken() returns array(false, $errorMessage) when a token is
invalid or its signature fails. The token branch in auth.php assigned
$user = $ret[0] unconditionally, leaving $user as boolean false on
failure. Because isset($false) is true in PHP, the ZM_OPT_USE_AUTH gate
in index.php (!isset($user)) was skipped, allowing unauthenticated
access via any malformed ?token= value. It also permitted an
unauthenticated DoS: downstream $user->Username() on a bool fatals.

Check !$ret[0] and unset($user) on failure, mirroring the existing
validateUser branch, so $user stays undefined and the auth gate blocks
the request. The API call sites already throw UnauthorizedException on
!$user and are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 01:20:16 -04:00
Isaac Connor 3a2d48ecab Merge pull request #4979 from IgorA100/patch-155197
Fix: Select an available player before setting a player in the stream, not after on the Watch page.
2026-07-10 22:18:34 -04:00
Buff3st-0v3rfl0w 144a04ca4f fix(auth): prevent token authentication bypass using instanceof check 2026-07-09 16:19:46 -04:00
IgorA100 576ff29894 Select an available player before setting a player in the stream, not after on the Watch page.
This behavior is especially noticeable when using looping playback if a specific player was selected, but it is supported in the next monitor you switch to.
2026-07-09 19:46:03 +03:00
Isaac ConnorandClaude Opus 4.8 346a4baa82 feat: preserve full-range colour when converting YUVJ frames via swscale
swscale assumes limited (MPEG, 16-235) input range by default. Mapping a
full-range JPEG format (YUVJ*) to its non-J equivalent for the conversion
therefore washes the colours out, because full-range luma/chroma gets
treated as limited.

Add pix_fmt_is_jpeg_range() and zm_sws_set_input_range(), which sets
srcRange=1 on the context when the original source format was full range.
Apply it at every conversion site that takes decoded frames: Image::Assign
and both SWScale::Convert overloads. Monitor::setupConvertContext already
did this inline with a hand-rolled switch and colorspace block; replace
that with the shared helpers to remove the duplication.

Add tests/zm_swscale_range.cpp: converting a Y=16 YUVJ420P image to RGB24
keeps luma ~16 (full range) rather than being crushed to ~0 (limited).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:21:25 -04:00
Isaac ConnorandClaude Opus 4.8 18170ec755 fix: map deprecated YUVJ pixel formats before swscale in Image::Assign
Image::Assign(const AVFrame*) passed the raw decoded frame format straight
to sws_getCachedContext. When the frame is a deprecated full-range JPEG
format (YUVJ420P etc, typical of MJPEG-decoded frames), swscale logs
"deprecated pixel format used, make sure you did set range correctly",
seen from nph-zms. Run the source format through fix_deprecated_pix_fmt()
before it reaches swscale, matching what SWScale::Convert already does.
Doing it before the identity-copy check also lets a YUVJ420P->YUV420P
same-size conversion take the av_image_copy fast path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:17:18 -04:00
IgorA100 dbc48493a0 In hlsDestroy(), we pass an object containing an HLS instance as an argument. 2026-07-08 20:03:53 +03:00
IgorA100 f4e8e7fc8e Modified the hlsDestroy() function and cleared video._fallbackTimer before executing fallbackToMjpeg() 2026-07-08 19:45:38 +03:00
Isaac Connor 889b5daa34 Merge pull request #4968 from IgorA100/patch-601764
Improved RTSP2Web processing in HLS mode.
2026-07-08 01:31:53 -04:00
Isaac Connor 26569ddc70 Merge pull request #4969 from IgorA100/patch-234017
More correct handling of audioMotionAnalyzer
2026-07-08 01:07:46 -04:00
Isaac Connor 31b7244feb Merge pull request #4971 from IgorA100/patch-696291
Fix: AudioMotion is displayed when playing a recorded event using the HLS player with Autoplay enabled in Firefox (event.js)
2026-07-08 01:07:17 -04:00
Isaac Connor c56a7e9f7b Merge pull request #4973 from IgorA100/patch-436859
Add left padding to sidebar for better layout on Watch page
2026-07-08 01:06:23 -04:00
IgorA100 99ab2b2985 Add left padding to sidebar for better layout
This is necessary so that the block looks better when the DOM loads (before the bootstrap styles are fully loaded and the ".col-sm-3" style is applied), since this block loads the fastest and doesn't look very nice on a slow connection.
2026-07-07 23:20:46 +03:00
Isaac Connor 9218653618 Merge pull request #4972 from IgorA100/patch-159612
Fix: Run updateHeaderStats(data) slightly earlier (before a possible return) on Log page
2026-07-07 11:25:00 -04:00
IgorA100 2b1bc68d89 Fix: Run updateHeaderStats(data) slightly earlier (before a possible return) (log.js)
Currently, updateHeaderStats(data) may fail to run due to the if (!data.rows.length && data.total > 0) condition and subsequent "return" statement.
We've fixed this issue.
2026-07-07 17:34:38 +03:00