使用 sd-client 连接 Spacedrive Daemon:Rust 客户端库的查询、执行与缩略图构建实战
2026/9/19 5:12:59 网站建设 项目流程

使用 sd-client 连接 Spacedrive Daemon:Rust 客户端库的查询、执行与缩略图构建实战

【免费下载链接】spacedriveSpacedrive is an open source cross-platform file explorer, powered by a virtual distributed filesystem written in Rust.项目地址: https://gitcode.com/gh_mirrors/sp/spacedrive

sd-client是 Spacedrive 仓库中面向 Rust 开发者的 daemon 客户端库,负责通过 Unix socket 与 Spacedrive Core 通信,并以类型安全的方式执行查询(Query)与动作(Action),同时提供媒体文件列取与缩略图 URL 构建等高频能力。读完本文,你将掌握SpacedriveClient的完整 API 用法、SdPath地址模型与缩略图变体选择算法,并能基于仓库内的示例程序快速搭建自己的连接代码。

库定位与功能总览

根据 crates/sd-client/README.md,sd-client是一个用于连接 Spacedrive daemon 的 Rust 客户端库,核心特性如下:

  • Unix socket 通信:通过本地 socket 与 Spacedrive Core 交互,避免网络开销;
  • 类型安全的查询与动作执行execute方法按query:/action:前缀区分请求类型,并借助 serde 完成强类型序列化/反序列化;
  • 媒体文件列取查询:内置media_listing查询,直接返回File领域模型列表;
  • 缩略图 URL 构造:根据内容 UUID、变体与格式拼接 HTTP 缩略图地址;
  • 智能缩略图变体选择select_best_thumbnail依据目标尺寸自动挑选最合适的已就绪缩略图。

从 crates/sd-client/Cargo.toml 可以看到,该库基于tokionetio-utilrt特性)、serde/serde_jsonanyhow构建,并直接依赖同仓库的sd-corepath = "../../core"),类型层面复用了 Core 的领域模型。

快速开始:最小可用示例

创建客户端并设置库上下文

SpacedriveClient::new接受两个参数:daemon 的 socket 地址与 HTTP 服务基础 URL(用于缩略图等资源访问)。随后通过set_library设定当前库上下文:

use sd_client::{SpacedriveClient, SdPath}; #[tokio::main] async fn main() -> anyhow::Result<()> { // Create client let mut client = SpacedriveClient::new( "/path/to/daemon.sock".into(), "http://localhost:54321".into(), ); // Set library context client.set_library("library-uuid".to_string()); // Query media files let files = client.media_listing( SdPath::Physical { device_id: "local".to_string(), path: "/Users/you/Photos".to_string(), }, Some(1000), ).await?; // Get thumbnail URLs for file in files { if let Some(content_id) = file.content_identity { if let Some(thumb) = client.select_best_thumbnail(&file.sidecars, 256.0) { let url = client.thumbnail_url( &content_id.uuid, &thumb.variant, &thumb.format, ); println!("{}: {}", file.name, url); } } } Ok(()) }

需要说明的是,该示例来自 README 本身,其中SdPath::Physical的字段命名(device_id)与仓库当前实现存在差异:实际定义在 core/src/domain/addressing.rs 中,字段名为device_slugpath。因此可运行版本应写作:

SdPath::Physical { device_slug: "local".to_string(), path: "/Users/you/Photos".into(), }

SdPath::local()便捷构造器(addressing.rs)会自动填入当前设备的 slug,也可直接使用。

运行官方示例程序

仓库提供了test_connection示例,通过环境变量配置连接参数并打印媒体文件及缩略图 URL:

export SD_LIBRARY_ID="your-library-uuid" export SD_SOCKET_PATH="$HOME/.spacedrive/daemon.sock" # optional export SD_HTTP_URL="http://127.0.0.1:54321" # optional cargo run --example test_connection

其中只有SD_LIBRARY_ID是必填项。从 crates/sd-client/examples/test_connection.rs 的源码可见其默认值逻辑:

  • SD_SOCKET_PATH缺省时,在 macOS 上回退到$HOME/Library/Application Support/spacedrive/daemon/daemon.sock
  • SD_HTTP_URL缺省为http://127.0.0.1:54321

示例运行时会依次打印:socket / HTTP / library 连接信息,media_listing查询到的文件总数,以及每个文件(取前 10 个)的 ID、大小、内容类型、Content UUID 和全部可用缩略图 URL,最后还会针对 200px 目标尺寸调用select_best_thumbnail给出推荐变体。

API 详解:SpacedriveClient 全方法剖析

new与库上下文管理

pub fn new(socket_addr: String, http_base_url: String) -> Self pub fn set_library(&mut self, library_id: String) pub fn get_library_id(&self) -> Option<&str>

对应实现位于 crates/sd-client/src/client.rs。library_idOption<String>存储,在执行请求时会随请求体一起发送(见下文execute)。此外客户端还暴露了get_http_url()异步方法,但目前实现返回“HTTP URL query not implemented in daemon yet”错误(client.rs),从源码结构看属于预留接口,实际使用中直接传入的http_base_url才是生效来源。

execute:统一的查询与动作入口

pub async fn execute<I, O>(&self, wire_method: &str, input: I) -> Result<O> where I: Serialize, O: serde::de::DeserializeOwned,

实现(client.rs)的关键逻辑是:

  1. 根据wire_method是否以query:开头,决定请求包装为{ "Query": ... }还是{ "Action": ... }
  2. 构造QueryRequest { method, library_id, payload },其中payload由输入serde_json::to_value得到;
  3. 交由TcpTransport::send_request发送。

因此,任何 daemon 支持的 query 或 action 都可以用execute("query:xxx", input)/execute("action:xxx", input)调用,media_listing只是其中一个封装好的便捷方法。

传输层:换行分隔 JSON 协议

TcpTransport(crates/sd-client/src/transport.rs)负责真实的 socket 通信:

  • 使用TcpStream::connect连接 daemon;
  • 将请求序列化为 JSON 后附加\n作为一条消息发送;
  • 读取一行作为响应(read_line),因此请求与响应均为newline-delimited JSON
  • 响应解析兼容多种格式:优先取json字段,其次JsonOk字段,遇到Error/error字段则抛出Daemon error,最后兜底尝试把整段 JSON 直接反序列化为目标类型(如Pong这类原始值)。

该设计意味着 daemon 端协议演进时,客户端可通过新增字段分支保持兼容。

media_listing:媒体文件列取

pub async fn media_listing(&self, path: SdPath, limit: Option<usize>) -> Result<Vec<File>>

内部构造(client.rs)的输入结构包含以下字段:

字段取值说明
path调用方传入起始路径(SdPath
include_descendantstrue(固定)递归包含子目录
media_typesNone为空时默认仅含 Image + Video
limit调用方传入返回数量上限
sort_by"datetaken"(固定)按拍摄时间排序

请求方法为query:files.media_listing,响应结构MediaListingResponse { files, has_more, total_count },最终只返回files向量。若解析失败,会打印Failed to deserialize media_listing response日志。

缩略图体系:URL 构造与变体选择

thumbnail_url:URL 拼接规则

pub fn thumbnail_url(&self, content_uuid: &str, variant: &str, format: &str) -> String

实现(client.rs)生成的格式为:

{http_base_url}/sidecar/{library_id}/{content_uuid}/thumb/{variant}.{format}

其中library_id在未设置时输出"None"占位符。这一点在 crates/sd-client/src/lib.rs 的单元测试中得到验证——测试断言:

http://localhost:54321/sidecar/None/0cc0b48f-a475-53ec-a580-bc7d47b486a9/thumb/grid@1x.webp

说明库上下文必须先用set_library设置,否则生成的 URL 中库 ID 为None

select_best_thumbnail:智能变体选择算法

pub fn select_best_thumbnail<'a>(&self, sidecars: &'a [Sidecar], target_size: f32) -> Option<&'a Sidecar>

算法(client.rs)步骤如下:

  1. 过滤出kind == "thumb"status == "ready"的 sidecar;
  2. 解析每个变体的标称尺寸与倍率:
    • parse_variant_size(client.rs):icon→ 128、grid→ 256、detail→ 1024,其余返回None
    • parse_variant_scale(client.rs):解析@后缀数字,如grid@2x→ 2;
  3. 计算目标尺寸:target_size <= 400.0时取target_size * 0.6,否则取target_size(避免过大缩略图浪费带宽);
  4. 打分:|size - preferred_size| + (scale - 1) * 100高倍率会被重罚(每高 1 倍罚 100 分),以保证渲染性能;
  5. 取分数最小的 sidecar 返回。

这一设计意味着:在 256px 网格场景下会优先选择grid@1x(256px,无惩罚)而不是grid@2x(256px + 100 分惩罚),除非确实没有 1x 变体。

数据类型与领域模型

sd-client通过 crates/sd-client/src/types.rs 直接再导出sd_core的领域类型:

  • FileSidecar来自 core/src/domain/file.rs;
  • SdPath来自 core/src/domain/addressing.rs;
  • ContentIdentity来自 core/src/domain/content_identity.rs;
  • ImageMediaData/VideoMediaData/AudioMediaData来自 core/src/domain/media_data.rs。

SdPath:与位置无关的文件引用

SdPath是 VDFS 地址系统的核心抽象(addressing.rs),共四种变体:

变体字段用途
Physicaldevice_slugpath指向某设备上的具体路径
Cloudserviceidentifierpath云存储地址(S3、GoogleDrive 等)
Contentcontent_id按内容寻址、可跨设备解析的句柄
Sidecarcontent_idkindvariantformat指向派生数据(缩略图、OCR、嵌入等)

它还提供了display()(输出local://...content://...等统一 URI)、from_uri()解析、is_local()resolve()解析到最优物理位置等能力。SdPath::local()会自动填入当前设备 slug,是构造本机路径最便捷的入口。

File:聚合领域模型

File(core/src/domain/file.rs)聚合了 Entry、ContentIdentity、Sidecar、Tag 与媒体元数据,主要字段包括idsd_pathkindnameextensionsizecontent_identityalternate_paths(重复内容的其他路径)、tagssidecarsimage_media_datavideo_media_dataaudio_media_data以及时间戳和content_kind。它还提供了has_content_identity()sidecars_by_kind()ready_sidecars()has_duplicates()is_media()等实用方法,便于客户端快速判断。

SidecarContentIdentity

Sidecar(file.rs)描述派生数据,字段为content_uuidkind(如thumb)、variant(如grid@1x)、format(如webp)、status(如ready)等——这正是select_best_thumbnail筛选所依赖的字段。

ContentIdentity(content_identity.rs)包含uuidkindcontent_hashintegrity_hashtotal_sizeentry_count等,是去重与内容寻址的基础。content_hash由 Core 侧基于 BLAKE3 生成:小于 100KB 的文件全量哈希,大文件采用 8KB 头 + 4 段 10KB 采样 + 8KB 尾部的采样哈希策略(见 content_identity.rs 的常量定义)。

适用前提与限制

  • sd-client依赖本仓库的sd-core类型与 daemon 协议,使用前需确保 daemon 正在运行且 socket 路径可达;
  • SdPath::Physical当前字段为device_slug(README 示例中的device_id是旧命名,编译时以仓库源码为准);
  • 缩略图 URL 中的库 ID 依赖set_library调用,未设置时输出None
  • get_http_url()尚未在 daemon 端实现,HTTP 基础地址需在new时显式传入;
  • 示例默认 socket 路径面向 macOS(~/Library/Application Support/spacedrive/daemon/daemon.sock),Linux 下建议显式设置SD_SOCKET_PATH

延伸阅读

  • 客户端实现:crates/sd-client/src/client.rs、crates/sd-client/src/transport.rs、crates/sd-client/src/types.rs
  • 示例程序:crates/sd-client/examples/test_connection.rs
  • 核心领域模型:core/src/domain/file.rs、core/src/domain/addressing.rs、core/src/domain/content_identity.rs
  • 仓库根 README:README.md

【免费下载链接】spacedriveSpacedrive is an open source cross-platform file explorer, powered by a virtual distributed filesystem written in Rust.项目地址: https://gitcode.com/gh_mirrors/sp/spacedrive

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询