Christoffer Martinsson 7ce264fd96 Implement Phase 1: Foundation and cache system
- Add Cargo project with TUI and async dependencies
- Implement cache-only architecture for low bandwidth operation
- Add file scanner with media type detection
- Create two-panel TUI layout (file tree and status)
- Add config file support for scan path management
- Implement XDG-compliant cache and config directories
- Add Gitea CI/CD workflow for automated releases
2025-12-06 12:32:17 +01:00

90 lines
2.7 KiB
Rust

use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileMetadata {
pub path: PathBuf,
pub size: u64,
pub duration: Option<f64>,
pub codec: Option<String>,
pub hash: Option<String>,
pub is_video: bool,
pub is_audio: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileTreeNode {
pub name: String,
pub path: PathBuf,
pub is_dir: bool,
pub children: Vec<FileTreeNode>,
pub metadata: Option<FileMetadata>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Cache {
pub file_tree: Vec<FileTreeNode>,
pub metadata: HashMap<PathBuf, FileMetadata>,
}
impl Cache {
pub fn new() -> Self {
Self {
file_tree: Vec::new(),
metadata: HashMap::new(),
}
}
pub fn load(cache_dir: &Path) -> Result<Self> {
let tree_path = cache_dir.join("file_tree.json");
let metadata_path = cache_dir.join("metadata.json");
if !tree_path.exists() || !metadata_path.exists() {
return Ok(Self::new());
}
let tree_data = fs::read_to_string(&tree_path)
.context("Failed to read file_tree.json")?;
let file_tree: Vec<FileTreeNode> = serde_json::from_str(&tree_data)
.context("Failed to parse file_tree.json")?;
let metadata_data = fs::read_to_string(&metadata_path)
.context("Failed to read metadata.json")?;
let metadata: HashMap<PathBuf, FileMetadata> = serde_json::from_str(&metadata_data)
.context("Failed to parse metadata.json")?;
Ok(Self {
file_tree,
metadata,
})
}
pub fn save(&self, cache_dir: &Path) -> Result<()> {
fs::create_dir_all(cache_dir)
.context("Failed to create cache directory")?;
let tree_path = cache_dir.join("file_tree.json");
let tree_data = serde_json::to_string_pretty(&self.file_tree)
.context("Failed to serialize file_tree")?;
fs::write(&tree_path, tree_data)
.context("Failed to write file_tree.json")?;
let metadata_path = cache_dir.join("metadata.json");
let metadata_data = serde_json::to_string_pretty(&self.metadata)
.context("Failed to serialize metadata")?;
fs::write(&metadata_path, metadata_data)
.context("Failed to write metadata.json")?;
Ok(())
}
}
pub fn get_cache_dir() -> Result<PathBuf> {
let cache_home = dirs::cache_dir()
.context("Failed to get cache directory")?;
Ok(cache_home.join("cm-player"))
}