Merge pull request #624 from MarekSpodBiedry/issue-378
Space out browser downloads so image-sequence exports are not dropped (#378)
This commit is contained in:
commit
9842fc0c15
|
|
@ -0,0 +1,24 @@
|
|||
//! Spacing of browser downloads (pure, so it is tested natively in `tests/download_pacing.rs`; the
|
||||
//! anchor clicks live in [`crate::fs::download`]).
|
||||
//!
|
||||
//! Chrome drops anchor-click downloads that start in one tight burst, so an image-sequence export
|
||||
//! (one file per frame) delivered only about half of its frames (#378). Each download is given a
|
||||
//! slot [`GAP_MS`] after the previous one.
|
||||
|
||||
/// Milliseconds between two download clicks.
|
||||
pub const GAP_MS: f64 = 125.0;
|
||||
|
||||
/// Hands out click times.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct Pacer {
|
||||
next_free: f64,
|
||||
}
|
||||
|
||||
impl Pacer {
|
||||
/// Reserve the next slot at time `now` (ms); returns how long to wait before clicking.
|
||||
pub fn reserve(&mut self, now: f64) -> f64 {
|
||||
let start = now.max(self.next_free);
|
||||
self.next_free = start + GAP_MS;
|
||||
start - now
|
||||
}
|
||||
}
|
||||
|
|
@ -54,6 +54,7 @@ struct Fs {
|
|||
|
||||
thread_local! {
|
||||
static FS: RefCell<Fs> = RefCell::new(Fs::default());
|
||||
static DOWNLOADS: RefCell<crate::download_pacing::Pacer> = RefCell::new(crate::download_pacing::Pacer::default());
|
||||
}
|
||||
|
||||
/// Wake-up hook for arriving data.
|
||||
|
|
@ -405,7 +406,8 @@ impl Services for WebServices {
|
|||
}
|
||||
}
|
||||
|
||||
/// Offer bytes as a browser download named `name`.
|
||||
/// Offer bytes as a browser download named `name`. Downloads are clicked at least
|
||||
/// [`crate::download_pacing::GAP_MS`] apart: Chrome drops a burst of them (#378).
|
||||
pub fn download(name: &str, data: &[u8]) -> Result<(), wasm_bindgen::JsValue> {
|
||||
let doc = web_sys::window().and_then(|w| w.document()).ok_or("no document")?;
|
||||
let blob = bytes_to_blob(data, "application/octet-stream")?;
|
||||
|
|
@ -414,10 +416,20 @@ pub fn download(name: &str, data: &[u8]) -> Result<(), wasm_bindgen::JsValue> {
|
|||
a.set_href(&url);
|
||||
a.set_download(file_name(name));
|
||||
a.style().set_property("display", "none")?;
|
||||
doc.body().ok_or("no body")?.append_child(&a)?;
|
||||
a.click();
|
||||
a.remove();
|
||||
let delay = DOWNLOADS.with(|d| d.borrow_mut().reserve(js_sys::Date::now()));
|
||||
let name = name.to_owned();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
if delay > 0.0 {
|
||||
sleep(delay as i32).await;
|
||||
}
|
||||
let clicked = match doc.body() {
|
||||
Some(body) => body.append_child(&a).map(|_| a.click()),
|
||||
None => Err("no body".into()),
|
||||
};
|
||||
a.remove();
|
||||
if let Err(e) = clicked {
|
||||
log::warn!("download {name}: {e:?}");
|
||||
}
|
||||
sleep(60_000).await;
|
||||
let _ = web_sys::Url::revoke_object_url(&url);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
pub mod api;
|
||||
pub mod audio;
|
||||
pub mod download_pacing;
|
||||
pub mod fs;
|
||||
pub mod import;
|
||||
pub mod opfs;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
//! Download spacing. It is pure, so it runs natively even though the rest of the crate is
|
||||
//! wasm32-only.
|
||||
|
||||
#[path = "../src/download_pacing.rs"]
|
||||
mod download_pacing;
|
||||
|
||||
use download_pacing::{GAP_MS, Pacer};
|
||||
|
||||
/// #378: 20 downloads requested in the same instant are spread out, not clicked in one burst.
|
||||
#[test]
|
||||
fn burst_of_downloads_is_spaced_out() {
|
||||
let mut p = Pacer::default();
|
||||
let delays: Vec<f64> = (0..20).map(|_| p.reserve(1000.0)).collect();
|
||||
assert_eq!(delays[0], 0.0);
|
||||
for (i, d) in delays.iter().enumerate() {
|
||||
assert_eq!(*d, i as f64 * GAP_MS);
|
||||
}
|
||||
}
|
||||
|
||||
/// A download after a quiet period is not delayed.
|
||||
#[test]
|
||||
fn download_after_a_pause_is_immediate() {
|
||||
let mut p = Pacer::default();
|
||||
assert_eq!(p.reserve(0.0), 0.0);
|
||||
assert_eq!(p.reserve(10_000.0), 0.0);
|
||||
}
|
||||
Loading…
Reference in New Issue