mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-22 15:21:04 +00:00
166 lines
4.4 KiB
Rust
166 lines
4.4 KiB
Rust
use std::{collections::{BTreeMap, BTreeSet}, path::PathBuf, sync::Arc};
|
|
|
|
use adaptor::Image;
|
|
use anyhow::Result;
|
|
use parking_lot::Mutex;
|
|
use shared::{calculate_size, Throttle};
|
|
use tokio::{fs, sync::mpsc};
|
|
|
|
use crate::{emit, external, files::{File, FilesOp}, tasks::TaskOp};
|
|
|
|
pub(crate) struct Precache {
|
|
rx: async_channel::Receiver<PrecacheOp>,
|
|
tx: async_channel::Sender<PrecacheOp>,
|
|
|
|
sch: mpsc::UnboundedSender<TaskOp>,
|
|
|
|
pub(crate) size_handing: Mutex<BTreeSet<PathBuf>>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) enum PrecacheOp {
|
|
Image(PrecacheOpImage),
|
|
Video(PrecacheOpVideo),
|
|
PDF(PrecacheOpPDF),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct PrecacheOpSize {
|
|
pub id: usize,
|
|
pub target: PathBuf,
|
|
pub throttle: Arc<Throttle<(PathBuf, File)>>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct PrecacheOpMime {
|
|
pub id: usize,
|
|
pub targets: Vec<PathBuf>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct PrecacheOpImage {
|
|
pub id: usize,
|
|
pub target: PathBuf,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct PrecacheOpVideo {
|
|
pub id: usize,
|
|
pub target: PathBuf,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct PrecacheOpPDF {
|
|
pub id: usize,
|
|
pub target: PathBuf,
|
|
}
|
|
|
|
impl Precache {
|
|
pub(crate) fn new(sch: mpsc::UnboundedSender<TaskOp>) -> Self {
|
|
let (tx, rx) = async_channel::unbounded();
|
|
Self { tx, rx, sch, size_handing: Default::default() }
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) async fn recv(&self) -> Result<(usize, PrecacheOp)> {
|
|
Ok(match self.rx.recv().await? {
|
|
PrecacheOp::Image(t) => (t.id, PrecacheOp::Image(t)),
|
|
PrecacheOp::Video(t) => (t.id, PrecacheOp::Video(t)),
|
|
PrecacheOp::PDF(t) => (t.id, PrecacheOp::PDF(t)),
|
|
})
|
|
}
|
|
|
|
pub(crate) async fn work(&self, task: &mut PrecacheOp) -> Result<()> {
|
|
match task {
|
|
PrecacheOp::Image(task) => {
|
|
let cache = Image::cache(&task.target);
|
|
if fs::metadata(&cache).await.is_ok() {
|
|
return Ok(self.sch.send(TaskOp::Adv(task.id, 1, 0))?);
|
|
}
|
|
if let Ok(img) = fs::read(&task.target).await {
|
|
Image::precache(img.into(), cache).await.ok();
|
|
}
|
|
self.sch.send(TaskOp::Adv(task.id, 1, 0))?;
|
|
}
|
|
PrecacheOp::Video(task) => {
|
|
let cache = Image::cache(&task.target);
|
|
if fs::metadata(&cache).await.is_ok() {
|
|
return Ok(self.sch.send(TaskOp::Adv(task.id, 1, 0))?);
|
|
}
|
|
|
|
external::ffmpegthumbnailer(&task.target, &cache).await.ok();
|
|
self.sch.send(TaskOp::Adv(task.id, 1, 0))?;
|
|
}
|
|
PrecacheOp::PDF(task) => {
|
|
let cache = Image::cache(&task.target);
|
|
if fs::metadata(&cache).await.is_ok() {
|
|
return Ok(self.sch.send(TaskOp::Adv(task.id, 1, 0))?);
|
|
}
|
|
|
|
external::pdftoppm(&task.target, &cache).await.ok();
|
|
self.sch.send(TaskOp::Adv(task.id, 1, 0))?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[inline]
|
|
fn done(&self, id: usize) -> Result<()> { Ok(self.sch.send(TaskOp::Done(id))?) }
|
|
|
|
pub(crate) async fn mime(&self, task: PrecacheOpMime) -> Result<()> {
|
|
self.sch.send(TaskOp::New(task.id, 0))?;
|
|
if let Ok(mimes) = external::file(&task.targets).await {
|
|
emit!(Mimetype(mimes));
|
|
}
|
|
|
|
self.sch.send(TaskOp::Adv(task.id, 1, 0))?;
|
|
self.done(task.id)
|
|
}
|
|
|
|
pub(crate) async fn size(&self, task: PrecacheOpSize) -> Result<()> {
|
|
self.sch.send(TaskOp::New(task.id, 0))?;
|
|
|
|
let length = Some(calculate_size(&task.target).await);
|
|
if let Ok(mut file) = File::from(&task.target).await {
|
|
file.length = length;
|
|
task.throttle.done((task.target, file), |buf| {
|
|
let mut handing = self.size_handing.lock();
|
|
for (path, _) in &buf {
|
|
handing.remove(path);
|
|
}
|
|
|
|
let parent = buf[0].0.parent().unwrap().to_path_buf();
|
|
emit!(Files(FilesOp::Sort(parent, BTreeMap::from_iter(buf))));
|
|
});
|
|
} else {
|
|
self.size_handing.lock().remove(&task.target);
|
|
};
|
|
|
|
self.sch.send(TaskOp::Adv(task.id, 1, 0))?;
|
|
self.done(task.id)
|
|
}
|
|
|
|
pub(crate) fn image(&self, id: usize, targets: Vec<PathBuf>) -> Result<()> {
|
|
for target in targets {
|
|
self.sch.send(TaskOp::New(id, 0))?;
|
|
self.tx.send_blocking(PrecacheOp::Image(PrecacheOpImage { id, target }))?;
|
|
}
|
|
self.done(id)
|
|
}
|
|
|
|
pub(crate) fn video(&self, id: usize, targets: Vec<PathBuf>) -> Result<()> {
|
|
for target in targets {
|
|
self.sch.send(TaskOp::New(id, 0))?;
|
|
self.tx.send_blocking(PrecacheOp::Video(PrecacheOpVideo { id, target }))?;
|
|
}
|
|
self.done(id)
|
|
}
|
|
|
|
pub(crate) fn pdf(&self, id: usize, targets: Vec<PathBuf>) -> Result<()> {
|
|
for target in targets {
|
|
self.sch.send(TaskOp::New(id, 0))?;
|
|
self.tx.send_blocking(PrecacheOp::PDF(PrecacheOpPDF { id, target }))?;
|
|
}
|
|
self.done(id)
|
|
}
|
|
}
|