Skip to content

Commit

Permalink
feat: archive preview
Browse files Browse the repository at this point in the history
  • Loading branch information
sxyazi committed Jul 17, 2023
1 parent 4af52c7 commit b438501
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 7 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ once_cell = "1.18.0"
parking_lot = "0.12.1"
ratatui = "0.21.0"
serde = { version = "1.0.164", features = [ "derive" ] }
serde_json = "1.0.103"
signal-hook-tokio = { version = "0.3.1", features = [ "futures-v0_3" ] }
syntect = "5.0.0"
tokio = { version = "1.28.2", features = [ "parking_lot", "macros", "rt-multi-thread", "sync", "fs", "process", "io-std", "io-util", "time" ] }
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,30 @@

Yazi ("duck" in Chinese) is a terminal file manager written in Rust, based on non-blocking async I/O. It aims to provide an efficient, user-friendly, and configurable file management experience.

⚠️ Note: Yazi is currently in active development and may be unstable. The API is subject to change without prior notice. Please use it with caution in a non-production environment.
⚠️ Note: Yazi is currently in active development and may be unstable. The API is subject to change without prior notice.

## Installation

Before getting started, ensure that the following dependencies are installed on your system:

- nerd-fonts (required, for icons)
- jq (optional, for JSON preview)
- unar (optional, for archive preview)
- ffmpegthumbnailer (optional, for video thumbnails)
- fd (optional, for file searching)
- rg (optional, for file content searching)
- fzf (optional, for directory jumping)
- zoxide (optional, for directory jumping)

```bash
# Arch Linux
pacman -S ttf-nerd-fonts-symbols jq unarchiver ffmpegthumbnailer fd ripgrep fzf zoxide

# macOS
brew install jq unar ffmpegthumbnailer fd ripgrep fzf zoxide
brew tap homebrew/cask-fonts && brew install --cask font-symbols-only-nerd-font
```

Execute the following commands to clone the project and build Yazi:

```bash
Expand Down
65 changes: 65 additions & 0 deletions src/core/external/lsar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use std::path::Path;

use anyhow::{bail, Result};
use serde::Deserialize;
use serde_json::Value;
use tokio::process::Command;
use tracing::info;

#[derive(Debug)]
pub enum LsarAttr {
Posix(u16),
Windows(u16),
Dos(u8),
}

#[derive(Debug, Deserialize)]
pub struct LsarFile {
#[serde(rename = "XADFileName")]
pub name: String,
#[serde(rename = "XADLastModificationDate")]
pub last_modified: String,
#[serde(rename = "XADFileSize")]
pub size: Option<usize>,
#[serde(rename = "XADCompressedSize")]
pub compressed_size: Option<usize>,
#[serde(rename = "XADCompressionName")]
pub compression_name: Option<String>,

#[serde(skip)]
pub attributes: Option<LsarAttr>,
}

pub async fn lsar(path: &Path) -> Result<Vec<LsarFile>> {
let output =
Command::new("lsar").args(["-j", "-jss"]).arg(path).kill_on_drop(true).output().await?;

if !output.status.success() {
bail!("failed to get json: {}", String::from_utf8_lossy(&output.stderr));
}

#[derive(Deserialize)]
struct Outer {
#[serde(rename = "lsarContents")]
contents: Vec<Value>,
}

let output = String::from_utf8_lossy(&output.stdout);
info!("lsar output: {}", output);
let contents = serde_json::from_str::<Outer>(output.trim())?.contents;

let mut files = Vec::with_capacity(contents.len());
for content in contents {
let mut file = serde_json::from_value::<LsarFile>(content.clone())?;
if let Some(p) = content.get("XADPosixPermissions").and_then(|p| p.as_u64()) {
file.attributes = Some(LsarAttr::Posix(p as u16));
} else if let Some(a) = content.get("XADWindowsFileAttributes").and_then(|a| a.as_u64()) {
file.attributes = Some(LsarAttr::Windows(a as u16));
} else if let Some(a) = content.get("XADDOSFileAttributes").and_then(|a| a.as_u64()) {
file.attributes = Some(LsarAttr::Dos(a as u8));
}

files.push(file);
}
Ok(files)
}
2 changes: 2 additions & 0 deletions src/core/external/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod ffmpegthumbnailer;
mod file;
mod fzf;
mod jq;
mod lsar;
mod rg;
mod zoxide;

Expand All @@ -11,5 +12,6 @@ pub use ffmpegthumbnailer::*;
pub use file::*;
pub use fzf::*;
pub use jq::*;
pub use lsar::*;
pub use rg::*;
pub use zoxide::*;
2 changes: 1 addition & 1 deletion src/core/manager/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, mem, path::PathBu
use tokio::fs;

use super::{PreviewData, Tab, Tabs, Watcher};
use crate::{core::{external::{self}, files::{File, FilesOp}, input::{InputOpt, InputPos}, manager::Folder, tasks::Tasks}, emit};
use crate::{core::{external, files::{File, FilesOp}, input::{InputOpt, InputPos}, manager::Folder, tasks::Tasks}, emit};

pub struct Manager {
tabs: Tabs,
Expand Down
26 changes: 23 additions & 3 deletions src/core/manager/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use syntect::{easy::HighlightLines, highlighting::{Theme, ThemeSet}, parsing::Sy
use tokio::{fs, task::JoinHandle};

use super::{ALL_RATIO, PREVIEW_BORDER, PREVIEW_PADDING, PREVIEW_RATIO};
use crate::{config::{PREVIEW, THEME}, core::{adapter::Kitty, external::{ffmpegthumbnailer, jq}, files::{Files, FilesOp}, tasks::Precache}, emit, misc::{first_n_lines, tty_ratio, tty_size, MimeKind}};
use crate::{config::{PREVIEW, THEME}, core::{adapter::Kitty, external, files::{Files, FilesOp}, tasks::Precache}, emit, misc::{first_n_lines, tty_ratio, tty_size, MimeKind}};

static SYNTECT_SYNTAX: OnceLock<SyntaxSet> = OnceLock::new();
static SYNTECT_THEME: OnceLock<Theme> = OnceLock::new();
Expand Down Expand Up @@ -51,6 +51,7 @@ impl Preview {
MimeKind::Text => Self::highlight(&path).await.map(PreviewData::Text),
MimeKind::Image => Self::image(&path).await.map(PreviewData::Image),
MimeKind::Video => Self::video(&path).await.map(PreviewData::Image),
MimeKind::Archive => Self::archive(&path).await.map(PreviewData::Text),
MimeKind::Others => Err(anyhow!("Unsupported mimetype: {}", mime)),
};

Expand Down Expand Up @@ -105,14 +106,33 @@ impl Preview {
pub async fn video(path: &Path) -> Result<Vec<u8>> {
let cache = Precache::cache(path);
if fs::metadata(&cache).await.is_err() {
ffmpegthumbnailer(path, &cache).await?;
external::ffmpegthumbnailer(path, &cache).await?;
}

Self::image(&cache).await
}

pub async fn json(path: &Path) -> Result<String> {
Ok(jq(path).await?.lines().take(Self::size().1 as usize).collect::<Vec<_>>().join("\n"))
Ok(
external::jq(path)
.await?
.lines()
.take(Self::size().1 as usize)
.collect::<Vec<_>>()
.join("\n"),
)
}

pub async fn archive(path: &Path) -> Result<String> {
Ok(
external::lsar(path)
.await?
.into_iter()
.take(Self::size().1 as usize)
.map(|f| f.name)
.collect::<Vec<_>>()
.join("\n"),
)
}

pub async fn highlight(path: &Path) -> Result<String> {
Expand Down
4 changes: 2 additions & 2 deletions src/core/tasks/precache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use image::{imageops::FilterType, ImageFormat};
use tokio::{fs, sync::mpsc};

use super::TaskOp;
use crate::{config::PREVIEW, core::external::{self, ffmpegthumbnailer}, emit};
use crate::{config::PREVIEW, core::external, emit};

pub struct Precache {
rx: async_channel::Receiver<PrecacheOp>,
Expand Down Expand Up @@ -79,7 +79,7 @@ impl Precache {
return Ok(self.sch.send(TaskOp::Adv(task.id, 1, 0))?);
}

ffmpegthumbnailer(&task.target, &cache).await.ok();
external::ffmpegthumbnailer(&task.target, &cache).await.ok();
self.sch.send(TaskOp::Adv(task.id, 1, 0))?;
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/misc/mime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub enum MimeKind {
Text,
Image,
Video,
Archive,
Others,
}

Expand Down Expand Up @@ -42,6 +43,15 @@ impl MimeKind {
Self::Image
} else if s.starts_with("video/") {
Self::Video
} else if s == "application/x-bzip"
|| s == "application/x-bzip2"
|| s == "application/gzip"
|| s == "application/vnd.rar"
|| s == "application/x-tar"
|| s == "application/zip"
|| s == "application/x-7z-compressed"
{
Self::Archive
} else {
Self::Others
}
Expand Down

0 comments on commit b438501

Please sign in to comment.