Add standalone media and remote downloads
This commit is contained in:
parent
8a213462e0
commit
5f9b5d9796
14 changed files with 683 additions and 86 deletions
|
|
@ -17,6 +17,7 @@ use crate::browser::BrowserEntry;
|
|||
pub struct ArchiveSource {
|
||||
pub local_path: PathBuf,
|
||||
pub source_uri: String,
|
||||
pub display_name: String,
|
||||
_temporary: Option<Rc<tempfile::TempPath>>,
|
||||
_pin: Option<Rc<CachePin>>,
|
||||
}
|
||||
|
|
@ -75,6 +76,7 @@ impl ZipCache {
|
|||
callback(Ok(ArchiveSource {
|
||||
local_path: path,
|
||||
source_uri: entry.uri,
|
||||
display_name: entry.name,
|
||||
_temporary: None,
|
||||
_pin: None,
|
||||
}));
|
||||
|
|
@ -82,24 +84,50 @@ impl ZipCache {
|
|||
}
|
||||
|
||||
if self.quota_bytes == 0 || entry.size.is_some_and(|size| size > self.quota_bytes) {
|
||||
self.download_temporary(source, entry.uri, entry.size, progress, callback);
|
||||
self.download_temporary(source, entry, true, progress, callback);
|
||||
} else {
|
||||
self.download_persistent(source, entry, progress, callback);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare_media<F, P>(self: &Rc<Self>, entry: BrowserEntry, progress: P, callback: F)
|
||||
where
|
||||
F: FnOnce(Result<ArchiveSource>) + 'static,
|
||||
P: FnMut(i64, i64) + 'static,
|
||||
{
|
||||
let source = gio::File::for_uri(&entry.uri);
|
||||
if let Some(path) = native_path(&source) {
|
||||
callback(Ok(ArchiveSource {
|
||||
local_path: path,
|
||||
source_uri: entry.uri,
|
||||
display_name: entry.name,
|
||||
_temporary: None,
|
||||
_pin: None,
|
||||
}));
|
||||
return;
|
||||
}
|
||||
self.download_temporary(source, entry, false, progress, callback);
|
||||
}
|
||||
|
||||
fn download_temporary<F, P>(
|
||||
self: &Rc<Self>,
|
||||
source: gio::File,
|
||||
source_uri: String,
|
||||
expected_size: Option<u64>,
|
||||
entry: BrowserEntry,
|
||||
validate_zip: bool,
|
||||
progress: P,
|
||||
callback: F,
|
||||
) where
|
||||
F: FnOnce(Result<ArchiveSource>) + 'static,
|
||||
P: FnMut(i64, i64) + 'static,
|
||||
{
|
||||
let temporary = match tempfile::Builder::new().suffix(".zip.part").tempfile() {
|
||||
let suffix = Path::new(&entry.name)
|
||||
.extension()
|
||||
.and_then(|extension| extension.to_str())
|
||||
.map_or_else(
|
||||
|| ".part".to_owned(),
|
||||
|extension| format!(".part.{extension}"),
|
||||
);
|
||||
let temporary = match tempfile::Builder::new().suffix(&suffix).tempfile() {
|
||||
Ok(file) => Rc::new(file.into_temp_path()),
|
||||
Err(error) => {
|
||||
callback(Err(error.into()));
|
||||
|
|
@ -108,6 +136,7 @@ impl ZipCache {
|
|||
};
|
||||
let destination = gio::File::for_path(temporary.as_ref());
|
||||
let retained = Rc::clone(&temporary);
|
||||
let expected_size = entry.size;
|
||||
source.copy_async(
|
||||
&destination,
|
||||
gio::FileCopyFlags::OVERWRITE,
|
||||
|
|
@ -116,10 +145,14 @@ impl ZipCache {
|
|||
Some(Box::new(progress)),
|
||||
move |result| {
|
||||
callback(result.map_err(Into::into).and_then(|()| {
|
||||
validate_download(retained.as_ref(), expected_size)?;
|
||||
validate_size(retained.as_ref(), expected_size)?;
|
||||
if validate_zip {
|
||||
validate_zip_file(retained.as_ref())?;
|
||||
}
|
||||
Ok(ArchiveSource {
|
||||
local_path: retained.to_path_buf(),
|
||||
source_uri,
|
||||
source_uri: entry.uri,
|
||||
display_name: entry.name,
|
||||
_temporary: Some(retained),
|
||||
_pin: None,
|
||||
})
|
||||
|
|
@ -143,7 +176,7 @@ impl ZipCache {
|
|||
if final_path.is_file() {
|
||||
if validate_download(&final_path, entry.size).is_ok() {
|
||||
let _ = filetime::set_file_mtime(&final_path, FileTime::now());
|
||||
callback(Ok(self.lease(final_path, entry.uri)));
|
||||
callback(Ok(self.lease(final_path, entry.uri, entry.name)));
|
||||
return;
|
||||
}
|
||||
let _ = fs::remove_file(&final_path);
|
||||
|
|
@ -168,7 +201,7 @@ impl ZipCache {
|
|||
)
|
||||
})?;
|
||||
cache.enforce_quota(&final_path)?;
|
||||
Ok(cache.lease(final_path, entry.uri))
|
||||
Ok(cache.lease(final_path, entry.uri, entry.name))
|
||||
});
|
||||
if result.is_err() {
|
||||
let _ = fs::remove_file(&part_path);
|
||||
|
|
@ -205,11 +238,17 @@ impl ZipCache {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn lease(self: &Rc<Self>, path: PathBuf, source_uri: String) -> ArchiveSource {
|
||||
fn lease(
|
||||
self: &Rc<Self>,
|
||||
path: PathBuf,
|
||||
source_uri: String,
|
||||
display_name: String,
|
||||
) -> ArchiveSource {
|
||||
*self.pinned.borrow_mut().entry(path.clone()).or_default() += 1;
|
||||
ArchiveSource {
|
||||
local_path: path.clone(),
|
||||
source_uri,
|
||||
display_name,
|
||||
_temporary: None,
|
||||
_pin: Some(Rc::new(CachePin {
|
||||
path,
|
||||
|
|
@ -224,6 +263,11 @@ fn native_path(file: &gio::File) -> Option<PathBuf> {
|
|||
}
|
||||
|
||||
fn validate_download(path: &Path, expected_size: Option<u64>) -> Result<()> {
|
||||
validate_size(path, expected_size)?;
|
||||
validate_zip_file(path)
|
||||
}
|
||||
|
||||
fn validate_size(path: &Path, expected_size: Option<u64>) -> Result<()> {
|
||||
let actual_size = fs::metadata(path)?.len();
|
||||
if let Some(expected_size) = expected_size
|
||||
&& actual_size != expected_size
|
||||
|
|
@ -232,6 +276,10 @@ fn validate_download(path: &Path, expected_size: Option<u64>) -> Result<()> {
|
|||
"다운로드 크기가 원본과 다릅니다: 예상 {expected_size}바이트, 실제 {actual_size}바이트"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_zip_file(path: &Path) -> Result<()> {
|
||||
let file = File::open(path)?;
|
||||
zip::ZipArchive::new(file).context("다운로드된 파일이 올바른 ZIP이 아닙니다")?;
|
||||
Ok(())
|
||||
|
|
@ -307,7 +355,11 @@ mod tests {
|
|||
pinned: RefCell::new(HashMap::new()),
|
||||
});
|
||||
|
||||
let lease = cache.lease(first.clone(), "file:///first.zip".into());
|
||||
let lease = cache.lease(
|
||||
first.clone(),
|
||||
"file:///first.zip".into(),
|
||||
"first.zip".into(),
|
||||
);
|
||||
cache.enforce_quota(&second).unwrap();
|
||||
assert!(first.exists());
|
||||
assert!(second.exists());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue