mirror of
https://github.com/sxyazi/yazi.git
synced 2024-11-24 09:53:12 +03:00
feat: pre-cache PDF files
This commit is contained in:
parent
0485259302
commit
ad554bab89
@ -26,16 +26,10 @@ impl Image {
|
||||
})
|
||||
});
|
||||
|
||||
Ok(img.await??)
|
||||
img.await?
|
||||
}
|
||||
|
||||
pub async fn precache(path: &Path) -> Result<()> {
|
||||
let cache = Self::cache(path);
|
||||
if fs::metadata(&cache).await.is_ok() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let img = fs::read(&path).await?;
|
||||
pub async fn precache(img: Vec<u8>, cache: PathBuf) -> Result<()> {
|
||||
let result = tokio::task::spawn_blocking(move || {
|
||||
let img = image::load_from_memory(&img)?;
|
||||
let (w, h) = (PREVIEW.max_width, PREVIEW.max_height);
|
||||
@ -46,7 +40,7 @@ impl Image {
|
||||
Ok::<(), Error>(())
|
||||
});
|
||||
|
||||
Ok(result.await??)
|
||||
result.await?
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
4
core/src/external/ffmpegthumbnailer.rs
vendored
4
core/src/external/ffmpegthumbnailer.rs
vendored
@ -4,10 +4,10 @@ use anyhow::{bail, Result};
|
||||
use config::PREVIEW;
|
||||
use tokio::process::Command;
|
||||
|
||||
pub async fn ffmpegthumbnailer(path: &Path, dest: &Path) -> Result<()> {
|
||||
pub async fn ffmpegthumbnailer(src: &Path, dest: &Path) -> Result<()> {
|
||||
let output = Command::new("ffmpegthumbnailer")
|
||||
.arg("-i")
|
||||
.arg(path)
|
||||
.arg(src)
|
||||
.arg("-o")
|
||||
.arg(dest)
|
||||
.args(["-q", "6", "-c", "jpeg", "-s", &PREVIEW.max_width.to_string()])
|
||||
|
27
core/src/external/pdftoppm.rs
vendored
27
core/src/external/pdftoppm.rs
vendored
@ -1,22 +1,19 @@
|
||||
use std::path::Path;
|
||||
|
||||
use adaptor::Image;
|
||||
use anyhow::{bail, Result};
|
||||
use tokio::process::Command;
|
||||
|
||||
pub async fn pdftoppm(path: &Path, dest: &Path) -> Result<()> {
|
||||
let output = Command::new("pdftoppm")
|
||||
.args(["-png", "-singlefile"])
|
||||
.arg(path)
|
||||
.arg(dest)
|
||||
.kill_on_drop(true)
|
||||
.output()
|
||||
.await?;
|
||||
pub async fn pdftoppm(src: &Path, dest: &Path) -> Result<()> {
|
||||
let output = Command::new("pdftoppm")
|
||||
.args(["-singlefile", "-jpeg", "-jpegopt", "quality=75"])
|
||||
.arg(src)
|
||||
.kill_on_drop(true)
|
||||
.output()
|
||||
.await?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!(
|
||||
"failed to get pdf: {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
if !output.status.success() {
|
||||
bail!("failed to generate PDF thumbnail: {}", String::from_utf8_lossy(&output.stderr));
|
||||
}
|
||||
Ok(Image::precache(output.stdout, dest.to_path_buf()).await?)
|
||||
}
|
||||
|
@ -304,6 +304,7 @@ impl Manager {
|
||||
|
||||
tasks.precache_image(&mimes);
|
||||
tasks.precache_video(&mimes);
|
||||
tasks.precache_pdf(&mimes);
|
||||
|
||||
self.mimetype.extend(mimes);
|
||||
true
|
||||
|
@ -59,13 +59,13 @@ impl Preview {
|
||||
let (path, mime) = (path.to_path_buf(), mime.to_owned());
|
||||
self.handle = Some(tokio::spawn(async move {
|
||||
let result = match kind {
|
||||
MimeKind::Dir => Self::folder(&path).await,
|
||||
MimeKind::JSON => Self::json(&path).await.map(PreviewData::Text),
|
||||
MimeKind::Text => Self::highlight(&path).await.map(PreviewData::Text),
|
||||
MimeKind::Image => Self::image(&path).await,
|
||||
MimeKind::Pdf => Self::pdf(&path).await,
|
||||
MimeKind::Video => Self::video(&path).await,
|
||||
MimeKind::Archive => Self::archive(&path).await.map(PreviewData::Text),
|
||||
MimeKind::Dir => Self::folder(&path).await,
|
||||
MimeKind::Image => Self::image(&path).await,
|
||||
MimeKind::Video => Self::video(&path).await,
|
||||
MimeKind::JSON => Self::json(&path).await.map(PreviewData::Text),
|
||||
MimeKind::PDF => Self::pdf(&path).await,
|
||||
MimeKind::Text => Self::highlight(&path).await.map(PreviewData::Text),
|
||||
MimeKind::Others => Err(anyhow!("Unsupported mimetype: {mime}")),
|
||||
};
|
||||
|
||||
@ -118,17 +118,13 @@ impl Preview {
|
||||
Self::image(&cache).await
|
||||
}
|
||||
|
||||
pub async fn pdf(path: &Path) -> Result<PreviewData> {
|
||||
pub async fn pdf(path: &Path) -> Result<PreviewData> {
|
||||
let cache = Image::cache(path);
|
||||
if fs::metadata(&cache).await.is_err() {
|
||||
external::pdftoppm(path, &cache).await?;
|
||||
}
|
||||
|
||||
let mut dest = cache.into_os_string();
|
||||
dest.push(".png");
|
||||
|
||||
let dest = Path::new(&dest);
|
||||
Self::image(dest).await
|
||||
Self::image(&cache).await
|
||||
}
|
||||
|
||||
pub async fn json(path: &Path) -> Result<String> {
|
||||
|
@ -375,4 +375,11 @@ impl Scheduler {
|
||||
|
||||
self.precache.video(id, targets).ok();
|
||||
}
|
||||
|
||||
pub(super) fn precache_pdf(&self, targets: Vec<PathBuf>) {
|
||||
let name = format!("Precache of {} PDF files", targets.len());
|
||||
let id = self.running.write().add(name);
|
||||
|
||||
self.precache.pdf(id, targets).ok();
|
||||
}
|
||||
}
|
||||
|
@ -265,6 +265,19 @@ impl Tasks {
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn precache_pdf(&self, mimetype: &BTreeMap<PathBuf, String>) -> bool {
|
||||
let targets = mimetype
|
||||
.into_iter()
|
||||
.filter(|(_, m)| MimeKind::new(m) == MimeKind::PDF)
|
||||
.map(|(p, _)| p.clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !targets.is_empty() {
|
||||
self.scheduler.precache_pdf(targets);
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Tasks {
|
||||
|
@ -21,6 +21,7 @@ pub(crate) struct Precache {
|
||||
pub(crate) enum PrecacheOp {
|
||||
Image(PrecacheOpImage),
|
||||
Video(PrecacheOpVideo),
|
||||
PDF(PrecacheOpPDF),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -48,6 +49,12 @@ pub(crate) struct PrecacheOpVideo {
|
||||
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();
|
||||
@ -59,13 +66,20 @@ impl Precache {
|
||||
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) => {
|
||||
Image::precache(&task.target).await.ok();
|
||||
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, cache).await.ok();
|
||||
}
|
||||
self.sch.send(TaskOp::Adv(task.id, 1, 0))?;
|
||||
}
|
||||
PrecacheOp::Video(task) => {
|
||||
@ -77,6 +91,15 @@ impl Precache {
|
||||
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(())
|
||||
}
|
||||
@ -132,4 +155,12 @@ impl Precache {
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver"],"version":"0.2","language":"en","flagWords":[]}
|
||||
{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm"],"version":"0.2","language":"en","flagWords":[]}
|
||||
|
@ -2,13 +2,16 @@ pub const MIME_DIR: &str = "inode/directory";
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MimeKind {
|
||||
Archive,
|
||||
Dir,
|
||||
JSON,
|
||||
Text,
|
||||
|
||||
Image,
|
||||
Video,
|
||||
Pdf,
|
||||
Archive,
|
||||
|
||||
JSON,
|
||||
PDF,
|
||||
Text,
|
||||
|
||||
Others,
|
||||
}
|
||||
|
||||
@ -28,7 +31,6 @@ impl MimeKind {
|
||||
"message" => true,
|
||||
"model" => true,
|
||||
"multipart" => true,
|
||||
"pdf" => true,
|
||||
"text" => true,
|
||||
"video" => true,
|
||||
_ => false,
|
||||
@ -39,16 +41,16 @@ impl MimeKind {
|
||||
pub fn new(s: &str) -> Self {
|
||||
if s == MIME_DIR {
|
||||
Self::Dir
|
||||
} else if s == "application/json" {
|
||||
Self::JSON
|
||||
} else if s == "application/pdf" {
|
||||
Self::Pdf
|
||||
} else if s.starts_with("text/") || s.ends_with("/xml") {
|
||||
Self::Text
|
||||
} else if s.starts_with("image/") {
|
||||
Self::Image
|
||||
} else if s.starts_with("video/") {
|
||||
Self::Video
|
||||
} else if s == "application/json" {
|
||||
Self::JSON
|
||||
} else if s == "application/pdf" {
|
||||
Self::PDF
|
||||
} else if s == "application/x-bzip"
|
||||
|| s == "application/x-bzip2"
|
||||
|| s == "application/gzip"
|
||||
|
Loading…
Reference in New Issue
Block a user