Say a photograph is downloading, and how far, instead of failing
The develop view reported a remote original on its way through the error message, so it read "Could not load image" over "Downloading…". It did so on every step along the roll, including a cached frame that was ready within a tick, so each step flashed the error. Waiting is now its own state. On the step, the grid's thumbnail of the photograph stands in at once. Only when a transfer is really on the wire does it dim under "Not on this device yet", with a line like "Downloading — 12.4 of 38.0 MB" and a progress bar. The bytes come from a new RemoteBackend::get_reporting. The Nextcloud backend overrides it to read the body chunk by chunk; the default reports once at the end. Progress is kept in the in-flight registry by path, because a step usually lands on a frame the prefetcher is already fetching. The catalog's file length stands in when the server sends no Content-Length.
This commit is contained in:
@@ -376,6 +376,33 @@ impl RemoteBackend for NextcloudBackend {
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
async fn get_reporting(
|
||||
&self,
|
||||
id: &RemoteId,
|
||||
progress: &(dyn Fn(u64, Option<u64>) + Send + Sync),
|
||||
) -> Result<Vec<u8>, RemoteError> {
|
||||
let url = self.url_for_id(id)?;
|
||||
let mut resp = self
|
||||
.client
|
||||
.get(&url)
|
||||
.basic_auth(&self.login, Some(&self.password))
|
||||
.send()
|
||||
.await
|
||||
.map_err(map_send_error)?;
|
||||
map_status(resp.status(), &url)?;
|
||||
|
||||
// Read chunk by chunk rather than with `bytes()`, which is the same
|
||||
// transfer with nothing to say until it ends.
|
||||
let declared = resp.content_length();
|
||||
let mut body = Vec::with_capacity(declared.unwrap_or(0) as usize);
|
||||
progress(0, declared);
|
||||
while let Some(chunk) = resp.chunk().await.map_err(map_send_error)? {
|
||||
body.extend_from_slice(&chunk);
|
||||
progress(body.len() as u64, declared);
|
||||
}
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
async fn put(
|
||||
&self,
|
||||
path: &RemotePath,
|
||||
|
||||
@@ -98,6 +98,26 @@ pub trait RemoteBackend: Send + Sync {
|
||||
/// either way; [`Capabilities::range_reads`] says whether it was cheap.
|
||||
async fn get(&self, id: &RemoteId, range: Option<Range<u64>>) -> Result<Vec<u8>, RemoteError>;
|
||||
|
||||
/// Fetch a whole object, saying how much of it has arrived as it arrives.
|
||||
///
|
||||
/// `progress` is called with the bytes received so far and the length the
|
||||
/// server declared, if it declared one. For the one transfer a person
|
||||
/// watches: an original opened in develop is tens of megabytes, and a
|
||||
/// view that can only say "downloading" for that long reads as stuck.
|
||||
///
|
||||
/// The default fetches with [`get`](Self::get) and reports once, at the
|
||||
/// end — right for a backend whose `get` is a local read, where there is
|
||||
/// no wait to report on.
|
||||
async fn get_reporting(
|
||||
&self,
|
||||
id: &RemoteId,
|
||||
progress: &(dyn Fn(u64, Option<u64>) + Send + Sync),
|
||||
) -> Result<Vec<u8>, RemoteError> {
|
||||
let body = self.get(id, None).await?;
|
||||
progress(body.len() as u64, Some(body.len() as u64));
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
/// Upload, optionally guarded by a precondition.
|
||||
///
|
||||
/// Backends handle chunking internally based on body size — chunked
|
||||
|
||||
Reference in New Issue
Block a user