Hide ghost details in object panel when preview fails to load
This commit is contained in:
@@ -175,13 +175,21 @@ def handle_app_error(error: AppError) -> Response:
|
|||||||
|
|
||||||
def handle_rate_limit_exceeded(e: RateLimitExceeded) -> Response:
|
def handle_rate_limit_exceeded(e: RateLimitExceeded) -> Response:
|
||||||
g.s3_error_code = "SlowDown"
|
g.s3_error_code = "SlowDown"
|
||||||
|
if request.path.startswith("/ui") or request.path.startswith("/buckets"):
|
||||||
|
wants_json = (
|
||||||
|
request.is_json or
|
||||||
|
request.headers.get("X-Requested-With") == "XMLHttpRequest" or
|
||||||
|
"application/json" in request.accept_mimetypes.values()
|
||||||
|
)
|
||||||
|
if wants_json:
|
||||||
|
return jsonify({"success": False, "error": {"code": "SlowDown", "message": "Please reduce your request rate."}}), 429
|
||||||
error = Element("Error")
|
error = Element("Error")
|
||||||
SubElement(error, "Code").text = "SlowDown"
|
SubElement(error, "Code").text = "SlowDown"
|
||||||
SubElement(error, "Message").text = "Please reduce your request rate."
|
SubElement(error, "Message").text = "Please reduce your request rate."
|
||||||
SubElement(error, "Resource").text = request.path
|
SubElement(error, "Resource").text = request.path
|
||||||
SubElement(error, "RequestId").text = getattr(g, "request_id", "")
|
SubElement(error, "RequestId").text = getattr(g, "request_id", "")
|
||||||
xml_bytes = tostring(error, encoding="utf-8")
|
xml_bytes = tostring(error, encoding="utf-8")
|
||||||
return Response(xml_bytes, status=429, mimetype="application/xml")
|
return Response(xml_bytes, status="429 Too Many Requests", mimetype="application/xml")
|
||||||
|
|
||||||
|
|
||||||
def register_error_handlers(app):
|
def register_error_handlers(app):
|
||||||
|
|||||||
@@ -99,6 +99,8 @@
|
|||||||
const previewMetadataList = document.getElementById('preview-metadata-list');
|
const previewMetadataList = document.getElementById('preview-metadata-list');
|
||||||
const previewPlaceholder = document.getElementById('preview-placeholder');
|
const previewPlaceholder = document.getElementById('preview-placeholder');
|
||||||
const previewPlaceholderDefault = previewPlaceholder ? previewPlaceholder.innerHTML : '';
|
const previewPlaceholderDefault = previewPlaceholder ? previewPlaceholder.innerHTML : '';
|
||||||
|
const previewErrorAlert = document.getElementById('preview-error-alert');
|
||||||
|
const previewDetailsMeta = document.getElementById('preview-details-meta');
|
||||||
const previewImage = document.getElementById('preview-image');
|
const previewImage = document.getElementById('preview-image');
|
||||||
const previewVideo = document.getElementById('preview-video');
|
const previewVideo = document.getElementById('preview-video');
|
||||||
const previewAudio = document.getElementById('preview-audio');
|
const previewAudio = document.getElementById('preview-audio');
|
||||||
@@ -1990,6 +1992,34 @@
|
|||||||
previewPlaceholder.classList.remove('d-none');
|
previewPlaceholder.classList.remove('d-none');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let previewFailed = false;
|
||||||
|
|
||||||
|
const handlePreviewError = () => {
|
||||||
|
previewFailed = true;
|
||||||
|
if (downloadButton) {
|
||||||
|
downloadButton.classList.add('disabled');
|
||||||
|
downloadButton.removeAttribute('href');
|
||||||
|
}
|
||||||
|
if (presignButton) presignButton.disabled = true;
|
||||||
|
if (generatePresignButton) generatePresignButton.disabled = true;
|
||||||
|
if (previewDetailsMeta) previewDetailsMeta.classList.add('d-none');
|
||||||
|
if (previewMetadata) previewMetadata.classList.add('d-none');
|
||||||
|
const tagsPanel = document.getElementById('preview-tags');
|
||||||
|
if (tagsPanel) tagsPanel.classList.add('d-none');
|
||||||
|
const versionPanel = document.getElementById('version-panel');
|
||||||
|
if (versionPanel) versionPanel.classList.add('d-none');
|
||||||
|
if (previewErrorAlert) {
|
||||||
|
previewErrorAlert.textContent = 'Unable to load object \u2014 it may have been deleted, or the server returned an error.';
|
||||||
|
previewErrorAlert.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearPreviewError = () => {
|
||||||
|
previewFailed = false;
|
||||||
|
if (previewErrorAlert) previewErrorAlert.classList.add('d-none');
|
||||||
|
if (previewDetailsMeta) previewDetailsMeta.classList.remove('d-none');
|
||||||
|
};
|
||||||
|
|
||||||
async function fetchMetadata(metadataUrl) {
|
async function fetchMetadata(metadataUrl) {
|
||||||
if (!metadataUrl) return null;
|
if (!metadataUrl) return null;
|
||||||
try {
|
try {
|
||||||
@@ -2011,6 +2041,7 @@
|
|||||||
previewPanel.classList.remove('d-none');
|
previewPanel.classList.remove('d-none');
|
||||||
activeRow = row;
|
activeRow = row;
|
||||||
renderMetadata(null);
|
renderMetadata(null);
|
||||||
|
clearPreviewError();
|
||||||
|
|
||||||
previewKey.textContent = row.dataset.key;
|
previewKey.textContent = row.dataset.key;
|
||||||
previewSize.textContent = formatBytes(Number(row.dataset.size));
|
previewSize.textContent = formatBytes(Number(row.dataset.size));
|
||||||
@@ -2036,25 +2067,69 @@
|
|||||||
if (previewUrl && lower.match(/\.(png|jpg|jpeg|gif|webp|svg|ico|bmp)$/)) {
|
if (previewUrl && lower.match(/\.(png|jpg|jpeg|gif|webp|svg|ico|bmp)$/)) {
|
||||||
previewPlaceholder.innerHTML = '<div class="spinner-border spinner-border-sm text-secondary" role="status"></div><div class="small mt-2">Loading preview\u2026</div>';
|
previewPlaceholder.innerHTML = '<div class="spinner-border spinner-border-sm text-secondary" role="status"></div><div class="small mt-2">Loading preview\u2026</div>';
|
||||||
const currentRow = row;
|
const currentRow = row;
|
||||||
previewImage.onload = () => {
|
fetch(previewUrl)
|
||||||
if (activeRow !== currentRow) return;
|
.then((r) => {
|
||||||
previewImage.classList.remove('d-none');
|
if (activeRow !== currentRow) return;
|
||||||
previewPlaceholder.classList.add('d-none');
|
if (!r.ok) {
|
||||||
};
|
previewPlaceholder.innerHTML = '<div class="small text-muted">Failed to load preview</div>';
|
||||||
previewImage.onerror = () => {
|
handlePreviewError();
|
||||||
if (activeRow !== currentRow) return;
|
return;
|
||||||
previewPlaceholder.innerHTML = '<div class="small text-muted">Failed to load preview</div>';
|
}
|
||||||
};
|
return r.blob();
|
||||||
previewImage.src = previewUrl;
|
})
|
||||||
|
.then((blob) => {
|
||||||
|
if (!blob || activeRow !== currentRow) return;
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
previewImage.onload = () => {
|
||||||
|
if (activeRow !== currentRow) { URL.revokeObjectURL(url); return; }
|
||||||
|
previewImage.classList.remove('d-none');
|
||||||
|
previewPlaceholder.classList.add('d-none');
|
||||||
|
};
|
||||||
|
previewImage.onerror = () => {
|
||||||
|
if (activeRow !== currentRow) { URL.revokeObjectURL(url); return; }
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
previewPlaceholder.innerHTML = '<div class="small text-muted">Failed to load preview</div>';
|
||||||
|
};
|
||||||
|
previewImage.src = url;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (activeRow !== currentRow) return;
|
||||||
|
previewPlaceholder.innerHTML = '<div class="small text-muted">Failed to load preview</div>';
|
||||||
|
handlePreviewError();
|
||||||
|
});
|
||||||
} else if (previewUrl && lower.match(/\.(mp4|webm|ogv|mov|avi|mkv)$/)) {
|
} else if (previewUrl && lower.match(/\.(mp4|webm|ogv|mov|avi|mkv)$/)) {
|
||||||
|
const currentRow = row;
|
||||||
|
previewVideo.onerror = () => {
|
||||||
|
if (activeRow !== currentRow) return;
|
||||||
|
previewVideo.classList.add('d-none');
|
||||||
|
previewPlaceholder.classList.remove('d-none');
|
||||||
|
previewPlaceholder.innerHTML = '<div class="small text-muted">Failed to load preview</div>';
|
||||||
|
handlePreviewError();
|
||||||
|
};
|
||||||
previewVideo.src = previewUrl;
|
previewVideo.src = previewUrl;
|
||||||
previewVideo.classList.remove('d-none');
|
previewVideo.classList.remove('d-none');
|
||||||
previewPlaceholder.classList.add('d-none');
|
previewPlaceholder.classList.add('d-none');
|
||||||
} else if (previewUrl && lower.match(/\.(mp3|wav|flac|ogg|aac|m4a|wma)$/)) {
|
} else if (previewUrl && lower.match(/\.(mp3|wav|flac|ogg|aac|m4a|wma)$/)) {
|
||||||
|
const currentRow = row;
|
||||||
|
previewAudio.onerror = () => {
|
||||||
|
if (activeRow !== currentRow) return;
|
||||||
|
previewAudio.classList.add('d-none');
|
||||||
|
previewPlaceholder.classList.remove('d-none');
|
||||||
|
previewPlaceholder.innerHTML = '<div class="small text-muted">Failed to load preview</div>';
|
||||||
|
handlePreviewError();
|
||||||
|
};
|
||||||
previewAudio.src = previewUrl;
|
previewAudio.src = previewUrl;
|
||||||
previewAudio.classList.remove('d-none');
|
previewAudio.classList.remove('d-none');
|
||||||
previewPlaceholder.classList.add('d-none');
|
previewPlaceholder.classList.add('d-none');
|
||||||
} else if (previewUrl && lower.match(/\.(pdf)$/)) {
|
} else if (previewUrl && lower.match(/\.(pdf)$/)) {
|
||||||
|
const currentRow = row;
|
||||||
|
previewIframe.onerror = () => {
|
||||||
|
if (activeRow !== currentRow) return;
|
||||||
|
previewIframe.classList.add('d-none');
|
||||||
|
previewPlaceholder.classList.remove('d-none');
|
||||||
|
previewPlaceholder.innerHTML = '<div class="small text-muted">Failed to load preview</div>';
|
||||||
|
handlePreviewError();
|
||||||
|
};
|
||||||
previewIframe.src = previewUrl;
|
previewIframe.src = previewUrl;
|
||||||
previewIframe.style.minHeight = '500px';
|
previewIframe.style.minHeight = '500px';
|
||||||
previewIframe.classList.remove('d-none');
|
previewIframe.classList.remove('d-none');
|
||||||
@@ -2079,14 +2154,17 @@
|
|||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
if (activeRow !== currentRow) return;
|
if (activeRow !== currentRow) return;
|
||||||
previewText.textContent = 'Failed to load preview';
|
previewText.classList.add('d-none');
|
||||||
|
previewPlaceholder.classList.remove('d-none');
|
||||||
|
previewPlaceholder.innerHTML = '<div class="small text-muted">Failed to load preview</div>';
|
||||||
|
handlePreviewError();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const metadataUrl = row.dataset.metadataUrl;
|
const metadataUrl = row.dataset.metadataUrl;
|
||||||
if (metadataUrl) {
|
if (metadataUrl) {
|
||||||
const metadata = await fetchMetadata(metadataUrl);
|
const metadata = await fetchMetadata(metadataUrl);
|
||||||
if (activeRow === row) {
|
if (activeRow === row && !previewFailed) {
|
||||||
renderMetadata(metadata);
|
renderMetadata(metadata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3993,6 +4071,10 @@
|
|||||||
|
|
||||||
const loadObjectTags = async (row) => {
|
const loadObjectTags = async (row) => {
|
||||||
if (!row || !previewTagsPanel) return;
|
if (!row || !previewTagsPanel) return;
|
||||||
|
if (previewFailed) {
|
||||||
|
previewTagsPanel.classList.add('d-none');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const tagsUrl = row.dataset.tagsUrl;
|
const tagsUrl = row.dataset.tagsUrl;
|
||||||
if (!tagsUrl) {
|
if (!tagsUrl) {
|
||||||
previewTagsPanel.classList.add('d-none');
|
previewTagsPanel.classList.add('d-none');
|
||||||
|
|||||||
@@ -257,7 +257,8 @@
|
|||||||
Share Link
|
Share Link
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-3 rounded mb-3" style="background: var(--myfsio-preview-bg);">
|
<div id="preview-error-alert" class="alert alert-warning d-none py-2 px-3 mb-3 small" role="alert"></div>
|
||||||
|
<div id="preview-details-meta" class="p-3 rounded mb-3" style="background: var(--myfsio-preview-bg);">
|
||||||
<dl class="row small mb-0">
|
<dl class="row small mb-0">
|
||||||
<dt class="col-5 text-muted fw-normal">Last modified</dt>
|
<dt class="col-5 text-muted fw-normal">Last modified</dt>
|
||||||
<dd class="col-7 mb-2 fw-medium" id="preview-modified"></dd>
|
<dd class="col-7 mb-2 fw-medium" id="preview-modified"></dd>
|
||||||
|
|||||||
Reference in New Issue
Block a user