Stream RAW saves with live progress v0.8.3
This commit is contained in:
1 parent
2dee67021e
commit
f1704c9822
6 files changed
+85
-27
No files matched your search
+58
-19
@@ -357,7 +357,7 @@ function updatePhotoStatus(photo) {
|
||||
const values = photo.fileValues || photo.values;
|
||||
if (photo.status === 'saved') {
|
||||
const timing = photo.lastSaveTiming
|
||||
? ` · ${photo.lastSaveTiming.total.toFixed(1)}s total · ${photo.lastSaveTiming.serverTransfer.toFixed(1)}s server/transfer · ${photo.lastSaveTiming.localWrite.toFixed(1)}s local write`
|
||||
? ` · ${formatSaveTiming(photo.lastSaveTiming)}`
|
||||
: '';
|
||||
setSaveStatus('saved', 'Previously saved', `${formatMetadataSummary(values)}${timing}`);
|
||||
} else if (photo.status === 'skipped') {
|
||||
@@ -371,6 +371,13 @@ function updatePhotoStatus(photo) {
|
||||
}
|
||||
}
|
||||
|
||||
function formatSaveTiming(timing) {
|
||||
if (timing.streamed) {
|
||||
return `${timing.total.toFixed(1)}s total · ${timing.firstByte.toFixed(1)}s to first byte · ${timing.streamWrite.toFixed(1)}s stream/write`;
|
||||
}
|
||||
return `${timing.total.toFixed(1)}s total · ${timing.serverTransfer.toFixed(1)}s server/transfer · ${timing.localWrite.toFixed(1)}s local write`;
|
||||
}
|
||||
|
||||
async function readPhotoMetadata(photo, file) {
|
||||
if (photo.metadataLoaded) return;
|
||||
const payload = new FormData();
|
||||
@@ -625,20 +632,59 @@ async function savePhoto(moveNext) {
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const updatedBlob = await response.blob();
|
||||
const serverAndTransferSeconds = (performance.now() - saveStarted) / 1000;
|
||||
setSaveStatus('saving', 'Writing updated file locally…', `${photo.name} · ${serverAndTransferSeconds.toFixed(1)}s processing and transfer`);
|
||||
const localWriteStarted = performance.now();
|
||||
const responseReceivedAt = performance.now();
|
||||
const firstByteSeconds = (responseReceivedAt - saveStarted) / 1000;
|
||||
const writable = await photo.handle.createWritable({ keepExistingData: false });
|
||||
let timing;
|
||||
let responseReader = null;
|
||||
try {
|
||||
await writable.write(updatedBlob);
|
||||
await writable.close();
|
||||
if (response.body && typeof response.body.getReader === 'function') {
|
||||
responseReader = response.body.getReader();
|
||||
const expectedBytes = Number(response.headers.get('content-length')) || 0;
|
||||
let receivedBytes = 0;
|
||||
let lastProgressUpdate = 0;
|
||||
while (true) {
|
||||
const { done, value } = await responseReader.read();
|
||||
if (done) break;
|
||||
await writable.write(value);
|
||||
receivedBytes += value.byteLength;
|
||||
const now = performance.now();
|
||||
if (now - lastProgressUpdate >= 200) {
|
||||
const progress = expectedBytes
|
||||
? `${Math.min(100, (receivedBytes / expectedBytes) * 100).toFixed(0)}% · ${formatBytes(receivedBytes)} / ${formatBytes(expectedBytes)}`
|
||||
: `${formatBytes(receivedBytes)} received`;
|
||||
setSaveStatus('saving', 'Streaming updated file…', `${photo.name} · ${progress}`);
|
||||
lastProgressUpdate = now;
|
||||
}
|
||||
}
|
||||
await writable.close();
|
||||
const completedAt = performance.now();
|
||||
timing = {
|
||||
streamed: true,
|
||||
total: (completedAt - saveStarted) / 1000,
|
||||
firstByte: firstByteSeconds,
|
||||
streamWrite: (completedAt - responseReceivedAt) / 1000,
|
||||
};
|
||||
} else {
|
||||
const updatedBlob = await response.blob();
|
||||
const serverAndTransferSeconds = (performance.now() - saveStarted) / 1000;
|
||||
setSaveStatus('saving', 'Writing updated file locally…', `${photo.name} · buffered compatibility mode`);
|
||||
const localWriteStarted = performance.now();
|
||||
await writable.write(updatedBlob);
|
||||
await writable.close();
|
||||
const localWriteSeconds = (performance.now() - localWriteStarted) / 1000;
|
||||
timing = {
|
||||
streamed: false,
|
||||
total: serverAndTransferSeconds + localWriteSeconds,
|
||||
serverTransfer: serverAndTransferSeconds,
|
||||
localWrite: localWriteSeconds,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
if (responseReader) await responseReader.cancel().catch(() => {});
|
||||
await writable.abort().catch(() => {});
|
||||
throw error;
|
||||
}
|
||||
const localWriteSeconds = (performance.now() - localWriteStarted) / 1000;
|
||||
const totalSaveSeconds = serverAndTransferSeconds + localWriteSeconds;
|
||||
|
||||
const updatedFile = await photo.handle.getFile();
|
||||
photo.size = updatedFile.size;
|
||||
@@ -650,21 +696,14 @@ async function savePhoto(moveNext) {
|
||||
photo.fileValues = { ...savedValues };
|
||||
photo.metadataLoaded = true;
|
||||
photo.metadataPresent = true;
|
||||
photo.lastSaveTiming = {
|
||||
total: totalSaveSeconds,
|
||||
serverTransfer: serverAndTransferSeconds,
|
||||
localWrite: localWriteSeconds,
|
||||
};
|
||||
photo.lastSaveTiming = timing;
|
||||
saveFolderState();
|
||||
setSaveStatus(
|
||||
'saved',
|
||||
'Saved in place',
|
||||
`${formatMetadataSummary(savedValues)} · ${serverAndTransferSeconds.toFixed(1)}s server/transfer · ${localWriteSeconds.toFixed(1)}s local write`,
|
||||
);
|
||||
showToast(
|
||||
`Saved ${photo.name}: ${totalSaveSeconds.toFixed(1)}s total · `
|
||||
+ `${serverAndTransferSeconds.toFixed(1)}s server/transfer · ${localWriteSeconds.toFixed(1)}s local write`,
|
||||
`${formatMetadataSummary(savedValues)} · ${formatSaveTiming(timing)}`,
|
||||
);
|
||||
showToast(`Saved ${photo.name}: ${formatSaveTiming(timing)}`);
|
||||
updateProgress();
|
||||
|
||||
const oldThumb = state.thumbUrls.get(photo.name);
|
||||
|
||||
Reference in new issue
Block a user