Skip to content

Instantly share code, notes, and snippets.

@akkuman
Last active October 11, 2024 02:24
Show Gist options
  • Save akkuman/c157e75c13a2d2281b3ad80a315631eb to your computer and use it in GitHub Desktop.
Save akkuman/c157e75c13a2d2281b3ad80a315631eb to your computer and use it in GitHub Desktop.
将思源图片转换成 webp 减少体积,系统上需要有安装 node(使用了squoosh-cli)
#! coding: utf-8
import http.client
import json
import sys
import typing
import pathlib
import subprocess
import re
img_file_pattern = re.compile(r'.*(\.png|\.jpg|\.bmp|\.jpeg)\!?\S*$')
def get_block(text: str) -> typing.Optional[str]:
conn = http.client.HTTPConnection("127.0.0.1", 6806)
payload = json.dumps({
"stmt": f"select * from blocks where markdown like '%{text}%' and type='p'"
})
headers = {
'Content-Type': 'application/json'
}
conn.request("POST", "/api/query/sql", payload, headers)
res = conn.getresponse()
data = res.read()
res_json = json.loads(data)
if res_json['code'] != 0:
raise ValueError(res_json['msg'])
if not res_json['data']:
return None
return res_json['data'][0]['id']
def update_img(block_id: str, path: str):
conn = http.client.HTTPConnection("127.0.0.1", 6806)
payload = json.dumps({
"dataType": "markdown",
"data": f"![{pathlib.Path(path).name}]({path})",
"id": block_id
})
headers = {
'Content-Type': 'application/json'
}
conn.request("POST", "/api/block/updateBlock", payload, headers)
res = conn.getresponse()
data = res.read()
res_json = json.loads(data)
if res_json['code'] != 0:
raise ValueError(res_json['msg'])
for file in pathlib.Path('C:\\Users\\Administrator\\SiYuan\\data\\assets').rglob('*'):
if file.is_dir():
continue
if not img_file_pattern.match(str(file)):
continue
# 如果文件小于20KB,则不进行转换,这种情况下大多数时候转换引入的噪点体验会差于体积焦虑,不划算
if file.stat().st_size < 20 * 1024:
continue
block_id = get_block(file.name)
if not block_id:
print(f'[ERR] 未找到图片对应的 block,跳过: {str(file.absolute())}')
continue
file_dir = file.parent
command = [
'npx',
'--yes',
'@frostoven/squoosh-cli',
'--webp',
'{"quality":50,"target_size":0,"target_PSNR":0,"method":4,"sns_strength":50,"filter_strength":60,"filter_sharpness":0,"filter_type":1,"partitions":0,"segments":4,"pass":1,"show_compressed":0,"preprocessing":0,"autofilter":0,"partition_limit":0,"alpha_compression":1,"alpha_filtering":1,"alpha_quality":100,"lossless":1,"exact":0,"image_hint":0,"emulate_jpeg_size":0,"thread_level":0,"low_memory":0,"near_lossless":100,"use_delta_palette":0,"use_sharp_yuv":0}',
str(file.absolute())
]
with subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, stderr=subprocess.PIPE, text=True, cwd=str(file_dir.absolute()), encoding='utf-8', errors='replace') as process:
for line in process.stdout:
print(line, end='') # 实时输出标准输出
for line in process.stderr:
print(line, end='') # 实时输出标准错误
return_code = process.wait() # 等待命令完成
if return_code != 0:
print(f'[ERR] 转换失败: {str(file.absolute())}')
sys.exit(1)
webp_imgpath = pathlib.Path(f'{str(file_dir.absolute())}/{file.stem}.webp')
# 如果转换出来的文件是源文件的 80% 以上,则不进行转换
if webp_imgpath.stat().st_size > file.stat().st_size * 0.8:
print(f'[INF] 转换后的图片为原图的{webp_imgpath.stat().st_size/file.stat().st_size:.1%},大于 80%,跳过')
webp_imgpath.unlink()
continue
if not webp_imgpath.exists():
print(f'[ERR] 未找到生成的 webp 图片: {webp_imgpath}')
sys.exit(1)
webp_imgpath = webp_imgpath.relative_to(pathlib.Path("C:\\Users\\Administrator\\SiYuan\\data\\")).as_posix()
update_img(block_id, webp_imgpath)
if pathlib.Path(webp_imgpath).name != file.name:
file.unlink()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment