feat: new cache_dir option (#96)

This commit is contained in:
三咲雅 · Misaki Masa 2023-08-31 18:21:06 +08:00 committed by GitHub
parent 4d4586409d
commit c0c1a6cae6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 85 additions and 50 deletions

View file

@ -1,7 +1,7 @@
use std::{path::{Path, PathBuf}, sync::atomic::{AtomicBool, Ordering}}; use std::{path::{Path, PathBuf}, sync::atomic::{AtomicBool, Ordering}};
use anyhow::Result; use anyhow::Result;
use config::{preview::PreviewAdaptor, BOOT, PREVIEW}; use config::{preview::PreviewAdaptor, PREVIEW};
use ratatui::prelude::Rect; use ratatui::prelude::Rect;
use shared::RoCell; use shared::RoCell;
use tokio::{fs, sync::mpsc::UnboundedSender}; use tokio::{fs, sync::mpsc::UnboundedSender};
@ -19,7 +19,7 @@ pub struct Adaptor;
impl Adaptor { impl Adaptor {
pub async fn image_show(mut path: &Path, rect: Rect) -> Result<()> { pub async fn image_show(mut path: &Path, rect: Rect) -> Result<()> {
let cache = BOOT.cache(path, 0); let cache = PREVIEW.cache(path, 0);
if fs::metadata(&cache).await.is_ok() { if fs::metadata(&cache).await.is_ok() {
path = cache.as_path(); path = cache.as_path();
} }

View file

@ -39,6 +39,7 @@
- tab_size: Tab width - tab_size: Tab width
- max_width: Maximum preview width for images and videos - max_width: Maximum preview width for images and videos
- max_height: Maximum preview height for images and videos - max_height: Maximum preview height for images and videos
- cache_dir: The system cache directory is used by default, and the cached files will go away on a reboot automatically. If you want to make it more persistent, you can specify the cache directory manually as an absolute path.
## opener ## opener

View file

@ -10,6 +10,7 @@ show_symlink = true
tab_size = 2 tab_size = 2
max_width = 600 max_width = 600
max_height = 900 max_height = 900
cache_dir = ""
[opener] [opener]
folder = [ folder = [

View file

@ -1,15 +1,13 @@
use std::{env, fs, path::{Path, PathBuf}, process, time::{self, SystemTime}}; use std::{env, fs, path::PathBuf, process};
use clap::{command, Parser}; use clap::{command, Parser};
use md5::{Digest, Md5};
use shared::absolute_path; use shared::absolute_path;
use crate::Xdg; use crate::{Xdg, PREVIEW};
#[derive(Debug)] #[derive(Debug)]
pub struct Boot { pub struct Boot {
pub cwd: PathBuf, pub cwd: PathBuf,
pub cache_dir: PathBuf,
pub state_dir: PathBuf, pub state_dir: PathBuf,
pub cwd_file: Option<PathBuf>, pub cwd_file: Option<PathBuf>,
@ -43,46 +41,37 @@ impl Default for Boot {
let cwd = args let cwd = args
.cwd .cwd
.map(|p| futures::executor::block_on(absolute_path(p))) .map(|p| futures::executor::block_on(absolute_path(p)))
.and_then(|p| p.is_dir().then_some(p)) .filter(|p| p.is_dir())
.or_else(|| env::current_dir().ok()); .or_else(|| env::current_dir().ok());
let boot = Self { let boot = Self {
cwd: cwd.unwrap_or("/".into()), cwd: cwd.unwrap_or("/".into()),
cache_dir: env::temp_dir().join("yazi"),
state_dir: Xdg::state_dir().unwrap(), state_dir: Xdg::state_dir().unwrap(),
cwd_file: args.cwd_file, cwd_file: args.cwd_file,
chooser_file: args.chooser_file, chooser_file: args.chooser_file,
}; };
if !boot.cache_dir.is_dir() {
fs::create_dir(&boot.cache_dir).unwrap();
}
if !boot.state_dir.is_dir() { if !boot.state_dir.is_dir() {
fs::create_dir_all(&boot.state_dir).unwrap(); fs::create_dir_all(&boot.state_dir).unwrap();
} }
if !PREVIEW.cache_dir.is_dir() {
fs::create_dir(&PREVIEW.cache_dir).unwrap();
}
if args.clear_cache { if args.clear_cache {
println!("Clearing cache directory: {:?}", boot.cache_dir); if PREVIEW.cache_dir == Xdg::cache_dir() {
fs::remove_dir_all(&boot.cache_dir).unwrap(); println!("Clearing cache directory: \n{:?}", PREVIEW.cache_dir);
fs::remove_dir_all(&PREVIEW.cache_dir).unwrap();
} else {
println!(
"You've changed the default cache directory, for your data's safety, please clear it manually: \n{:?}",
PREVIEW.cache_dir
);
}
process::exit(0); process::exit(0);
} }
boot boot
} }
} }
impl Boot {
#[inline]
pub fn cache(&self, path: &Path, skip: usize) -> PathBuf {
self
.cache_dir
.join(format!("{:x}", Md5::new_with_prefix(format!("{:?}///{}", path, skip)).finalize()))
}
#[inline]
pub fn tmpfile(&self, prefix: &str) -> PathBuf {
let nanos = SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos();
self.cache_dir.join(format!("{prefix}-{}", nanos / 1000))
}
}

View file

@ -23,7 +23,6 @@ static MERGED_KEYMAP: RoCell<String> = RoCell::new();
static MERGED_THEME: RoCell<String> = RoCell::new(); static MERGED_THEME: RoCell<String> = RoCell::new();
static MERGED_YAZI: RoCell<String> = RoCell::new(); static MERGED_YAZI: RoCell<String> = RoCell::new();
pub static BOOT: RoCell<boot::Boot> = RoCell::new();
pub static KEYMAP: RoCell<keymap::Keymap> = RoCell::new(); pub static KEYMAP: RoCell<keymap::Keymap> = RoCell::new();
pub static LOG: RoCell<log::Log> = RoCell::new(); pub static LOG: RoCell<log::Log> = RoCell::new();
pub static MANAGER: RoCell<manager::Manager> = RoCell::new(); pub static MANAGER: RoCell<manager::Manager> = RoCell::new();
@ -32,12 +31,13 @@ pub static PREVIEW: RoCell<preview::Preview> = RoCell::new();
pub static TASKS: RoCell<tasks::Tasks> = RoCell::new(); pub static TASKS: RoCell<tasks::Tasks> = RoCell::new();
pub static THEME: RoCell<theme::Theme> = RoCell::new(); pub static THEME: RoCell<theme::Theme> = RoCell::new();
pub static BOOT: RoCell<boot::Boot> = RoCell::new();
pub fn init() { pub fn init() {
MERGED_KEYMAP.with(Preset::keymap); MERGED_KEYMAP.with(Preset::keymap);
MERGED_THEME.with(Preset::theme); MERGED_THEME.with(Preset::theme);
MERGED_YAZI.with(Preset::yazi); MERGED_YAZI.with(Preset::yazi);
BOOT.with(Default::default);
KEYMAP.with(Default::default); KEYMAP.with(Default::default);
LOG.with(Default::default); LOG.with(Default::default);
MANAGER.with(Default::default); MANAGER.with(Default::default);
@ -45,4 +45,6 @@ pub fn init() {
PREVIEW.with(Default::default); PREVIEW.with(Default::default);
TASKS.with(Default::default); TASKS.with(Default::default);
THEME.with(Default::default); THEME.with(Default::default);
BOOT.with(Default::default);
} }

View file

@ -1,26 +1,65 @@
use std::{path::{Path, PathBuf}, time::{self, SystemTime}};
use md5::{Digest, Md5};
use serde::Deserialize; use serde::Deserialize;
use super::PreviewAdaptor; use super::PreviewAdaptor;
use crate::MERGED_YAZI; use crate::{xdg::Xdg, MERGED_YAZI};
#[derive(Debug, Deserialize)] #[derive(Debug)]
pub struct Preview { pub struct Preview {
#[serde(skip)] pub adaptor: PreviewAdaptor,
pub adaptor: PreviewAdaptor,
pub tab_size: u32,
pub tab_size: u32,
pub max_width: u32, pub max_width: u32,
pub max_height: u32, pub max_height: u32,
pub cache_dir: PathBuf,
} }
impl Default for Preview { impl Default for Preview {
fn default() -> Self { fn default() -> Self {
#[derive(Deserialize)] #[derive(Deserialize)]
struct Outer { struct Outer {
preview: Preview, preview: Shadow,
}
#[derive(Deserialize)]
struct Shadow {
pub tab_size: u32,
pub max_width: u32,
pub max_height: u32,
pub cache_dir: Option<String>,
} }
let outer: Outer = toml::from_str(&MERGED_YAZI).unwrap(); let preview = toml::from_str::<Outer>(&MERGED_YAZI).unwrap().preview;
outer.preview
let cache_dir =
preview.cache_dir.filter(|p| !p.is_empty()).map_or_else(Xdg::cache_dir, PathBuf::from);
Preview {
adaptor: Default::default(),
tab_size: preview.tab_size,
max_width: preview.max_width,
max_height: preview.max_height,
cache_dir,
}
}
}
impl Preview {
#[inline]
pub fn cache(&self, path: &Path, skip: usize) -> PathBuf {
self
.cache_dir
.join(format!("{:x}", Md5::new_with_prefix(format!("{:?}///{}", path, skip)).finalize()))
}
#[inline]
pub fn tmpfile(&self, prefix: &str) -> PathBuf {
let nanos = SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos();
self.cache_dir.join(format!("{prefix}-{}", nanos / 1000))
} }
} }

View file

@ -1,4 +1,4 @@
use std::path::PathBuf; use std::{env, path::PathBuf};
pub(super) struct Xdg; pub(super) struct Xdg;
@ -12,7 +12,7 @@ impl Xdg {
{ {
std::env::var_os("XDG_CONFIG_HOME") std::env::var_os("XDG_CONFIG_HOME")
.map(PathBuf::from) .map(PathBuf::from)
.and_then(|p| p.is_absolute().then_some(p)) .filter(|p| p.is_absolute())
.or_else(|| dirs::home_dir().map(|h| h.join(".config"))) .or_else(|| dirs::home_dir().map(|h| h.join(".config")))
.map(|p| p.join("yazi")) .map(|p| p.join("yazi"))
} }
@ -27,9 +27,12 @@ impl Xdg {
{ {
std::env::var_os("XDG_STATE_HOME") std::env::var_os("XDG_STATE_HOME")
.map(PathBuf::from) .map(PathBuf::from)
.and_then(|p| p.is_absolute().then_some(p)) .filter(|p| p.is_absolute())
.or_else(|| dirs::home_dir().map(|h| h.join(".local/state"))) .or_else(|| dirs::home_dir().map(|h| h.join(".local/state")))
.map(|p| p.join("yazi")) .map(|p| p.join("yazi"))
} }
} }
#[inline]
pub(super) fn cache_dir() -> PathBuf { env::temp_dir().join("yazi") }
} }

View file

@ -1,7 +1,7 @@
use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, env, ffi::OsStr, io::{stdout, BufWriter, Write}, mem, path::{Path, PathBuf}}; use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, env, ffi::OsStr, io::{stdout, BufWriter, Write}, mem, path::{Path, PathBuf}};
use anyhow::{anyhow, bail, Error, Result}; use anyhow::{anyhow, bail, Error, Result};
use config::{BOOT, OPEN}; use config::{OPEN, PREVIEW};
use shared::{max_common_root, Defer, Term, MIME_DIR}; use shared::{max_common_root, Defer, Term, MIME_DIR};
use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}};
@ -222,7 +222,7 @@ impl Manager {
let root = max_common_root(&old); let root = max_common_root(&old);
let old: Vec<_> = old.into_iter().map(|p| p.strip_prefix(&root).unwrap().to_owned()).collect(); let old: Vec<_> = old.into_iter().map(|p| p.strip_prefix(&root).unwrap().to_owned()).collect();
let tmp = BOOT.tmpfile("bulk"); let tmp = PREVIEW.tmpfile("bulk");
tokio::spawn(async move { tokio::spawn(async move {
let Some(opener) = OPEN.block_opener("bulk.txt", "text/plain") else { let Some(opener) = OPEN.block_opener("bulk.txt", "text/plain") else {
bail!("No opener for bulk rename"); bail!("No opener for bulk rename");

View file

@ -2,7 +2,7 @@ use std::{io::BufRead, path::Path, sync::atomic::{AtomicUsize, Ordering}};
use adaptor::Adaptor; use adaptor::Adaptor;
use anyhow::anyhow; use anyhow::anyhow;
use config::{BOOT, MANAGER, PREVIEW}; use config::{MANAGER, PREVIEW};
use shared::{MimeKind, PeekError}; use shared::{MimeKind, PeekError};
use syntect::{easy::HighlightFile, util::as_24_bit_terminal_escaped}; use syntect::{easy::HighlightFile, util::as_24_bit_terminal_escaped};
use tokio::fs; use tokio::fs;
@ -62,7 +62,7 @@ impl Provider {
} }
pub(super) async fn video(path: &Path, skip: usize) -> Result<PreviewData, PeekError> { pub(super) async fn video(path: &Path, skip: usize) -> Result<PreviewData, PeekError> {
let cache = BOOT.cache(path, skip); let cache = PREVIEW.cache(path, skip);
if fs::metadata(&cache).await.is_err() { if fs::metadata(&cache).await.is_err() {
external::ffmpegthumbnailer(path, &cache, skip).await?; external::ffmpegthumbnailer(path, &cache, skip).await?;
} }
@ -71,7 +71,7 @@ impl Provider {
} }
pub(super) async fn pdf(path: &Path, skip: usize) -> Result<PreviewData, PeekError> { pub(super) async fn pdf(path: &Path, skip: usize) -> Result<PreviewData, PeekError> {
let cache = BOOT.cache(path, skip); let cache = PREVIEW.cache(path, skip);
if fs::metadata(&cache).await.is_err() { if fs::metadata(&cache).await.is_err() {
external::pdftoppm(path, &cache, skip).await?; external::pdftoppm(path, &cache, skip).await?;
} }

View file

@ -2,7 +2,7 @@ use std::{collections::{BTreeMap, BTreeSet}, path::PathBuf, sync::Arc};
use adaptor::Image; use adaptor::Image;
use anyhow::Result; use anyhow::Result;
use config::BOOT; use config::PREVIEW;
use parking_lot::Mutex; use parking_lot::Mutex;
use shared::{calculate_size, Throttle}; use shared::{calculate_size, Throttle};
use tokio::{fs, sync::mpsc}; use tokio::{fs, sync::mpsc};
@ -74,7 +74,7 @@ impl Precache {
pub(crate) async fn work(&self, task: &mut PrecacheOp) -> Result<()> { pub(crate) async fn work(&self, task: &mut PrecacheOp) -> Result<()> {
match task { match task {
PrecacheOp::Image(task) => { PrecacheOp::Image(task) => {
let cache = BOOT.cache(&task.target, 0); let cache = PREVIEW.cache(&task.target, 0);
if fs::metadata(&cache).await.is_ok() { if fs::metadata(&cache).await.is_ok() {
return Ok(self.sch.send(TaskOp::Adv(task.id, 1, 0))?); return Ok(self.sch.send(TaskOp::Adv(task.id, 1, 0))?);
} }
@ -84,7 +84,7 @@ impl Precache {
self.sch.send(TaskOp::Adv(task.id, 1, 0))?; self.sch.send(TaskOp::Adv(task.id, 1, 0))?;
} }
PrecacheOp::Video(task) => { PrecacheOp::Video(task) => {
let cache = BOOT.cache(&task.target, 0); let cache = PREVIEW.cache(&task.target, 0);
if fs::metadata(&cache).await.is_ok() { if fs::metadata(&cache).await.is_ok() {
return Ok(self.sch.send(TaskOp::Adv(task.id, 1, 0))?); return Ok(self.sch.send(TaskOp::Adv(task.id, 1, 0))?);
} }
@ -93,7 +93,7 @@ impl Precache {
self.sch.send(TaskOp::Adv(task.id, 1, 0))?; self.sch.send(TaskOp::Adv(task.id, 1, 0))?;
} }
PrecacheOp::Pdf(task) => { PrecacheOp::Pdf(task) => {
let cache = BOOT.cache(&task.target, 0); let cache = PREVIEW.cache(&task.target, 0);
if fs::metadata(&cache).await.is_ok() { if fs::metadata(&cache).await.is_ok() {
return Ok(self.sch.send(TaskOp::Adv(task.id, 1, 0))?); return Ok(self.sch.send(TaskOp::Adv(task.id, 1, 0))?);
} }