mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2025-12-18 05:55:45 +00:00
WIP
This commit is contained in:
7
.github/test/Dockerfile-alpine
vendored
7
.github/test/Dockerfile-alpine
vendored
@@ -27,7 +27,12 @@ RUN \
|
||||
file \
|
||||
nodejs \
|
||||
poppler-utils \
|
||||
python3 && \
|
||||
python3 \
|
||||
# OpenCV dependencies for image processing
|
||||
glib \
|
||||
libsm \
|
||||
libxext \
|
||||
libxrender && \
|
||||
echo "**** pip3 install test of changedetection.io ****" && \
|
||||
python3 -m venv /lsiopy && \
|
||||
pip install -U pip wheel setuptools && \
|
||||
|
||||
@@ -43,6 +43,11 @@ RUN --mount=type=cache,id=pip,sharing=locked,target=/tmp/pip-cache \
|
||||
--cache-dir=/tmp/pip-cache \
|
||||
-r /requirements.txt
|
||||
|
||||
# Copy installed packages from system Python to /dependencies for final image
|
||||
ARG PYTHON_VERSION
|
||||
RUN mkdir -p /dependencies && \
|
||||
cp -r /usr/local/lib/python${PYTHON_VERSION}/site-packages/* /dependencies/
|
||||
|
||||
|
||||
# Playwright is an alternative to Selenium
|
||||
# Excluded this package from requirements.txt to prevent arm/v6 and arm/v7 builds from failing
|
||||
|
||||
@@ -241,9 +241,9 @@ def generate_diff_image_opencv(img_bytes_to, diff_mask):
|
||||
# Convert back to PIL Image
|
||||
diff_img = Image.fromarray(result_array.astype(np.uint8))
|
||||
|
||||
# Save to bytes as PNG (faster rendering than JPEG for diff images)
|
||||
# Save to bytes as JPEG (smaller and faster than PNG for diff visualization)
|
||||
buf = io.BytesIO()
|
||||
diff_img.save(buf, format='PNG', optimize=True)
|
||||
diff_img.save(buf, format='JPEG', quality=85, optimize=True)
|
||||
diff_bytes = buf.getvalue()
|
||||
|
||||
# Explicit memory cleanup - close files and buffers, delete large objects
|
||||
@@ -264,14 +264,17 @@ def generate_diff_image_pixelmatch(diff_array):
|
||||
diff_array: RGBA diff array from pixelmatch (4D numpy array)
|
||||
|
||||
Returns:
|
||||
bytes: PNG image with highlighted differences
|
||||
bytes: JPEG image with highlighted differences
|
||||
"""
|
||||
# Convert diff array to PIL Image
|
||||
# Convert diff array to PIL Image (RGBA)
|
||||
diff_img = Image.fromarray(diff_array.astype(np.uint8), mode='RGBA')
|
||||
|
||||
# Save to bytes as PNG
|
||||
# Convert RGBA to RGB for JPEG (JPEG doesn't support transparency)
|
||||
diff_img = diff_img.convert('RGB')
|
||||
|
||||
# Save to bytes as JPEG (smaller and faster than PNG)
|
||||
buf = io.BytesIO()
|
||||
diff_img.save(buf, format='PNG', optimize=True)
|
||||
diff_img.save(buf, format='JPEG', quality=85, optimize=True)
|
||||
diff_bytes = buf.getvalue()
|
||||
|
||||
# Explicit memory cleanup - close files first, then delete
|
||||
|
||||
@@ -157,7 +157,16 @@
|
||||
<div style="color: #666; font-size: 0.9em; margin-bottom: 1em;">
|
||||
{{ from_version|format_timestamp_timeago }}
|
||||
</div>
|
||||
<img src="data:image/png;base64,{{ img_from_b64 }}" alt="Previous screenshot">
|
||||
<div style="text-align: center; margin-bottom: 0.5em;">
|
||||
<a href="#" onclick="downloadImage('img-from', '{{ from_version }}'); return false;" style="color: #0078e7; text-decoration: none; display: inline-flex; align-items: center; gap: 0.3em; font-size: 0.85em;" title="Download previous snapshot">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" style="display: inline-block;">
|
||||
<path d="M8 12L3 7h3V1h4v6h3z"/>
|
||||
<path d="M1 14h14v2H1z"/>
|
||||
</svg>
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
<img id="img-from" src="data:image/png;base64,{{ img_from_b64 }}" alt="Previous screenshot">
|
||||
</div>
|
||||
|
||||
<!-- Panel 2: To (Current) -->
|
||||
@@ -166,16 +175,32 @@
|
||||
<div style="color: #666; font-size: 0.9em; margin-bottom: 1em;">
|
||||
{{ to_version|format_timestamp_timeago }}
|
||||
</div>
|
||||
<img src="data:image/png;base64,{{ img_to_b64 }}" alt="Current screenshot">
|
||||
<div style="text-align: center; margin-bottom: 0.5em;">
|
||||
<a href="#" onclick="downloadImage('img-to', '{{ to_version }}'); return false;" style="color: #0078e7; text-decoration: none; display: inline-flex; align-items: center; gap: 0.3em; font-size: 0.85em;" title="Download current snapshot">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" style="display: inline-block;">
|
||||
<path d="M8 12L3 7h3V1h4v6h3z"/>
|
||||
<path d="M1 14h14v2H1z"/>
|
||||
</svg>
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
<img id="img-to" src="data:image/png;base64,{{ img_to_b64 }}" alt="Current screenshot">
|
||||
</div>
|
||||
|
||||
<!-- Panel 3: Difference (with red highlights) -->
|
||||
<div class="screenshot-panel diff">
|
||||
<h3>Difference Visualization</h3>
|
||||
<div style="color: #d32f2f; font-size: 0.9em; margin-bottom: 1em; font-weight: bold;">
|
||||
Red = Changed Pixels
|
||||
<div style="color: #d32f2f; font-size: 0.9em; margin-bottom: 1em; font-weight: bold; display: flex; align-items: center; justify-content: center; gap: 1em;">
|
||||
<a href="#" onclick="downloadImage('diff-image', '{{ to_version }}_diff'); return false;" style="color: #0078e7; text-decoration: none; display: flex; align-items: center; gap: 0.3em;" title="Download difference image">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" style="display: inline-block;">
|
||||
<path d="M8 12L3 7h3V1h4v6h3z"/>
|
||||
<path d="M1 14h14v2H1z"/>
|
||||
</svg>
|
||||
Download
|
||||
</a>
|
||||
<span>Red = Changed Pixels</span>
|
||||
</div>
|
||||
<img src="data:image/png;base64,{{ diff_image_b64 }}" alt="Difference visualization with red highlights">
|
||||
<img id="diff-image" src="data:image/jpeg;base64,{{ diff_image_b64 }}" alt="Difference visualization with red highlights">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -215,4 +240,41 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function downloadImage(imageId, filename) {
|
||||
// Get the image element
|
||||
const img = document.getElementById(imageId);
|
||||
const base64Data = img.src;
|
||||
|
||||
// Convert base64 to blob
|
||||
const byteString = atob(base64Data.split(',')[1]);
|
||||
const mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0];
|
||||
|
||||
const ab = new ArrayBuffer(byteString.length);
|
||||
const ia = new Uint8Array(ab);
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
ia[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
|
||||
const blob = new Blob([ab], { type: mimeString });
|
||||
|
||||
// Determine file extension from MIME type
|
||||
const extension = mimeString.includes('jpeg') ? '.jpeg' : '.png';
|
||||
|
||||
// Create download link
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename + extension;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
|
||||
// Cleanup
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}, 100);
|
||||
}
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user