MoreRSS

site iconThe Wandering Allison修改

试图找回自主表达权的普通人。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

The Wandering Allison的 RSS 预览

瞎折腾之博客流程优化

2025-01-30 08:00:00

有点不好意思写这么鸡毛蒜皮的折腾,但是不记一下我肯定会忘,所以……

反正我的博客我做主!

一个是关于 Obsidian 和 PicList。

之前 PicList 是设置的开机启动,但我的笔电实在是有点旧每次都得开着任务管理器小心哪个应用太占地,PicList 比 PicGo 更占内存,而我只有开 Obsidian 的时候才会需要用到 PicList 上传,根本没必要开机就启动。如果在 Obsidian 写到一半要上传图片时再开 PicList 又觉得有点麻烦,靠着万能的 AI 写了个 bat 文件,让 PicList 和 Obsidian 联动,这样打开和退出 Obsidian 时 Piclist 也自动启动和关闭。试用两天之后发现 bat 文件运行后会在 taskbar 保留一个窗口很碍眼,而且要是关闭这个文件的话 Obsidian 和 PicList 也会停止。那有没有什么办法彻底让 bat 窗口隐藏呢?问了 AI 给我的答案居然是用 VB 写一个脚本让 bat 程序在后台运行。VB!这什么祖奶奶级别的语言!但是真的很有用!

所以就是先建一个 bat 文件:

@echo off
start /b "Obsidian" "C:\Path\to\Obsidian.exe" 
start /b "PicList" "C:\Path\to\PicList.exe"

:wait_for_obsidian 
tasklist /FI "IMAGENAME eq Obsidian.exe" | findstr /I "Obsidian.exe" >nul 
if errorlevel 1 goto exit_piclist

timeout /t 1 >nul 
goto wait_for_obsidian

:exit_piclist
taskkill /IM PicList.exe /F
exit

再建一个 vbs 文件,比如 run_hidden.vbs

Set WshShell = CreateObject("Wscript.Shell")
WshShell.Run chr(34) & "C:\Path\to\Your\BatchFile.bat" & chr(34), 0, True 
Set WshShell = Nothing

替换相应路径,再为 vb 文件创建一个桌面快捷方式,之后启动这个快捷方式,就可以自动打开 Obsidian 和 Piclist,上面的 bat 脚本在后台运行,之后退出 Obsidian 时 PicList 也会自动关闭。

另一个则是关于我的读书记录。

之前我是从博客同步的书影音 CSV 文件里手动复制,再给标题加 ###和 shortcode 格式,即使每次用上 alt + shift 批量编辑多条信息我也觉得很烦。因为我几乎每个月都要处理这个问题,并且每次都是十几甚至几十条,这种重复劳动也是太烦人。现在的方案是用 Python 要求输入日期,然后读取 CSV 文件内该时间段的 titleidcomment column,再按标记时间增序输出特定格式,如下:

python_output

import pandas as pd

def process_csv():
    # Step 1: Ask for input
    date_input = input("Enter a date (format: yyyymm): ")

    try:
        # Ensure date is valid
        pd.to_datetime(date_input, format="%Y%m")
    except ValueError:
        print("Invalid date format. Please use yyyymm.")
        return

    # Step 2: Read the CSV file
    file_path = "CSV file" #replace with actual csv file path 
    try:
        df = pd.read_csv(file_path)
    except FileNotFoundError:
        print("File not found. Please provide a valid path.")
        return
    except Exception as e:
        print(f"An error occurred while reading the file: {e}")
        return

    # Check if required columns exist
    required_columns = {"star_time", "title", "id", "comment"}
    if not required_columns.issubset(df.columns):
        print(f"The CSV file must contain these columns: {required_columns}")
        return

    # Step 3: Filter rows based on the input date
    df["star_time"] = pd.to_datetime(df["star_time"], errors="coerce")
    filtered_df = df[df["star_time"].dt.strftime("%Y%m") == date_input]

    # Step 4: Sort the results by date (ascending)
    sorted_df = filtered_df.sort_values(by="star_time", ascending=True)

    # Step 5: Format the output as Markdown
    md_content = ""

    for _, row in sorted_df.iterrows():
         md_content += f"###{row['title']}\n\n"
         md_content += f"{{{/{<douban \"{row['id']}\" >}/}}}\n\n"  
         # 取消反斜杠/
         md_content += f"{row['comment']}\n\n"

    # Step 6: Save the output as an .md file
    output_folder = "your_folder_path"  # Replace "your_folder_path" with the actual path to your folder
    output_file = f"{output_folder}/{date_input}.md"

    try:
        with open(output_file, "w", encoding="utf-8") as f:
            f.write(md_content)
        print(f"Markdown file saved as '{output_file}'.")
    except Exception as e:
        print(f"An error occurred while writing the Markdown file: {e}")

# Run the script
process_csv()

Cloudflare + Backblaze + PicList 搭建博客图床(无需自有域名)

2025-01-27 08:00:00

(在火车上无聊于是开始写博客,我这精神真是感天动地…)

过去我用 Github 的仓库当图床,配合 PicGo 自动上传,但一来 GitHub 的仓库和单个文件都有大小限制,二来我觉得这多少是对 Github 的滥用,最近终于完成了新的图床配置并且运行良好,在这里记录下(写到后面太烦了开始中英夹杂,只求自己看懂就行,请见谅)。

在一众 object storage 服务商中(Cloudflare、Wasabi、AWS、Scaleway、IBM Cloud Object Storage、Azure、Oracle),Backblaze 是唯一不添加付款信息就可使用的服务,只是有限制条件,只能创建 private bucket。也可以付费 $1 进行验证后创建 public bucket。一美金不是问题,但我不想在还不够了解运作流程的情况下贸然添加支付信息,而且这个网络账号所关联的信息我会希望尽可能和现实身份隔离。

其他几个可参考的教程:

我没有自己的域名,以上步骤对我不太适用,worker 部分全是根据自己需求写的,这篇只是过程,不是教程,请酌情参考。

Backblaze

Backblaze free account plan:

Backblaze B2 has a free account with 10GB of storage. The free account includes 1GB of daily download bandwidth, 2,500 class B transactions connected to downloads and 2,500 class C transactions.

对于博客图床来说是够用了。

创建 bucket

官网 注册账号,新建 private bucket,命名(尽可能复杂)。

create b2 bucket|600

在Bucket Settings 中的 Bucket Info 内添加:{"cache-control": "public, max-age=86400"},也可将数字调得更大,86400 是缓存一天。

创建 application key

新建两个 application key。我是为了将 PicList 上传和 Cloudflare 访问区分开,前者的权限是 Read and Write,后者则只是 Read Only。不介意的话也可以只建一个,但权限就需要设置成 Read and Write

create application key |600

接下来这个页面只会出现一次,复制下 keyIDkeyNameapplicationKey 的值。

application key info|600

Cloudflare

创建 worker

复制以下代码。

export default {

  async fetch(request, env) {

    const API_KEY_ID = env.API_KEY_ID; // Application Key ID
    const API_KEY = env.API_KEY; // Application Key
    const BUCKET_NAME = env.BUCKET_NAME; // Bucket Name
    const KV_NAMESPACE = env.KV_NAMESPACE; // KV namespace for rate limiting
    
    const ALLOWED_REFERERS = ["https://site1/",
                              "https://site2/",
                              "https://site3/",
                              "https://localhost:1313/"
                            ]; // List of allowed referers

    const MAX_REQUESTS_PER_DAY = 1000; // Limit to 1000 requests per day
    const url = new URL(request.url);
    const filePath = url.pathname.slice(1); // Extract file path from URL

    if (!filePath) {
      return new Response("File path not provided", { status: 400 });
    }

    // Step 1: Validate Referer Header

    const referer = request.headers.get("Referer");
    const isAllowedReferer =
      !referer || ALLOWED_REFERERS.some((allowed) => referer.includes(allowed));

    if (!isAllowedReferer) {
      return new Response("Invalid referer", { status: 403 });
    }

    // Access Cloudflare Cache API
    const cache = caches.default;

    // Check if the response is already in cache

    const cachedResponse = await cache.match(request);
    if (cachedResponse) {
      console.log(`Cache hit for: ${filePath}`);
      return cachedResponse; // Serve from cache
    }

    // Step 2: Rate Limiting using KV
    const currentDate = new Date().toISOString().split("T")[0]; // YYYY-MM-DD
    const requestCountKey = `${currentDate}:${filePath}`;
    let requestCount = (await KV_NAMESPACE.get(requestCountKey)) || 0;

    if (requestCount >= MAX_REQUESTS_PER_DAY) {
      return new Response("Daily limit reached for this file", { status: 429 });
    }

    requestCount = parseInt(requestCount) + 1;
    await KV_NAMESPACE.put(requestCountKey, requestCount, { expirationTtl: 86400 }); // Reset every 24 hours

    console.log(`Request count for ${filePath}: ${requestCount}`);

    console.log(`Cache miss for: ${filePath}. Fetching from Backblaze.`);

    // Step 3: Authenticate with Backblaze

    const authResponse = await fetch("https://api.backblazeb2.com/b2api/v2/b2_authorize_account", {
      headers: {
        Authorization: `Basic ${btoa(`${API_KEY_ID}:${API_KEY}`)}`,
      },
    });

    const authResponseText = await authResponse.text();

    if (!authResponse.ok) {
      return new Response(`Authorization failed: ${authResponseText}`, { status: 500 });
    }

    const authData = JSON.parse(authResponseText);
    const authToken = authData.authorizationToken;
    const downloadUrl = authData.downloadUrl; // Base URL for the bucket

    // Step 4: Construct the file URL

    const b2FileUrl = `${downloadUrl}/file/${BUCKET_NAME}/${filePath}`;

    // Step 5: Fetch the file from Backblaze

    const fileResponse = await fetch(b2FileUrl, {
      headers: {
        Authorization: authToken,
      },
    });

    if (!fileResponse.ok) {
      const errorText = await fileResponse.text();
      return new Response(`Failed to fetch file from Backblaze: ${errorText}`, { status: 404 });
    }

    // Clone the response to store in cache
    const responseToCache = new Response(fileResponse.body, {
      headers: {
        "Content-Type": fileResponse.headers.get("Content-Type"),
        "Cache-Control": "public, max-age=31536000", // Cache for 1 year
      },
    });

    // Store the response in cache

    await cache.put(request, responseToCache.clone());
    // Return the fetched response to the client
    return responseToCache;
  },
};

几个自定义的值:

  • ALLOWED_REFERERS: 允许引用该图片地址的网站
  • MAX_REQUESTS_PER_DAY:cloudflare 每日最多授权访问 backblaze 的次数
  • max-age:cloudflare 缓存时间,我直接设置成了一年。

这个 worker 的作用是使用 application key 访问 private bucket 获取图片地址缓存到 cloudflare 并生成一个公开可见的 cloudflare worker 地址。加上 ALLOWED_REFERERS 是避免外站引用,毕竟我的免费账户流量有限,同时在没有 header 的情况下在我的 Obsidian 里可以预览。

另外绑定 KV Namespace 设置 Cloudflare 每日可对 Backblaze 发出 request 次数,这是为了防止 class c transition 超标,之前有一天很莫名地 cloudflare 对 backblaze 请求 authorize 2000 多次,所以我加上这个以防万一。

创建 KV Namespace

写不动了看 ChatGPT 写的吧。

Create a KV Namespace
- Go to your Cloudflare dashboard.
- Navigate to **Workers** > **KV** > **Create a Namespace**.
- Give your namespace a name (e.g., `daily-usage-tracker`).
Bind the Namespace to Your Worker
- After creating the namespace, you'll need to bind it to your Worker.
- Go to your Worker script in the Cloudflare dashboard.
- Under the **Settings** tab, find the **KV Namespace Bindings** section.
- Add a binding by giving it a name (e.g., `KV_NAMESPACE`) and selecting the namespace you created earlier.
Use the Namespace in Your Script
- The binding (`KV_NAMESPACE`) will be available in the `env` object passed to your Worker script.

创建 secrets

在 worker 设置里新建以下 secrets:

  • API_KEY_ID
  • API_KEY
  • BUCKET_NAME

值是上面用于 cloudflare 的 read only 的application key 对应内容。

worker secrets|600

Piclist

Official document

PicGo 也支持 S3 图床但需要另外安装插件,而不知是不是这个项目久不维护了,安装插件频繁报错,之前我装 exif remover 时就费了很大功夫,重装 Npm packages,设置 npm proxy 等等等等,而 PicList 内置了这些功能,索性直接迁移了。

下载了 PicList Windows exe 程序又安装不上,微软什么毛病,头大,只能用命令行安装,那又先得安装 scope。

Install scope

Open a PowerShell terminal (version 5.1 or later) and from the PS C:> prompt, run:


Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression

How to open PowerShell from PS C:> prompt

Press Windows + R, type powershell, and hit Enter.


cd C:\

Install Piclist


scoop bucket add lemon https://github.com/hoilc/scoop-lemon
scoop install lemon/piclist

Import Picgo config

PicGo 设置可以一键导入,如果已经在那边用上了 S3 或其他图床就无需再配置。

Config s3 account

来设置 PicList 里的 S3 图床,要填的是以下几栏。

  • AccessKeyID:
  • SecretAccessKey:
  • Bucket: bucket name
  • Self Endpoint: https://{endpoint}
  • Set ACL:private
  • Self Custom URL:cloudflare worker 地址

PicList Config|600

Obsidian

Obsidian 里的 Image auto upload 插件对 PicGo 和PicList 都适用,以前安装过就不用再变动了。

具体我的图片上传流程可见 我的博客写作流程

2024九十月读书记录

2025-01-24 08:00:00

点击我的阅读页面 即可跳转对应条目。

What an Owl Knows

对一些猫头鹰最新研究的综述。

看了两章后剩余是听的 audiobook,所以只能拣出一些零碎印象。

很显然,大家都爱猫头鹰,有很多研究数据是得益于志愿者们的贡献,许多人在正职之外为各个研究项目提供支持,这种模式其实非常值得推广。之前也在其他书里读到过类似的倡导,比如你可以寻找一个自己的小小“自留地”,它是步行可达非常方便观察的地点,长时间地留意一块土地生态的变化。

其他知识点比如 barn owl 的 baby owl 和人类婴儿一样是 rem(有梦睡眠)更多,因此作者提问,它们会做梦吗?它们会说梦话吗?(目前还没有答案。)

大部分猫头鹰不仅是社会层面的单偶制,还是基因上的单偶制——也就是很少有配偶之外的繁殖现象,这在鸟类中可以说是相当罕见。但至于是否真的没有婚外情,这我们就不得而知了。

另一个需要牢记在心的认知则是,我们过去所学到的关于猫头鹰的知识并不能完全应用于所有猫头鹰种群上:不是所有猫头鹰都是夜行性的,不是所有都可以无声飞行,不是所有都有不对称耳朵,不是所有都终身配对,不是所有都栖息在森林里。

So many of the generalizations we’ve learned about owls don’t hold true for all species. Not all owls are nocturnal. Not all fly quietly. Not all have asymmetrical ears. Not all mate for life. Not all roost in forest trees.

再其他的,呃,几个月过去加上都是听的,让我复述也是太难了。Audiobook 是作者自己读的,声音温柔动听不说,她还会活灵活现学各种猫头鹰叫,把我萌翻了!真是散步良伴。听完我又迅速过了一遍书里插图,好多美丽照片,尤其是最后的大头照,捂住心口倒地,请问谁会不爱猫头鹰!

Snipaste_2024-09-02_00-25-47.png|600Snipaste_2024-09-02_00-23-51.png|600Snipaste_2024-09-02_00-24-46.png|600Snipaste_2024-09-02_00-25-30.png|600

我们为什么吃(太多)

一本关于体重、饮食和健康的科普。作者是做胃切除的外科医生,接触过许多超重患者。这类型书最后结论都差不多——健康饮食、适量运动、多吃天然食品、保证睡眠以及精神健康、不要节食。

但人的大脑(或者说,我的大脑)就是需要各种这样喋喋不休从原理出发的解释。

书里提到的一些内容:

基因和环境的影响,不仅仅是远古家族的基因,上一代甚至跨代的环境也会后天影响到孩子的体重水平——比如饥荒、战争等等。

人的体重绝不仅仅是简单的摄入消耗加减算数,这是一个复杂的反馈和负反馈过程。身体无法区分自愿节食与食物饥荒,依靠节食手段减重只能让身体处于低代谢水平,注定会反弹。

只要你饮食得当、避免零食、生活压力低,就没有必要数着卡路里过日子——你的体重定点自然会处在合理范围,新陈代谢也会令其保持健康水平。

不过书里还重点强调了 omega-3 和 omega-6 的区别,我总觉得这部分还无法完全说服我。作者的态度是我们需要多摄入 omega-3,尽量减少 omega-6。

请记住,新鲜食物只能保存几天;而含有欧米伽-6脂肪酸的食物能保存好几个月。你能靠这个来区分它们。

欧米伽-3脂肪酸源自绿叶植物(和海洋中的绿藻),以及以叶子、青草和海藻为食的各种动物和鱼类。要知道,有很多养殖场会以非天然的谷物为基础来喂养牲畜,其中含有欧米伽-6脂肪酸,这样能让它们长得更大更快(对动物和对人同样有效)。从这些养殖场出来的牲畜,肉质当中欧米伽-3的含量很低,而欧米伽-6的含量很高(跟人一样)。因此,如果有条件的话,也要避免吃这种养殖肉。糟糕的是,用谷物来饲养牲畜已经成了养殖场的常态,你将不得不十分谨慎地选取草饲动物的肉品。几乎所有的鸡肉和猪肉,以及大多数牛肉,都是谷饲的,所以你要试着避开这些肉品。大多数羊仍旧是在草地上放养,它们会是一个不错的选择。

而各类坚果在作者的观点里虽然 omega-3 含量不低但 omega-6 更高,并不是一个好的选择。另一个重灾区则是植物油,几乎所有植物油都含有过高 omega-6,作者的建议是使用橄榄油和黄油代替植物油烹饪。不过想想我日常也确实不怎么用油,而且有什么场景需要大量用油呢?肉类本身就有脂肪,鸡腿、牛肉、带皮的鱼,煎一煎就有油分析出了,根本没必要额外用油?之前我买了一升橄榄油,两年保质期结束居然还有 1/4 没用完,我可是一日三餐都几乎自己做的人!采纳这个建议对我几乎没有影响,就也试试看吧。

万神殿堂

建筑史诗系列的第一本。

用了大段笔墨介绍希腊时代的神庙建筑,再到罗马时期建筑技艺的发展,做够了铺陈,好比一串强光打到舞台中心,最后主角万神殿才浓墨重彩地登场。万神庙的前奏是希腊式的廊柱,主乐章则是罗马由混凝土拱券打造出的气势恢宏空间。这时罗马帝国还没有被基督教污染(嗯我的极其主观用词),万神庙造就的,是一个与自然、宇宙、光影、众神对话的场景。尤其是对神庙里光的描写,让我想起 《亲爱的老爱尔兰》 里提到的诺斯石墓。对我来说,还是多神论、泛神论时代比较有趣,等到基督教时期,宗教场所全都修得高高在上,要把人辗入尘埃,只为信仰它那唯一的神。

战斗吧,蝗虫博士

很生气,被简介和推广诈骗,阅读体验完全不符合预期,犹豫了一下是给两星还是三星,但看到豆瓣离谱的高分最后还是只打了两星。其实看文案我就应该警铃大作的,它居然拿《天真的人类学家》(插播之前的辱骂)做比,那还能有什么好!可惜我没能听从我的直觉。

本以为会是像《远东冰原上的猫头鹰》(见 2023_book_review)那样既有扎实的一手研究实录,又有当地风土人情介绍,结果呢,日本男博士这水准真是差得离谱。通篇所谓的“爆笑”全是恶俗两性笑话,什么要和蝗虫谈情说爱,来上一场艳遇,一亲芳泽,彻头彻尾的猥琐宅男形象。提到日本人对海滩的印象,他自然而然的表述是“日本人会觉得海边都是比基尼美女,但这里其实 balabala ”,我满头问号,呵,就直说是你自己的想象好了。

不仅写得差,译得也差。我不知道编辑是怎么允许痛感、背刺、好大儿这种词出现在成书里的。译者也接了上百本日语书了,怎么是不知道痛感是日语词不是中文吗?整本书里出现了八百遍痛感吧,甚至还有“非常痛感”这样的表述,我真是白眼要翻到天上。这个译者译的另一本日本观鸟书籍也是错得一塌糊涂,还把 species 译成“品种”,简直离奇。是只想赶紧计件结束拉倒吗?

打着在非洲治理蝗虫的噱头,关于蝗虫的研究却基本等于无,不会真有人觉得我看这书是为了看找不到工作的日本博士日常流水账吧?科普、生态哪怕自然描写都几乎没有,这么松泡敷衍的书是怎么在日本卖了二十万册的?作者倒是在后记写了,因为论文还没发表,所以书里不能讲研究内容。那你出什么书?简直荒谬。

The Eyes and the Impossible

扒拉 Libby 里可听的资源,因为本地图书馆只买了少儿库所以可挑的并不多。避开各式充满人类幼崽的卡通封面,这只看上去很神气的奔跑的狗狗抓住了我眼球,点开试阅看了眼,被这段前言吸引:

This is a work of fiction. No places are real places. No animals are real animals. And, most crucially, no animals symbolize people. It is a tendency of the human species to see themselves in everything, to assume all living things, animals in particular, are simply corollaries to humans, but in this book, that is not the case. Here, the dogs are dogs, the birds are birds, goats are goats, the Bison Bison.

“Here, the dogs are dogs, the birds are birds, goats are goats, the Bison Bison”,我的确也是受够了那些过于拟人化的动物故事。Audiobook 的朗读者非常厉害,各个角色的语气切换真是活灵活现。

不过虽然作者说“dogs are dogs”,其实本质还是一个冒险故事。一个特立独行的主角,结识朋友,认识自我,经历告别,最后踏上新的旅程。

老实说即使是儿童书籍我听的过程中还是不能完全抓住对话,名字更是完全没对上号,但朗读者的声线转换帮了大忙,全靠他的音色和语气变化来判断对话发生的对象。但听到一半我也成功猜出这只叫声是“ha ha hooooo”的狗狗究竟是什么(不过多剧透了),哈,多亏之前读另外几本北美动物书籍攒下的知识。

整体来说还不错,但不算非常出彩,audiobook 加分不少,好像书里也有不少插图,想找点简单轻松内容读读(或者家里有小朋友)可以考虑一下这本。

Jellies : The Life of Jellyfish

翻本地文件发现这本,没想到这么薄,字还这么少!我不可置信地去 Goodreads 上确认了下真的就只有 30 来页。一些水母图片,配上名字和只有一句话的描述,没了。

唯一有意思的就是这句:

Jellyfish are so simple that they look like plastic trash floating in the sea. When an animal eats a jellyfish it stays healthy and strong. When an animal eats plastic it gets weaker and weaker and eventually dies.

生命进化的跃升

非常非常精彩的关于地球生命演化的论述。

也可以说是 Nick Lane 书里最好读的一本,因为是抽取了十个作者认为最重要的演化史上的突变——让原始生命何以最终成为如今地球上繁盛的复杂多细胞集合体,因此每一章节都有明确主题和确定结果,且基本只涉及简单的原理,不至于像《复杂生命的起源》那样必须完全理解每一步骤的推演才能进入下一步。

复杂生命的形成是一个偶然,错失任何一步可能都不会成为今天的我们。无论是氧气的利用、线粒体与单细胞生物的融合、眼睛的出现、性的繁衍、意识的产生乃至,最后的死亡,都是生命必须的过程。实在是精彩纷呈。

这才是一流的科普写作,不是泛泛地展示现象,而是不停地引领读者提问、思考,不畏惧解释复杂问题,也愿意引入最前沿的理论(哪怕还在猜想阶段),尊重读者的智商和相信读者的好奇心。

我喜欢这种信息量给够让我享受缜密的逻辑和有无限探索未知可能的书!当然,其实看这本已经是偷懒,因为另外几本比如《复杂生命的起源》、Oxygen(这本只有英文版我不自量力地听了下有声书听得两眼冒蚊香,只听懂了一串 oxygen 和 hrdrogen,至于那一串生化反应真真是天书……)等是纵向地深入,难度更大。

此外作者时不时插播对上帝创世论的讽刺也是深得我心,阴阳怪气太好笑了!

(还想写更多但算了,等把 Nick Lane 的书再多看几本集中写,希望我不是又在立 flag……)

你所不知道的日本名词故事

因为我的日语学习停滞了,想着找点日语相关边角料读读也不错。这本就是一些零碎(无用)知识合集。

而在诸多词语里让我心情非常复杂的是这段。

江户时代(公元十七到十九世纪,一六〇三—一八六七)的日本人,曾把中国称为“南京”。从中国进口的舶来品,尤其是小巧玲珑的好东西,经常用“南京”一词儿来形容了,如:南京豆、南京米、南京锭(锁)。

是啊,曾经的南京(ナンキン)被用来指代小巧玲珑的好东西,那南京大屠杀又怎么说呢?我不想清算什么,可我显然也不能把那当作没有发生。

如何理解一只鸟

没有很理解鸟,但是理解了为什么豆瓣短评会说这书好看又不好看。

所有插图都非常美丽,但对于这个文本我的评价是,大概是博士生读博副产物吧。读了 100 篇论文后觉得不能浪费,于是给每篇写个百来字的综述再打上 tag,最后就归类成现在我看到的这个东西。散乱,零碎,无趣。尤其同时我在读《生命进化的跃升》,这落差实在太大了!当然,对每个科普作者用 Nick Lane 的标准来要求也是太苛刻,但是不需要讲解原理追本溯源,哪怕只论述最新发现也不是不能写得好看,What an Owl Knows、无敌蝇家、鱼什么都知道,都是这类的,人家怎么就能写得好?

你要硬说我有没有学到什么,那必然多少也是有一点的,可如果我想读这么无趣的文字,我何不去看教科书?那起码还有个章节编排和全景式框架。这本书就像我用 obsidian 筛选了同类 tag 下的段落直接塞到一起,而吝啬用自己的大脑去寻找逻辑和隐形的骨架将其串联。

最后,写读后感怎么会这么累(有气无力),感觉是在使劲搅和几个月前的我的脑子,我写得有点想吐……

我的博客写作流程

2025-01-15 08:00:00

动机

过去在互联网上我一向话不算多,正因如此,极少数有强烈表达欲的时刻被豆瓣或者微博删帖、禁言才更让我生气。恰好那时转移到了长毛象,发现同时兴起了一股博客复兴潮。很久之前我在新浪有个博客,也始终是 RSS 的忠实使用者,读博客的习惯从未落下,真的写起来也就毫无困难。搭博客也比我以为的简单许多,2020 年底我在一行代码也不懂的情况下也顺利地用不到两天的时间借助 Hugo 和 GitHub Pages 让博客成功运行至今。

但是为了保证打字的流畅,陆续地我加上了不少工具以避免额外劳动,这篇就是我常规的博客运作流程。

灵感收集

怎么写,写什么,每个人有不同的兴趣和表达方式,重要的是有想法就尽量及时保存。

书桌上纸质笔记本,在不开电子设备(尤其看纸质书)时写。电脑,Obsidian。手机平板,Google Keep。出门行程较长时(长时间地铁或跨城高铁)会带随身手账本在路上记录。

写作

录入都在电脑 Obsidian 里完成。

大部分内容会打大纲,写得顺全程键盘,写得不顺就换纸笔,重新理提纲,闷头把能想到的全一股脑记下来,草稿写完再整理思路,删减、调整结构。偶尔在笔记本上会一口气倾倒十页八页的废话,最后电脑输入只用一半甚至三分之一。

输入法是微软拼音(小鹤双拼),效率比全拼高多了!

排版

文字

Metadata,用 Obsidian 的 template + 自定义快捷键自动插入。

数字、中英文混排自动插入空格,标点符号自动补全,连续输入全角转半角等功能依靠 Obsidian 的 Easy Typing 插件。

image.png|600

讨厌一切打字之外的劳动,所以总是在找更无痛的编辑方式。非常依赖 obsidian 的各种 hotkey 以避免敲键盘时还得调格式或是分心挪鼠标。比如 Ctrl + K 插入 markdown 链接格式 []() ,Ctrl、shift、fn 和方向键的组合以选中不同文字等等。

图片

用到的工具如下。

  • 手机截图和照片:Google Photos
  • 电脑截图:Snipaste
  • 图床管理:PicList
  • Obsidian 图片上传插件:Image auto upload
  • Object storage:Backblaze
  • Backblaze private bucket 转为公开及添加图片缓存: Cloudflare Worker

刚建博客时我用 GitHub 仓库当图床,靠的是 Picgo + Image auto upload 插件自动上传。因为觉得这不是一个长久之道且不太正当,最近把图片全都传到了 Backblaze 的 object storage,前置设置变得复杂了些,但逻辑还是一致的。

这个流程是:

打开 Google Photos(使用 Chrome 的 Install App 功能添加到桌面),Ctrl C。电脑上的截图则是 fn + f1 (snipaste 截图快捷键),Ctrl C。

打开 Obsidian 文档, Ctrl V。如果有多张照片/截图,还可以先多次 Ctrl C,再使用 Win + V 打开系统 clipboard (如下图)多次粘贴。

image.png|600

执行粘贴动作后 Image atuo upload 插件自动调用 PicList,PicList 将图片重命名、去除 exif data、转格式为 webp 后上传至 Backblaze,返回 markdown 图片链接,像上图 Ctrl V 后最终呈现在 Obsidian 里的是 ![image.png|600](https://misty-limit-424f.allison-00c.workers.dev/2025/01/c73894ff0eebabc1c0eccfa07ebe5f4a.webp)

Cloudflare Worker 起的作用是通过 application key 访问 Backblaze 的 private bucket 将图片缓存并生成一个 Worker 链接使之公开可见。在 PicList 中设定自定义域名,也就是 Worker 地址,返回的是转义后的链接,避免个人 bucket 地址暴露,同时还利用 Cloudflare 的缓存减少对 Object Storage 的访问。唯一的问题是,国内 IP 看不到 Cloudflare 的图片,但这不是我能控制的了。

实现过程颇为复杂,等我试验一段时间确认完全没问题会再写详细操作,但后续的维护很轻松,插入图片所有我进行的只是两个键盘动作,Ctrl C、Ctrl V 而已。

审校

全部写完会在 Obsidian 里右击文档 tab 选择在默认 app 中打开,也就是 VS Code,利用 VS Code 的 markdown 插件修正格式,多余空格、空行等等,并且通读一遍检查错字、标点符号、病句之类。

image.png|600

发布

最后返回 Obsidian,快捷键移动文档到 post 文件夹,修改 draft 值为 false,再使用 git 插件一键 commit 和 sync。(隐藏的流程还有两个连环触发的 GitHub Action 但这实在是太长了此处略过。) 除非动了博客架构,比如改 CSS,添加 shortcode 之类,否则我不会在本地使用 Hugo 以及 Hugo server,嗯就是这么怕麻烦。

关联阅读:

“我没说过”

2025-01-06 08:00:00

一篇不在计划内的更新,关于一件颇为令人尴尬的事实。

博客主题的 config.toml 文件里有个 paramsintro,建站那会儿恰好在某篇文章里读到这句话,据说引自波伏瓦:

我厌倦了贞洁又郁闷的日子,又没有勇气过堕落的生活。

虽然会让人误解,但这种逃离乏味庸常生活的渴望恰好合了我的心意,就一直放在了首页上。

最近突然又想起来这件事,发现我从来没有确认过来源。试图在网上查找出处,全是二手引用,宣称“波伏瓦曾说过”,却无一给出具体的书名或文章名。

虽然我日常惯例是辱骂微信读书也不得不说这时它的搜索功能非常有用,可以在全站图书里查询特定内容。搜出三本和波伏瓦完全无关的书,全以“波伏瓦说过”开头,接着一字不差地引用了这句话。

换个思路,逐本排查波伏娃的书,说不定是译文不同所导致的语句差别?关键词“贞洁”、“厌倦”或者“堕落”总该不会有错把。而微信读书上架的所有波伏瓦的书,甚至包括她的访谈录和传记,用这三个词分别全文查找,都找不到类似的表述。

我又把这句话翻成英文和法语发给 ChatGPT,问它能否找到波伏瓦的任何原文有相似表达,很遗憾,AI 说可能是波伏瓦的话——这种关于性、苦闷之类的话题,但它找不到任何直接证据。

好吧,再来从中文入手。撇除一堆乱七八糟的网站文章,豆瓣上有篇读书笔记原文引用了这句话,2001 年出版的柴静的《用我一辈子去忘记》,第 80 页:

我们在宿舍齐声念西蒙波娃的句子“我厌倦了贞洁又郁闷的日子,又没有勇气过堕落的生活。”

2001 年?我再试着限定 Google 的搜索结果时间,2001 年之前中文互联网一条关于这个句子的内容都没有。再放宽到十年后,2011 年,只有两条结果,2007 年 4 月的 央视网 ,再就是 2007 年 2 月的这个短文学。央视网上的柴静文章内容和豆瓣读书里的原文摘抄也对上了,始作俑者难道就是柴静?之后这句所谓名言就大量出现。不过,央视网这个网站是多少年没维护过了?还停留在十几年前的画风。甚至——柴静在中国算是被封杀的状态了吧,央视网的主持人专栏也没把她撤下来。

image.png|300

所以,这个“波伏瓦名言”,大概率是他人捏造,真是尴尬,迅速把首页 intro 改掉,庆幸至少最初我没在下面注明人名,否则就更尴尬了……

不是给自己找补,但大部分时候我会更严谨地对待这种“某某名人说”。比如刘慈欣在三体里引用的那句“给岁月以文明,而不是给文明以岁月”,顺便又被无数其他小说再次引用,并冠之“帕斯卡说”,但搜遍全网也找不到帕斯卡说的这句话!之前我还很认真地给小说作者评论,问她具体出处是哪里(嗯是的,其实是在某篇 BL 小说里读到的),可惜作者完全没理我。稍后我也读了三体,于是顿悟,是这种二手引用来着呀。

顺带搜索了下还有哪些“我没说过”,发现了这篇文章, The Greatest Misquotes in History ,那个可比“何不食肉糜”的法国玛丽王后的 “Let Them Eat Cake!” 也是杜撰呢。

以及,最开始很想插鲁迅表情包——就是那个“我没说过这句话”,但发现我还是不喜欢用任何真人表情包,试着让 AI 给我生成了个类似的 I NEVER SAID THAT meme,可惜 AI 图片质感就是很差啊!没办法忍受插这种图到文章里,请自行脑补吧!

博客印象收集!

2025-01-04 08:00:00

在长毛象看到大家玩嘟主印象我也好想参加!可我毛象发的不多,互动更少,实在是玩不起来,那要不在博客试试。快速用 Google Forms 做了个问卷出来,如果在下文中看不到可以点击 链接

把“技术分享”也作为博客主题的一项真是非常心虚,但确实点击率最高的几篇都是这类文章,就还是大言不惭地放上去了。

意见/建议那一栏,因为我有时真的非常、非常、非常想对一些博客说,起码使用标准标点符号和减少 emoji 吧!(以及一些更刻薄的观点。)但我还是有一些基本的互联网礼貌不会这样不请自来地给别人意见。不过也会想,会不会在别人眼中,我的一些语言习惯也是很有问题的呢?很好奇别人的印象(但我未必会改)。

Loading…