-
-
Notifications
You must be signed in to change notification settings - Fork 111
Gifplayer html #240
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: mdev
Are you sure you want to change the base?
Gifplayer html #240
Conversation
- loading spinner now showing on upload - hostname value now trimmed and checked for change before refresh - remove invisible upload button
they will now use the existing presets, even if they were previously renamed
essentially sending the "off" command
- move advanced features to config dialog
WalkthroughA GIF player feature was integrated into the WLED project. This includes adding a new HTML interface for managing and playing GIF images, updating the main UI to link to this page, extending server logic to serve the GIF player, and introducing a utility function for URL handling. Build tooling was updated to process the new HTML file. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Browser
participant WLED Server
User->>Browser: Clicks "GIF" button
Browser->>WLED Server: GET /gifplayer.htm
WLED Server-->>Browser: Serve gifplayer.htm (from memory if enabled)
Browser->>User: Display GIF player UI
User->>Browser: Upload/select GIF image
Browser->>WLED Server: POST image file
WLED Server-->>Browser: Acknowledge upload
User->>Browser: Play/pause/stop GIF
Browser->>WLED Server: Send JSON command
WLED Server-->>Browser: Respond with status
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~15 minutes Possibly related PRs
Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🧹 Nitpick comments (1)
wled00/data/gifplayer/gifplayer.htm (1)
835-837
:AbortSignal.timeout()
is not yet supported in all browsersFetch will throw
TypeError: AbortSignal.timeout is not a function
on e.g. Safari ≤ 17 or older Chrome.
Consider a polyfill or manualAbortController
for wider compatibility.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
tools/cdata.js
(2 hunks)wled00/data/gifplayer/gifplayer.htm
(1 hunks)wled00/data/index.htm
(1 hunks)wled00/wled_server.cpp
(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (20)
- GitHub Check: Builds (esp01_1MB_S)
- GitHub Check: Builds (athom_music_esp32_4MB_M)
- GitHub Check: Builds (esp8266pro_16MB_M)
- GitHub Check: Builds (esp8266_2MB_S)
- GitHub Check: Builds (esp8266_4MB_M)
- GitHub Check: Builds (esp32c3dev_4MB_M)
- GitHub Check: Builds (seeed_esp32c3_4MB_S)
- GitHub Check: Builds (esp32S3_WROOM-2_M)
- GitHub Check: Builds (esp32S3_4MB_PSRAM_S)
- GitHub Check: Builds (esp32S3_8MB_M)
- GitHub Check: Builds (esp32S3_8MB_PSRAM_M)
- GitHub Check: Builds (esp32S3_4MB_S)
- GitHub Check: Builds (esp32_16MB_V4_M)
- GitHub Check: Builds (esp32_4MB_V4_HUB75_forum)
- GitHub Check: Builds (esp32_4MB_V4_S)
- GitHub Check: Builds (esp32_4MB_M)
- GitHub Check: Builds (esp32_16MB_M_eth)
- GitHub Check: Builds (esp32_4MB_XL)
- GitHub Check: Builds (esp32_4MB_S)
- GitHub Check: Builds (esp32_4MB_M_eth)
🔇 Additional comments (2)
tools/cdata.js (1)
226-228
: Confirm gzip-generation order
writeHtmlGzipped()
is invoked forgifplayer.htm
after the other UI pages, which is fine, but remember: build order controls the numeric IDs compiled into PROGMEM.
If older user-code assumes a fixed index sequence, add the new page at the end everywhere (server routes, enum lists) or document the breaking change.wled00/wled_server.cpp (1)
12-14
: Header must be gated behind the same compile-time flag
#include "html_gifplayer.h"
is already wrapped in#ifndef WLED_DISABLE_GIFPLAYER
, good.
Just check thattools/cdata.js
really emitsPAGE_gifplayer_L
(capital L) – a typo here will only surface at link-time.
// await loading of image list, actual images will load asynchronously | ||
if (await images()); | ||
{ | ||
// await loading of presets.json |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic bug – stray semicolon after if
if (await images());
{
// this block always executes
}
Remove the extra semicolon.
-if (await images());
-{
+if (await images()) {
...
}
🤖 Prompt for AI Agents
In wled00/data/gifplayer/gifplayer.htm around lines 1131 to 1134, there is a
stray semicolon immediately after the if condition "if (await images());" which
causes the following block to always execute regardless of the condition. Remove
the semicolon after the if statement to ensure the block only executes when the
condition is true.
const hostname = gId("hostname"); | ||
const imgDialog = gId("image-dialog"); | ||
const settingsDialog = gId("settings-dialog"); | ||
const contentDivConnected = gId("content-connected"); | ||
let selectedImage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong element reference – will always be null
const contentDivConnected = gId("content-connected");
The actual element is id="contentConnected"
.
Either change the JS or the markup; otherwise contentDivConnected
is null
and any future access will throw.
🤖 Prompt for AI Agents
In wled00/data/gifplayer/gifplayer.htm around lines 756 to 760, the variable
contentDivConnected is assigned using gId("content-connected"), but the actual
element ID in the markup is "contentConnected". To fix this, update the
JavaScript to use gId("contentConnected") so it correctly references the
existing element and does not return null.
|
||
// find next lowest ID / ID of existing preset with segment name == image name | ||
let existingPreset = false; | ||
existingPreset = findPresetID(selectedImage.name); | ||
items = Object.keys(presetsJson); | ||
const id = existingPreset || parseInt(items.pop()) + 1; | ||
const pName = existingPreset |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Implicit globals (items
, formData
, newWledUrl
…)
Several variables are assigned without let/const
, leaking them to window
:
items = Object.keys(...)
formData = new FormData()
newWledUrl = ...
Always declare with let
/const
to avoid hard-to-trace bugs.
-let items = Object.keys(...);
+const items = Object.keys(...);
-let formData = new FormData();
+const formData = new FormData();
Also applies to: 1142-1144
🤖 Prompt for AI Agents
In wled00/data/gifplayer/gifplayer.htm around lines 1018 to 1024 and also lines
1142 to 1144, variables like items, formData, and newWledUrl are assigned
without let or const, causing them to become implicit globals. To fix this,
declare these variables explicitly using let or const at their first assignment
to prevent polluting the global scope and avoid potential bugs.
source.files = e.dataTransfer.files; | ||
const { name } = source.files[0]; | ||
upload(); | ||
}); | ||
|
||
gId("dropzone").addEventListener("click", () => { | ||
source.click(); | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
DOM element accessed via implicit global source
Relying on the HTML-collection fallback (source
→ element with id source
) is deprecated and breaks in strict mode.
Use gId('source')
consistently.
- source.files = e.dataTransfer.files;
+ gId('source').files = e.dataTransfer.files;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
source.files = e.dataTransfer.files; | |
const { name } = source.files[0]; | |
upload(); | |
}); | |
gId("dropzone").addEventListener("click", () => { | |
source.click(); | |
}); | |
gId('source').files = e.dataTransfer.files; | |
const { name } = source.files[0]; | |
upload(); | |
}); | |
gId("dropzone").addEventListener("click", () => { | |
source.click(); | |
}); |
🤖 Prompt for AI Agents
In wled00/data/gifplayer/gifplayer.htm around lines 1196 to 1204, the code uses
the implicit global variable `source` to access the DOM element, which is
deprecated and incompatible with strict mode. Replace all instances of `source`
with `gId('source')` to explicitly get the element by its ID, ensuring
consistent and standards-compliant DOM access.
<div id="btnBack"> | ||
<div | ||
class="btn-icon back" | ||
id="btnBack" | ||
onclick="window.location.href = WLED_URL;" | ||
></div> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate ID btnBack
violates HTML spec
The outer wrapper and the inner icon both use id="btnBack"
.
Duplicate IDs break getElementById()
and CSS specificity.
-<div id="btnBack">
- <div class="btn-icon back" id="btnBack" ...></div>
+<div id="btnBack">
+ <div class="btn-icon back" ...></div>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
<div id="btnBack"> | |
<div | |
class="btn-icon back" | |
id="btnBack" | |
onclick="window.location.href = WLED_URL;" | |
></div> | |
</div> | |
<div id="btnBack"> | |
<div | |
class="btn-icon back" | |
onclick="window.location.href = WLED_URL;" | |
></div> | |
</div> |
🤖 Prompt for AI Agents
In wled00/data/gifplayer/gifplayer.htm between lines 570 and 576, the outer div
and the inner div both use the same id "btnBack", which violates HTML
specifications by duplicating IDs. To fix this, remove the id attribute from the
inner div or rename it to a unique value so that only one element has the id
"btnBack". This will ensure getElementById() and CSS selectors work correctly
without conflicts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (2)
wled00/FX.cpp (1)
4664-4667
: Remove dead / commented-out debug codeLines 4664-4667 are legacy debugging snippets that are now superseded by the proposed explicit error handling. Keeping them commented clutters the file.
- // if (status != 0 && status != 254 && status != 255) { - // Serial.print("GIF renderer return: "); - // Serial.println(status); - // }wled00/image_loader.cpp (1)
38-44
:openGif
never clearsgifDecodeFailed
on success
A previous failure leaves the flag set, forcing IMAGE_ERROR_PREV even after a successful open. Reset it here or right after successfulstartDecoding()
.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
platformio.ini
(2 hunks)wled00/FX.cpp
(2 hunks)wled00/FX.h
(1 hunks)wled00/FX_fcn.cpp
(1 hunks)wled00/fcn_declare.h
(1 hunks)wled00/image_loader.cpp
(1 hunks)wled00/wled_server.cpp
(2 hunks)
✅ Files skipped from review due to trivial changes (3)
- wled00/FX_fcn.cpp
- platformio.ini
- wled00/fcn_declare.h
🚧 Files skipped from review as they are similar to previous changes (1)
- wled00/wled_server.cpp
🧰 Additional context used
🧬 Code Graph Analysis (1)
wled00/FX.cpp (2)
wled00/image_loader.cpp (2)
renderImageToSegment
(78-131)renderImageToSegment
(78-78)wled00/fcn_declare.h (1)
renderImageToSegment
(121-121)
⏰ Context from checks skipped due to timeout of 90000ms (20)
- GitHub Check: Builds (adafruit_matrixportal_esp32s3_tinyUF2)
- GitHub Check: Builds (seeed_esp32c3_4MB_S)
- GitHub Check: Builds (athom_music_esp32_4MB_M)
- GitHub Check: Builds (esp8266_4MB_S)
- GitHub Check: Builds (esp32s2_PSRAM_M)
- GitHub Check: Builds (esp32S3_WROOM-2_M)
- GitHub Check: Builds (esp32_4MB_V4_HUB75_forum)
- GitHub Check: Builds (esp32S3_8MB_PSRAM_M)
- GitHub Check: Builds (esp32s2_PSRAM_S)
- GitHub Check: Builds (esp32S3_4MB_PSRAM_M)
- GitHub Check: Builds (esp32_16MB_V4_M)
- GitHub Check: Builds (esp32S3_4MB_S)
- GitHub Check: Builds (esp32_16MB_V4_S)
- GitHub Check: Builds (wemos_shield_esp32_4MB_M)
- GitHub Check: Builds (esp32_pico_4MB_M)
- GitHub Check: Builds (esp32_16MB_M_eth)
- GitHub Check: Builds (esp32_16MB_M)
- GitHub Check: Builds (esp32_4MB_S)
- GitHub Check: Builds (esp32_4MB_M)
- GitHub Check: Builds (esp32_4MB_M_debug)
🔇 Additional comments (4)
wled00/FX.h (2)
338-341
:✅ Verification successful
MODE_COUNT
consistency check
MODE_COUNT
is hard-coded to 198. With the new IMAGE mode the expected total increases by one; if any mode is still conditionally compiled out, the real count may diverge from this constant. Mismatches break array indexing and JSON dumps.Verify that the number of
FX_MODE_*
defines (excluding commented lines) equalsMODE_COUNT
under every build configuration, or compute the value automatically in the build system instead of a literal.
🏁 Script executed:
#!/bin/bash # Detect duplicates and count defined FX_MODE_* constants rg --no-heading --trim --regexp "#define[ \t]+FX_MODE_" -n | awk '{print $3}' | sort -n | uniq -d echo "Total modes defined:" rg --no-heading --trim --regexp "#define[ \t]+FX_MODE_" -n | wc -lLength of output: 223
MODE_COUNT matches total FX_MODE definitions
Verified there are exactly 198FX_MODE_*
defines and no duplicates, matchingMODE_COUNT
. No mismatch detected. Consider automating this count in the build system to avoid future drift when modes are added or conditionally compiled out.
188-191
:✅ Verification successful
New constant
FX_MODE_IMAGE
may be left without a registered effectThe enum now reserves ID 53 for
FX_MODE_IMAGE
, unconditionally, but the actual effect registration (inWS2812FX::setupEffectData()
/FX.cpp
) is normally wrapped in#ifdef WLED_ENABLE_GIF
.
When the flag is not enabled, the vectors_mode
/_modeData
will have onlyMODE_COUNT - 1
items, so every access that assumes a valid entry at index 53 (e.g. UI look-ups, JSON export) will produce bogus data or out-of-bounds reads.Two safe fixes:
-#define FX_MODE_IMAGE 53 +#ifdef WLED_ENABLE_GIF +#define FX_MODE_IMAGE 53 +#endifor, if you want to keep contiguous numbering, always push a stub entry (name = "Image", function =
mode_static
) when GIF support is disabled.Please confirm that
setupEffectData()
does one of the above, otherwise runtime errors are likely.
🏁 Script executed:
#!/bin/bash # Count how many effects are actually pushed into _modeData rg --no-heading --line-number "_modeData\.push_back" | wc -lLength of output: 64
🏁 Script executed:
#!/bin/bash # List files containing setupEffectData echo "Files containing 'setupEffectData':" rg -l "setupEffectData" -g '*.cpp' # Display the contents of WS2812FX::setupEffectData echo -e "\nFunction WS2812FX::setupEffectData:" rg -n "void WS2812FX::setupEffectData" -A200 -B5 -g '*.cpp' # Show all _modeData.push_back calls with context echo -e "\nAll _modeData.push_back occurrences:" rg -n "_modeData\.push_back" -C3Length of output: 18665
🏁 Script executed:
#!/bin/bash echo "FX_MODE_IMAGE context in FX.h:" rg -n "FX_MODE_IMAGE" -g 'wled00/FX.h' -A3 -B3 echo -e "\nMODE_COUNT define in FX.h:" rg -n "#define MODE_COUNT" -g 'wled00/FX.h' -A3 echo -e "\nclass WS2812FX and _modeCount in FX.h:" rg -n "class WS2812FX" -g 'wled00/FX.h' -A20 -B3 rg -n "_modeCount" -g 'wled00/FX.h' -A5Length of output: 4266
No action needed: missing GIF effect is safely stubbed
The
setupEffectData()
routine always pre-fills all_mode
/_modeData
slots (0…MODE_COUNT–1) with a static stub, then selectively overwrites registered effects. Even whenWLED_ENABLE_GIF
is disabled, index 53 exists (holding the reserved stub), so there’s no out-of-bounds or empty vector entry.wled00/FX.cpp (1)
9089-9094
: Verify build whenWLED_ENABLE_GIF
is undefined
FX_MODE_IMAGE
is conditionally registered under#ifdef WLED_ENABLE_GIF
, but the enum value (defined in FX.h) and the_data_FX_MODE_IMAGE
string are always compiled. That is fine, but ensure:
- No other unconditional references to
mode_image
orFX_MODE_IMAGE
exist.FX_MODE_IMAGE
’s numeric value does not collide with another mode when GIF is disabled (the index gap must still be respected by the UI).If either condition is violated, wrap the enum or adjust numbering accordingly.
wled00/image_loader.cpp (1)
54-63
:⚠️ Potential issueOut-of-bounds risk in up-scaling loops
outX + i
/outY + j
can exceed segment dimensions whengifWidth < activeSeg->width()
. Guard the write to prevent overruns:- activeSeg->setPixelColorXY(outX + i, outY + j, …); + uint16_t xx = outX + i; + uint16_t yy = outY + j; + if (xx < activeSeg->width() && yy < activeSeg->height()) + activeSeg->setPixelColorXY(xx, yy, …);Likely an incorrect or invalid review comment.
/* | ||
Image effect | ||
Draws a .gif image from filesystem on the matrix/strip | ||
*/ | ||
uint16_t mode_image(void) { | ||
#ifndef WLED_ENABLE_GIF | ||
return mode_static(); | ||
#else | ||
renderImageToSegment(SEGMENT); | ||
return FRAMETIME; | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Capture and react to renderImageToSegment()
return value
The error code returned by renderImageToSegment(SEGMENT)
is silently discarded.
This makes troubleshooting impossible and leaves segments “stuck” on the IMAGE effect if the decoder fails (e.g., unsupported file, missing file, etc.).
Consider at minimum logging non-OK codes and falling back to mode_static()
(or another safe effect) when an error persists.
#else
- renderImageToSegment(SEGMENT);
- return FRAMETIME;
+ byte status = renderImageToSegment(SEGMENT);
+ if (status && status < 254) { // 254/255 are WAITING/NO-OP codes
+ DEBUG_PRINTLN(F("GIF error: ")); // keep short to avoid serial flood
+ DEBUG_PRINTF("%u\n", status);
+ return mode_static(); // graceful fallback
+ }
+ return FRAMETIME;
#endif
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
/* | |
Image effect | |
Draws a .gif image from filesystem on the matrix/strip | |
*/ | |
uint16_t mode_image(void) { | |
#ifndef WLED_ENABLE_GIF | |
return mode_static(); | |
#else | |
renderImageToSegment(SEGMENT); | |
return FRAMETIME; | |
#endif | |
/* | |
Image effect | |
Draws a .gif image from filesystem on the matrix/strip | |
*/ | |
uint16_t mode_image(void) { | |
#ifndef WLED_ENABLE_GIF | |
return mode_static(); | |
#else | |
byte status = renderImageToSegment(SEGMENT); | |
if (status && status < 254) { // 254/255 are WAITING/NO-OP codes | |
DEBUG_PRINTLN(F("GIF error: ")); // keep short to avoid serial flood | |
DEBUG_PRINTF("%u\n", status); | |
return mode_static(); // graceful fallback | |
} | |
return FRAMETIME; | |
#endif | |
} |
🤖 Prompt for AI Agents
In wled00/FX.cpp around lines 4653 to 4663, the return value of
renderImageToSegment(SEGMENT) is ignored, which prevents error detection and
recovery if the image rendering fails. Modify the code to capture the return
value, check if it indicates an error, log the error appropriately, and return
mode_static() or another safe fallback effect to avoid the segment getting stuck
on the image effect.
if (strncmp(lastFilename +1, seg.name, 32) != 0) { // segment name changed, load new image | ||
strncpy(lastFilename +1, seg.name, 32); | ||
gifDecodeFailed = false; | ||
if (strcmp(lastFilename + strlen(lastFilename) - 4, ".gif") != 0) { | ||
gifDecodeFailed = true; | ||
return IMAGE_ERROR_UNSUPPORTED_FORMAT; | ||
} | ||
if (file) file.close(); | ||
openGif(lastFilename); | ||
if (!file) { gifDecodeFailed = true; return IMAGE_ERROR_FILE_MISSING; } | ||
decoder.setScreenClearCallback(screenClearCallback); | ||
decoder.setUpdateScreenCallback(updateScreenCallback); | ||
decoder.setDrawPixelCallback(drawPixelCallback); | ||
decoder.setFileSeekCallback(fileSeekCallback); | ||
decoder.setFilePositionCallback(filePositionCallback); | ||
decoder.setFileReadCallback(fileReadCallback); | ||
decoder.setFileReadBlockCallback(fileReadBlockCallback); | ||
decoder.setFileSizeCallback(fileSizeCallback); | ||
decoder.alloc(); | ||
DEBUG_PRINTLN(F("Starting decoding")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Memory leak on successive image changes
When the segment name changes a fresh decoder.alloc()
is called without a preceding decoder.dealloc()
, leaking heap every change. Invoke dealloc()
(and close the prior file) before reallocating:
- if (file) file.close();
+ if (file) file.close();
+ decoder.dealloc();
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
if (strncmp(lastFilename +1, seg.name, 32) != 0) { // segment name changed, load new image | |
strncpy(lastFilename +1, seg.name, 32); | |
gifDecodeFailed = false; | |
if (strcmp(lastFilename + strlen(lastFilename) - 4, ".gif") != 0) { | |
gifDecodeFailed = true; | |
return IMAGE_ERROR_UNSUPPORTED_FORMAT; | |
} | |
if (file) file.close(); | |
openGif(lastFilename); | |
if (!file) { gifDecodeFailed = true; return IMAGE_ERROR_FILE_MISSING; } | |
decoder.setScreenClearCallback(screenClearCallback); | |
decoder.setUpdateScreenCallback(updateScreenCallback); | |
decoder.setDrawPixelCallback(drawPixelCallback); | |
decoder.setFileSeekCallback(fileSeekCallback); | |
decoder.setFilePositionCallback(filePositionCallback); | |
decoder.setFileReadCallback(fileReadCallback); | |
decoder.setFileReadBlockCallback(fileReadBlockCallback); | |
decoder.setFileSizeCallback(fileSizeCallback); | |
decoder.alloc(); | |
DEBUG_PRINTLN(F("Starting decoding")); | |
if (strncmp(lastFilename +1, seg.name, 32) != 0) { // segment name changed, load new image | |
strncpy(lastFilename +1, seg.name, 32); | |
gifDecodeFailed = false; | |
if (strcmp(lastFilename + strlen(lastFilename) - 4, ".gif") != 0) { | |
gifDecodeFailed = true; | |
return IMAGE_ERROR_UNSUPPORTED_FORMAT; | |
} | |
if (file) file.close(); | |
decoder.dealloc(); | |
openGif(lastFilename); | |
if (!file) { gifDecodeFailed = true; return IMAGE_ERROR_FILE_MISSING; } | |
decoder.setScreenClearCallback(screenClearCallback); | |
decoder.setUpdateScreenCallback(updateScreenCallback); | |
decoder.setDrawPixelCallback(drawPixelCallback); | |
decoder.setFileSeekCallback(fileSeekCallback); | |
decoder.setFilePositionCallback(filePositionCallback); | |
decoder.setFileReadCallback(fileReadCallback); | |
decoder.setFileReadBlockCallback(fileReadBlockCallback); | |
decoder.setFileSizeCallback(fileSizeCallback); | |
decoder.alloc(); | |
DEBUG_PRINTLN(F("Starting decoding")); |
🤖 Prompt for AI Agents
In wled00/image_loader.cpp around lines 85 to 104, the code calls
decoder.alloc() when the segment name changes without first calling
decoder.dealloc(), causing a memory leak on successive image changes. To fix
this, before calling decoder.alloc(), ensure you call decoder.dealloc() to free
previously allocated memory and also close the prior file if it is open. This
will prevent heap leaks by properly releasing resources before reallocating.
uint32_t wait = currentFrameDelay * 2 - seg.speed * currentFrameDelay / 128; | ||
|
||
// TODO consider handling this on FX level with a different frametime, but that would cause slow gifs to speed up during transitions | ||
if (millis() - lastFrameDisplayTime < wait) return IMAGE_ERROR_WAITING; | ||
|
||
decoder.getSize(&gifWidth, &gifHeight); | ||
|
||
int result = decoder.decodeFrame(false); | ||
if (result < 0) { gifDecodeFailed = true; return IMAGE_ERROR_FRAME_DECODE; } | ||
|
||
currentFrameDelay = decoder.getFrameDelay_ms(); | ||
unsigned long tooSlowBy = (millis() - lastFrameDisplayTime) - wait; // if last frame was longer than intended, compensate | ||
currentFrameDelay = tooSlowBy > currentFrameDelay ? 0 : currentFrameDelay - tooSlowBy; | ||
lastFrameDisplayTime = millis(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Unsigned arithmetic can underflow
tooSlowBy
is computed with unsigned long
; if the previous frame was faster than wait
, the subtraction underflows, producing a huge value and zeroing currentFrameDelay
unintentionally. Use signed math:
- unsigned long tooSlowBy = (millis() - lastFrameDisplayTime) - wait;
+ long tooSlowBy = long(millis() - lastFrameDisplayTime) - long(wait);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
uint32_t wait = currentFrameDelay * 2 - seg.speed * currentFrameDelay / 128; | |
// TODO consider handling this on FX level with a different frametime, but that would cause slow gifs to speed up during transitions | |
if (millis() - lastFrameDisplayTime < wait) return IMAGE_ERROR_WAITING; | |
decoder.getSize(&gifWidth, &gifHeight); | |
int result = decoder.decodeFrame(false); | |
if (result < 0) { gifDecodeFailed = true; return IMAGE_ERROR_FRAME_DECODE; } | |
currentFrameDelay = decoder.getFrameDelay_ms(); | |
unsigned long tooSlowBy = (millis() - lastFrameDisplayTime) - wait; // if last frame was longer than intended, compensate | |
currentFrameDelay = tooSlowBy > currentFrameDelay ? 0 : currentFrameDelay - tooSlowBy; | |
lastFrameDisplayTime = millis(); | |
uint32_t wait = currentFrameDelay * 2 - seg.speed * currentFrameDelay / 128; | |
// TODO consider handling this on FX level with a different frametime, but that would cause slow gifs to speed up during transitions | |
if (millis() - lastFrameDisplayTime < wait) return IMAGE_ERROR_WAITING; | |
decoder.getSize(&gifWidth, &gifHeight); | |
int result = decoder.decodeFrame(false); | |
if (result < 0) { gifDecodeFailed = true; return IMAGE_ERROR_FRAME_DECODE; } | |
currentFrameDelay = decoder.getFrameDelay_ms(); | |
long tooSlowBy = long(millis() - lastFrameDisplayTime) - long(wait); // if last frame was longer than intended, compensate | |
currentFrameDelay = tooSlowBy > currentFrameDelay ? 0 : currentFrameDelay - tooSlowBy; | |
lastFrameDisplayTime = millis(); |
🤖 Prompt for AI Agents
In wled00/image_loader.cpp around lines 115 to 128, the variable tooSlowBy is
computed using unsigned arithmetic which can underflow if the previous frame was
faster than wait, causing incorrect large values and zeroing currentFrameDelay
unintentionally. Change tooSlowBy to a signed integer type and perform the
subtraction using signed arithmetic to correctly handle cases where the elapsed
time is less than wait.
File file; | ||
char lastFilename[34] = "/"; | ||
GifDecoder<320,320,12,true> decoder; | ||
bool gifDecodeFailed = false; | ||
unsigned long lastFrameDisplayTime = 0, currentFrameDelay = 0; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lastFilename
is vulnerable to missing NUL-termination
strncpy(lastFilename + 1, …, 32)
does not guarantee a trailing \0
when the source length ≥32, producing UB in the later strlen
/strcmp
checks and in openGif
.
- strncpy(lastFilename +1, seg.name, 32);
+ strncpy(lastFilename + 1, seg.name, 32);
+ lastFilename[33] = '\0'; // hard-stop
+ lastFilename[strlen(lastFilename)] = '\0'; // ensure NUL
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In wled00/image_loader.cpp around lines 12 to 17, the lastFilename array is
initialized with a fixed size of 34 and starts with "/". When using strncpy to
copy into lastFilename + 1 with a length of 32, it may not null-terminate if the
source string length is 32 or more, causing undefined behavior in subsequent
strlen, strcmp, or openGif calls. To fix this, ensure that after strncpy, you
explicitly set lastFilename[33] = '\0' to guarantee null-termination, or use a
safer string copy method that always null-terminates within the buffer size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
wled00/data/index.js
(1 hunks)wled00/wled_server.cpp
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- wled00/wled_server.cpp
⏰ Context from checks skipped due to timeout of 90000ms (16)
- GitHub Check: Builds (esp32s2_PSRAM_M)
- GitHub Check: Builds (esp32c3mini_dio_4MB_M)
- GitHub Check: Builds (esp32c3dev_2MB_M)
- GitHub Check: Builds (esp32S3_WROOM-2_M)
- GitHub Check: Builds (esp32S3_16MB_PSRAM_M_HUB75)
- GitHub Check: Builds (esp32S3_4MB_S)
- GitHub Check: Builds (esp32S3_8MB_M)
- GitHub Check: Builds (esp32_4MB_PSRAM_S)
- GitHub Check: Builds (esp32S3_8MB_PSRAM_M)
- GitHub Check: Builds (esp32S3_8MB_S)
- GitHub Check: Builds (esp32S3_4MB_PSRAM_S)
- GitHub Check: Builds (wemos_shield_esp32_4MB_M)
- GitHub Check: Builds (esp32_4MB_V4_S)
- GitHub Check: Builds (esp32_16MB_M_eth)
- GitHub Check: Builds (esp32_pico_4MB_M)
- GitHub Check: Builds (esp32_4MB_M_debug)
function getURL(path) { | ||
return (loc ? locproto + "//" + locip : "") + path; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Undefined locproto
triggers a runtime exception
locproto
is not declared anywhere in this file (and a quick scan shows no earlier definition).
When loc === true
, calling getURL()
will throw ReferenceError: locproto is not defined
, breaking every navigation that relies on this helper.
- function getURL(path) {
- return (loc ? locproto + "//" + locip : "") + path;
+// Builds a URL that honours the page’s current protocol.
+function getURL(path) {
+ const proto = (typeof location !== 'undefined') ? location.protocol : 'http:';
+ return (loc ? proto + "//" + locip : "") + path;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
function getURL(path) { | |
return (loc ? locproto + "//" + locip : "") + path; | |
} | |
// Builds a URL that honours the page’s current protocol. | |
function getURL(path) { | |
const proto = (typeof location !== 'undefined') ? location.protocol : 'http:'; | |
return (loc ? proto + "//" + locip : "") + path; | |
} |
🤖 Prompt for AI Agents
In wled00/data/index.js around lines 208 to 210, the variable locproto used in
getURL is not defined, causing a ReferenceError when loc is true. To fix this,
declare and initialize locproto appropriately before using it in the return
statement, ensuring it holds the correct protocol string (e.g., "http:" or
"https:") based on the context or environment.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Port of wled#3921 to MoonModules WLED-MM
Also includes #226 for ease of testing
Summary by CodeRabbit
New Features
Improvements