【硬核】思源笔记同步空间压缩计划

为了尽量讲解的通俗易懂,导致本文过于冗长,为了不浪费大家时间,简述下本文解决了什么问题:

  1. 针对资源文件的压缩处理、
  2. 针对资源文件压缩后扩展名(后缀)不一致的处理方法
  3. 提出一种批量替换思源文档的处理方法

本文同步与思源笔记社区链滴,链接 => 思源笔记同步空间压缩计划

前言

在拜读 @qiancang思源笔记同步空间拯救计划 之后,发现文中的情况并不适合我,

遂寻找一种可持续、可复现的操作手段来对资源文件夹的内容进行压缩规整,让我们开始吧。

image

如果您对思源笔记工作空间内的文件结构不太清楚,请查阅:

思源笔记文件系统介绍:数据快照与同步 - 知乎 (zhihu.com)

缓解数据焦虑,思源笔记文件存储介绍 - 链滴(ld246.com)

缓解数据焦虑,思源笔记文件存储介绍 2 - 链滴(ld246.com)

分析

想要进行空间压缩,第一步就是要知道目前空间占用情况,然后对相关文件进行分析,针对性的处理。

在这里推荐一款工具,名字叫:WizTree,并且个人免费使用。

下载地址:www.diskanalyzer.com/download

image

只需要点击即可下载。

软件中文切换方法:

image

软件使用方法:

image

如何导出/复制特定的文件类型方便进行二次处理

assets 资源文件默认存放着全部格式的文件,不方便进行操作,所以需要将特定的文件类型单独区分出来,方便二次操作。

方法一:按照类型排序,然后选择

image

使用排序功能后,文件就会按照类型进行排序,然后:

  1. 选中【你找到第一个视频】
  2. 按下 Shift 键,点击【你找到最后一个视频】

此时会选中【你找到第一个视频】和【你找到最后一个视频】之间的全部文件,

通过这种方法可以很方便的将特定类型的文件提取出来。

方法二:使用命令行窗口配合 xcopy 快速复制

按下 Windows + R 打开运行窗口,在打开处输入 cmd 然后回车,这个时候打开的就是命令行窗口了。

接着使用以下命令:

1
2
3
4
5
# 命令
xcopy "源文件路径" "目标文件夹路径" /s /i

# 例子
xcopy "C:\Users\xiaoqi\SiYuan\data\assets\*.png" "C:\Users\xiaoqi\Desktop\PNG" /s /i

解释一下这个命令:

  1. xcopy 是 Windows 中的一个命令行工具,用于复制文件和目录。
  2. "C:\Users\xiaoqi\SiYuan\data\assets\*.png" 是源文件路径。
    这里指定了复制 C:\Users\xiaoqi\SiYuan\data\assets 目录下所有扩展名为 .png 的文件。
    这里的 * 你可以理解成【任意】。
  3. "C:\Users\xiaoqi\Desktop\PNG" 是目标文件夹路径。
    这里指定了将文件复制到 C:\Users\xiaoqi\Desktop\PNG 目录下。
  4. /s 选项表示复制包括子目录在内的所有文件。
    如果源目录包含子目录,这个选项可以确保子目录中的文件也被复制。
  5. /i 选项表示如果目标是一个目录,则创建目录。
    如果目标文件夹 C:\Users\xiaoqi\Desktop\PNG 不存在,该命令会自动创建它。

通过这种方法,在配合【任意】* 符号的使用(如视频:*.mp4 等),可以对多种格式快速的复制。

针对视频格式处理方法

如果你的视频占用高,那么可以尝试对视频进行压缩处理,具体操作方法如下:

方法一:使用 File Converter

这是一款开源的文件转换器,是属于 ffmpeg 的封装。

安装完成默认会在上下文菜单中增加一个菜单项。

下载地址:https://file-converter.io/

Github 地址:https://github.com/Tichau/FileConverter

使用方法:

FileConverterUsage

!!使用的时候请注意!!

  1. 软件默认的压缩配置会将压缩完成的文件放置在同一文件夹
  2. 压缩完成的文件会自动重命名并且添加如:(720p)(1080p)等内容。

所以需要修改一下,不然不方便后期操作。

image

模板代码:

1
2
3
(p:m)压缩\(f)
(p:m)保存文件夹\(f)
...

然后就可以全选压缩,等待....

完成后直接复制到 "C:\Users\xiaoqi\SiYuan\data\assets\" 覆盖它。

方法二:使用 FFmpeg

FFmpeg 用于处理视频、音频和其他多媒体文件及流的库和程序,广泛用于格式转码和基本编辑。

下载地址:www.gyan.dev/ffmpeg/builds

新建一个 bat 文件,将以下内容复制进去:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@echo off
setlocal

rem 设置 ffmpeg 的路径(例: D:\CommonTools\ffmpeg\bin\ffmpeg.exe)
set "ffmpeg_path=D:\CommonTools\ffmpeg\bin\ffmpeg.exe"

rem 待压缩视频文件夹(例: C:\Users\xiaoqi\Desktop\MP4)
set "input_folder=C:\Users\xiaoqi\Desktop\MP4"

rem 压缩后存放位置(例: C:\Users\xiaoqi\Desktop\MP4-ys)
set "output_folder=C:\Users\xiaoqi\Desktop\MP4-ys"

rem 创建输出文件夹(如果不存在)
if not exist "%output_folder%" (
    mkdir "%output_folder%"
)

rem 遍历输入文件夹中的所有 MP4 文件
for %%f in ("%input_folder%\*.mp4") do (
    echo 正在压缩 %%~nxf...
    "%ffmpeg_path%" -n -stats -i "%%f" -c:v libx264 -preset medium -crf 21 -c:a aac -qscale:a 1.3 -vf "scale=1280:720,format=yuv420p" "%output_folder%\%%~nxf"
)

echo 压缩完成!
pause

注意修改:ffmpeg_pathinput_folderoutput_folder,保存后双击运行,接着就是等待....

完成后请手动检查下文件夹内的文件数量是否一致,如不一致,请看本文 -> 文件不一致如何比对。

完成后直接复制到 "C:\Users\xiaoqi\SiYuan\data\assets\" 覆盖它。

针对图片格式处理方法

对于一些图片,也可以进行压缩,具体操作方法如下:

方法一:使用图压

这里我使用的是图压这一款软件,你也可以使用其他的图片压缩软件,操作步骤同理。

注:图压的项目源代码是申请开放制的,具体请看官网。

图压官网:https://tuya.xinxiao.tech/

目前官方网站已经无法正常浏览和下载软件了,但是你可以使用 Web 时光机功能,找到它,

请不要使用xxx下载站来下载。

图压官网(archive 链接)

Windows 下载地址(archive 链接)

MacOS 下载地址(archive 链接)

注:archive 中文叫网页时光机,可以访问和浏览过去存档的网站。

image

软件很简洁,我推荐的方法设置方法是:

  1. 目标格式选择原格式
  2. 保存路径使用自定义位置
  3. 文件后缀清除

完成后请手动检查下文件夹内的文件数量是否一致,如不一致,请看本文 -> 文件不一致如何比对。

这样压缩好的图片就可以快速覆盖到 "C:\Users\xiaoqi\SiYuan\data\assets\"

方法二:使用压缩能力更加优秀的格式

图片格式可以使用更加优秀的格式来代替它,比如 WebP。

WebP 是由 Google 收购 On2 Technologies 后发展出来的图片格式,

根据业界给出的改造数据可知,改造 WebP 之后图片体积会降低很多:

image

具体可参照 WebP 体积测试链接

注:浏览器支持程度和兼容性浏览:caniuse.com/?search=WebP,思源当然是支持的啦!

可以使用上面的图压工具来压缩并且转换为 WebP 图片格式(目标格式选择 WebP) ,也可以使用以下方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@echo off
chcp 65001 >nul

:: 設置 cwebp.exe 的路徑(例:D:\CommonTools\cwebp.exe)
:: 详细介绍: https://rene78.github.io/batch-convert-to-webp
set "cwebp_path=D:\CommonTools\cwebp.exe"

:: 設置源文件夾路徑(例:C:\Users\xiaoqi\Desktop\PNG)
set "source_folder=C:\Users\xiaoqi\Desktop\PNG"

:: 設置目標文件夾(例:C:\Users\xiaoqi\Desktop\PNG1)
set "target_folder=%source_folder%\converted"

:: 確保目標文件夾存在
if not exist "%target_folder%" (
    mkdir "%target_folder%"
)

:: 遍歷源文件夾中的所有支持的圖像文件
for %%f in ("%source_folder%\*.jpeg", "%source_folder%\*.jpg", "%source_folder%\*.png", "%source_folder%\*.bmp") do (
    echo 正在處理文件:%%f
    :: 提取文件名(不帶擴展名)
    set "filename=%%~nf"
    :: 執行轉換命令
    "%cwebp_path%" -q 80 "%%f" -o "%target_folder%\%%~nf.webp"
)

echo 所有文件處理完成!
pause

注:cwebp.exe 用于将图片文件压缩为 WebP 文件,具体参数介绍下载地址

这里主要不是介绍如何转换,而是介绍转换后的内容如何修改到思源。

image

其实答案很简单,即:修改笔记内容。

这里我的思路是直接在数据库里面将这部分内容查询出来,然后生成替换命令,直接运行命令替换,

image

如果大家有更加好的方法,欢迎在下面留言探讨,谢谢!

第一步:数据库找到相矣数据

这里我使用的是 HexHub,你也可以使用任意的工具,比如:

  1. HeidiSQL 官方网站
  2. sy-query-view 插件SQL 查询器
  3. 十个优秀的开源免费数据库管理SQL客户端 -(知乎 zhihu.com)

Sql 教程帖子定位:

思源 SQL 新人指南:SQL 语法 + Query + 模板

SQL&Query 用法记录

SQL该怎么用?

image

PixPin_2025-02-28_22-18-24

image

PixPin_2025-02-28_22-38-14

连接成功后先别急着查询,先来看看 assets 数据库表与字段分别代表什么意思。

此处参考:@shuoying 的思源笔记数据库表与字段一帖,感谢!

PixPin_2025-02-28_22-38-14

需要修改笔记内容就需要找到这个资源对应是哪个文档,

path(资源文件路径)name(资源文件名)这两个字段可以是哪个资源,

box(笔记本 ID)docpath(文档路径)就可以确定是哪个笔记本及文件存放位置。

image

1
2
3
4
box: 20241224095229-yxbhu6o
docpath: /20241212102205-fwomcie/20240220165409-i5eml9m.sy
path: assets/image-20240319104536-66zo3wo.webp
name: image-20240319104536-66zo3wo.webp

分析该数据得出:

此路径的文档:工作空间/data/20241224095229-yxbhu6o/20241212102205-fwomcie/20240220165409-i5eml9m.sy
该文档使用了这个路径下的文件:工作空间/data/assets/image-20240319104536-66zo3wo.webp
资源文件名称叫:image-20240319104536-66zo3wo.webp

打开这个文档,使用搜索工具搜索 image-20240319104536-66zo3wo.webp 看看是否正确。

image

确定没问题后,接下来就可以通过 SQL 查询出全部内容来了,

这里主要是查询 png 格式的文件,也就是这样操作:

1
2
3
4
5
6
7
8
# 查询语句例子

## 查询 assets 表里面 path 字段全部为 .mp4 结尾的文件
SELECT * FROM assets WHERE path LIKE '%.mp4';

## 查询 assets 表里面 path 字段全部为 .png 结尾的文件
SELECT * FROM assets WHERE path LIKE '%.png';
...

image

第二步:构建替换命令

接下来就是想办法批量的将数据库查询出来box(笔记本 ID)docpath(文档路径)文档打开, 然后使用替换工具批量的替换里面的path(资源文件路径)name(资源文件名)资源扩展名。

比如:

一个工作空间/.../20240220165409-i5eml9m.sy 的文档,

使用了工作空间/data/assets/image-20240319104536-66zo3wo.png这个文件,

资源保存的文件名称叫image-20240319104536-66zo3wo.png


操作步骤:

我打开工作空间/.../20240220165409-i5eml9m.sy 文档,

搜索 image-20240319104536-66zo3wo.png 这个资源文件名,替换成我前面压缩并且转换成 WebP 的文件名称,

也就是image-20240319104536-66zo3wo.webp,最后在保存。


使用方法:

替换操作我想到了 Linux 操作系统的 sed 命令,于是我尝试搜索一下是否能来 Windows 上使用 sed 这个命令工具。

Github 地址:https://github.com/mbuilov/sed-windows

下载链接:https://github.com/mbuilov/sed-windows/releases/tag/sed-4.9-x64-fixed

下载好之后其实就是一个很小的 exe 文件,我下载的是 sed-4.9-x64.exe 这个版本,使用方法如下:

image

常用例子:

# 打印文件 /etc/passwd 的第三行
# 指定地址定界 3
# command 命令设置为 p 即打印输出
# options 选项使用 -n 表示静默模式(多余的不显示)
sed -n '3p' /etc/passwd

# 打印文件 /etc/passwd 的第三行到第七行区间的内容
sed -n '3,7p' /etc/passwd

# 使用正则表达式匹配:
# 开始:/^root/
# 结束:/^sync/
# 打印开始和结束的区间的全部内容
sed -n '/^root/,/^sync/p' /etc/passwd

# 在第一行前面指定添加字符串
# 1 指定第一行
# command 命令设置为 i:insert,增
# 666 是内容
# 第一行前面指定添加字符串 666
sed '1i666' /etc/passwd

# 删除 /etc/passwd 文件中的第二行到第五行区间的内容
# 2,5 是区间
# command 命令设置为 d:delete 删
sed '2,5d' /etc/passwd

# 查找并替换
# 将小写的 root 替换为大写的 ROOT,替换文件 /etc/passwd 的内容
# command 命令设置为 s:substitute 改
# 这里的 g 表示 global,意味着全局替换,简单来说就是替换全部
# 如果没有添加 g 如果查到有多条的情况下,默认替换第一条
sed 's/root/ROOT/' /etc/passwd
sed 's/root/ROOT/g' /etc/passwd

接下来就是构建 sed 的替换命令了,注意看上面的常用例子,

options 选项都没有使用 -i 直接在原文件上进行操作的方式,下面就需要用到它。

image

思源工作空间路径 + box(笔记本 ID)+ docpath(文档路径)就是需要替换的文档的绝对路径,

name 是搜索关键词,

对搜索关键词 name 修改后的结果就是替换内容。

image

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 对于 SQLite version 3.44.0 (2023-11-01) 之后的版本可以使用:
SELECT 
  CONCAT(
    'D:/CommonTools/sed-4.9-x64.exe',
    ' -i "s/',
    name,
    '/',
    REPLACE(name, '.png', '.webp'),
    '/" "C:/Users/xiaoqi/SiYuan/data/',
    box,
    docpath,
    '"'
  ) AS replace_command
FROM assets
WHERE name LIKE '%.png';

# 对于 SQLite version 3.44.0 之前的版本请使用:
SELECT 
  'D:/CommonTools/sed-4.9-x64.exe' ||
  ' -i "s/' ||
  name ||
  '/' ||
  REPLACE(name, '.png', '.webp') ||
  '/' ||
  '"' ||
  ' ' ||
  '"' ||
  'C:/Users/xiaoqi/SiYuan/data/' ||
  box ||
  docpath ||
  '"' AS replace_command
FROM assets
WHERE name LIKE '%.png';

如何查看版本?

使用 SELECT sqlite_source_id();命令查看返回的日期。

这里需要注意的是:

  1. D:/CommonTools/sed-4.9-x64.exe 需要替换成你的 sed 位置
    (当然你也可以将文件名称修改成 sed.exe并复制到 C:\Windows 里面,这样就可以直接使用 sed 代替它)
  2. C:/Users/xiaoqi/SiYuan/data/ 思源工作空间路径需要添加 data/

执行完毕后对查询结果导出,就获得了完整的操作命令啦~

image

第三步:执行替换操作

对于 Hexhub 软件来说,导出的 TXT 文件直接可以修改扩展名修改成 bat 或者 cmd 就可以直接双击运行了

HeidiSQL 则需要导出成 CSV 格式然后使用 Excel 打开复制 A1 列,复制到一个 bat 里面在运行。

或者你可以直接复制全部命令然后到 cmd 控制台粘贴回车自动运行....

这个时候工作空间的 "C:\Users\xiaoqi\SiYuan\data\assets\" 资源文件夹

既存在了原有的 png 又保留了压缩又转换的 WebP

这些文件可以使用思源的【设置】【资源】【未引用资源】处删除...

执行完毕后记得重建索引!

重建索引后数据库会刷新,可以通过这个机制来判断是否存在遗漏。

文件不一致如何比对

在处理时可能会存在压缩前后文件数量对不上的问题,如果不注意使用 sed 就进行替换的话,

就会出现大面积的资源文件丢失的情况(思源【设置】【资源】【资源文件丢失】)

所以需要在压缩/转换操作完成后对比压缩前和压缩后的文件数量,如果出现数量对不上,使用以下方法寻找文件:

方法一:使用 Excel

Windows 中,有个 dir 命令,用于列出当前文件夹的文件详细信息:

image

其中有个参数 /B 可以只显示全部文件的名称

image

通过这个命令在配合重定向输出 > 来使用,就可以快速实现指定文件夹快速名称导出。

image

为此制作了个简单的 bat 脚本:

1
2
3
4
5
6
@echo off

rem 生成当前文件夹的目录树并保存到 tree.txt 文件
dir /B > tree.txt

echo 目录树已保存到 tree.txt 文件中。

将上面的代码保存为一个 .bat 文件,然后直接复制到你要生成的文件夹内,双击运行。

当压缩前和压缩后的文件名称都有了,那就可以使用对比工具来对比下少了哪个文件了。

这里我直接使用 Excel,对比过程不在叙述。

image

方法二:使用 Python

直接使用 Python 来处理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import os
import shutil

## 哪两个文件夹进行对比?
# 源文件夹
png_folder = r"C:\Users\xiaoqi\Desktop\PNG"
# 目标文件夹路径
webp_folder = r"C:\Users\xiaoqi\Desktop\converted"

## 对比完成后将缺失文件复制到此文件夹内
new_folder = r"C:\Users\xiaoqi\Desktop\missing_png"

# 创建新文件夹
if not os.path.exists(new_folder):
    os.makedirs(new_folder)

# 获取 PNG 和 WEBP 文件列表
png_files = os.listdir(png_folder)
webp_files = os.listdir(webp_folder)

# 找出 PNG 文件在 WEBP 文件夹中缺失的文件
missing_png_files = [f for f in png_files if f.endswith(".png") and f.replace(".png", ".webp") not in webp_files]

# 将缺失的 PNG 文件复制到新文件夹
for f in missing_png_files:
    src_path = os.path.join(png_folder, f)
    dst_path = os.path.join(new_folder, f)
    shutil.copy2(src_path, dst_path)
    print(f"Copied {f} to {new_folder}")

print(f"Total {len(missing_png_files)} missing PNG files copied to {new_folder}")

本文参考内容

Shell 三剑客之 sed

Shell脚本学习之sed详解

How to Concatenate Strings in SQLite

concat function error

思源笔记同步空间拯救计划

思源笔记文件系统介绍:数据快照与同步 - 知乎 (zhihu.com)

缓解数据焦虑,思源笔记文件存储介绍 - 链滴(ld246.com)

缓解数据焦虑,思源笔记文件存储介绍 2 - 链滴(ld246.com)

思源 SQL 新人指南:SQL 语法 + Query + 模板

SQL&Query 用法记录

SQL该怎么用?

思源笔记数据库表与字段