MoreRSS

site iconzzbd修改

90 后理工男,轻度社恐
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

zzbd的 RSS 预览

十月杂谈,你在秋天等什么呢

2025-10-16 15:45:29

:头图

今年最后一个长假已经过去好多天,本想写写假期见闻,心里的浮躁却总也压不住,好在屋外的凉爽让紧绷的神经能稍稍放松。最近这段时间,身边几乎所有人都在感叹时间过得飞快,一转眼,2025 年就要结束了。

多邻国的卡片一直挂在手机桌面,我就眼睁睁看着多儿各种呼唤,直到连续摆烂的天数过了三百。长久以来,可能唯一坚持下来的就是偶尔写写博客。已经记不起上一次凉快是什么时候,7 月份退租的不愉快经历让我连带对这两年的生活产生厌恶。虽然现在住的地方更破更小,但偶尔一些小惊喜也会暂时打消心头的疑虑,比如前几天奶孙二人组在附近又发现了一个不错的公园✌️。

今年变化最大的要数 Samuel,还有一个月多一点就该满 4 岁了。有时候跟他讲话,你也不知道该把他当小孩还是大人,经常说出口的话自己都觉得幼稚,好在小朋友不会去计较这些。总的来说,搬家后两口子吵架次数少了,奶孙组合也高度默契,除了 Samuel 的早晚饭时间😅。听闻同龄人都开始报兴趣班,跆拳道啥的,我也不懂有没有必要跟个风,或者等他再长大一点?就我观察,小孩子的兴趣基本靠环境引导。比如家里最多的玩具就是小汽车和恐龙,大多是朋友送的,新的旧的都有。Samuel 现在最痴迷的就是这两样。日常能接触到的最多的文字是地铁站名,这个他也很喜欢。我在想,如果朋友送的玩具大多是奥特曼,小朋友的兴趣点应该也会入乡随俗。然而,特意准备的 switch 没有成功让他上道,也可能奥德赛之类的游戏对他来说还是太难了点。最近准备装个 minecraft,有机会再“引导”一下。

昨天晚上把小主机的 ubuntu 系统从 24.10 升到 25.04,因为担心拖得太久后面都没法自动升级。这就是尝鲜的代价,如果留在 LTS 分支最少能省心用十年。期间都还算顺利,就是升完之后 php 没了,手动安装又发现 yourls 出了问题。在🖥️方面,我一直有点偏左加上轻微强迫症,比如版本追新,严格按文档操作,觉得手动编译会更好等等,事实上很多时候是自寻烦恼。比如自行 diy 过或者很长时间没升级的话我一般不敢动它,宁可再等过段时间大版本升级再全部推到重来。这次被逼无奈强制覆盖升级了 yourls,发现所有问题迎刃而解。所以,只要能正常运转,就不要管什么“纯洁”性,哪里坏了修哪里,能满足需求就不要追新,好奇心会害死🐱。

最近用 switch 玩了把霍格沃茨之遗🏰,仿佛又找到了第一次玩老滚 5 的感觉。画面和建模跟 14 年前的老滚如出一辙,也难怪这么多人吐槽。幸运的是我也算老哈迷了,场景设计和 BGM 就完完全全是我的菜,何况咱也不是画质党。主线任务经常玩到手心冒汗,操作难度不高,可玩性上限在我看来还是挺高的。听到吐槽最多的应该是中后期大量无脑重复的任务,以及“话痨”型 NPC,我感觉到我这也是没法坚持玩到大结局,就像塞尔达一样。所以现在特意降低进游戏的频率,企图能更长久地保留新鲜感,就像塞尔达一样。盼望着,盼望 Samuel 能快点长大,有个搭子肯定更欢乐咯🐥。

打字太慢,先就写这么多吧,去办公室窗户拍张照当作首图。

明天要去苏州一趟,看天气好似不会下雨。

天青色等烟雨,年底了,你还有哪些期待呢?

医学考古:旧约、不孕不育和曼德拉草

2025-09-15 09:40:29

:首图前不久看到一篇神奇的文章1,题目是:Genetics of infertility and “assisted fertilization” in the Bible: The case of Abraham and his family。不孕不育的文献多如牛毛,但从圣经视角做研究还是头一回碰到。

🎓文章通讯作者 Livio Casarini 是摩德纳大学教授,生殖和内分泌领域专家,h-index 29,总计被引 3000+2。一作 Manuela Simoni 总和 h-index 高达 84,被引次数超过 240003。所以这也不是什么路边社刊登的娱乐文章,而是严肃的科学问题(bushi🙊)。

🤰不孕不育是一种相当古老的疾病,最早的文字记录出现于《创世记》,写作时间大约在公元前 10 世纪,彼时中国正处在西周时期。传统教义认为,亚伯拉罕和他的子孙以撒和雅各是以色列人的族长(patriarchs),而他们最宠爱的妻子都患有原发性不孕。这篇文章完全根据经文中的线索重建亚伯拉罕家族的遗传图谱,总结圣经中主要的不孕不育案例,并试图用现代医学思想寻找合理的解释和科学推论。

📌背景介绍

文章开头提到了新月沃土(Fertile Crescent),圣经中很多重要情节都发生于这片神奇的土地。巧合的是,Fertile 也有生殖(fertility)的含义。《创世记》是摩西五经(或称摩西律法)的第一卷,讲述了以色列国和阿拉伯民族的创始者、犹太人/穆斯林/基督徒的族长——亚伯拉罕的故事。然而,这个家族长期以来备受生育问题困扰,并且只有在神的帮助下才能完成受孕。在旧约中,生育被视为上帝赐予人类的神圣使命,比如「要繁衍增多,遍满地面,并制伏这地。——创世记 1:28」,「儿女是耶和华所赐的产业;腹中的果子是他所给的赏赐。——诗篇 127:3-5」。生育能力则被视为神的恩典与回应祷告的结果,而非仅仅是生理过程。这种生育观的影响一直持续到现今,比如罗诉韦德案代表的美国堕胎法案争议。

✝️不孕不育的故事

旧约时代的婚姻和生育习俗与现代社会截然不同。彼时一夫多妻是常态,丈夫与“小三”借腹产子是不育妻子对丈夫的补偿,并且孩子被妻子视若己出。这是因为所有不育问题都被归咎为女性的责任,是对妻子或者家族曾经犯的错降下的惩罚。此外,为了维护团体利益,家族通婚也比较盛行。最后,宾客权力神圣不可侵犯,甚至允许客人与自己家中的女性上床

下面是文章作者通过 AI 整理的内容,其中用到了 Perplexity、ChatGPT4.0 和 Claude 3.5 Sonnet,提示词为:Retrieve and discuss (with accurate bibliographic references) all examples or instances (even mentions) of infertile women (or couples) who at some point conceived a child in the Bible.

撒拉(以撒之母)

撒拉是亚伯拉罕的妻子,直到 90 岁才有孩子。经上记,「撒莱不能生育,没有孩子。——创世记 11:30」。尽管撒拉年事已高,但上帝仍应许亚伯拉罕,她会生一个儿子。起初,撒拉对这个承诺嗤之以鼻,认为这是不可能的(创世记 18:12)。然而,她最终怀孕生下了以撒,以撒意为“笑声”,既指撒拉最初的怀疑,也指她后来的喜悦。

利百加(以扫和雅各之母)

以撒的妻子利百加起初也无法怀孕。创世记 25:21 记载,「以撒因他妻子不能生育,就为她祈求耶和华。」婚姻的第 20 年,利百加在以撒的祷告下怀上了双胞胎——以扫雅各

拉结(约瑟和便雅悯之母)

在创世记 29-30 章,我们找到了雅各挚爱妻子拉结的故事。经文特别指出,「耶和华见利亚失宠,就使她生育;拉结却不能生育。——创世记 29:31」。拉结的不孕让她非常痛苦,尤其是看着姐姐利亚生了好几个孩子。多年不孕之后,「神记念拉结,垂听了她,使她能生育。——创世记 30:22」。她生下了约瑟,后来又生了便雅悯

玛挪亚之妻(参孙之母)

在士师记 13:1-24 中,我们读到关于参孙的故事,「以色列人得罪耶和华,所以耶和华将他们交在非利士人手中四十年。那时有一个琐拉人,属但人的家族,名叫玛挪亚;他的妻子不能生育,没有生孩子。耶和华的使者向那妇人显现,对她说,向来你不能生育,没有生孩子;如今你必怀孕生一个儿子。所以你当谨慎,淡酒浓酒都不可喝,一切不洁之物也不可吃。你必怀孕生一个儿子,不可用剃刀剃他的头,因为这孩子从母腹里就归神作拿细耳人;他必起首拯救以色列脱离非利士人的手。」参孙是以色列的士师,他的死使以色列摆脱了非利士人的统治。

哈拿(撒母耳之母)

哈拿的故事记载在撒母耳记上 1-2 章。她是以利加拿的两个妻子之一,经文记载:「耶和华使哈拿不能生育。——撒母耳记上 1:5」。在神介入之前,哈拿在示罗的圣殿里热切祷告,并发誓要将任何一个儿子献给神侍奉。最终,她怀孕生下了撒母耳,撒母耳后来成为以色列一位重要的先知。

书念的妇人

在列王纪下 4:8-17 中,我们读到了一个富有的书念妇人的故事,她热情款待了先知以利沙。虽然经文没有明确提到不孕,但她始终没有孩子,而且她的丈夫年事已高。以利沙预言她会生一个儿子,尽管她心存疑虑“神人,我主阿,不要欺哄你的婢女”,她还是怀孕生了一个儿子。不幸的是,这个儿子夭折了,但以利沙用类似心肺复苏术的方法使他苏醒过来(列王纪下 4:33-37)。

以利沙伯(施洗约翰之母)

在新约中,我们遇到了祭司撒迦利亚的妻子以利沙伯。路加福音 1:7 明确指出,「只是没有孩子,因为以利沙伯不生育,两个人又上了年纪。」天使加百列宣告她将生一个儿子。借着神的作工,她怀孕生下了成为耶稣先驱的施洗约翰。值得注意的是,天使加百列同时向以利沙伯的亲戚玛利亚宣告了圣灵成孕的耶稣即将出生。然而,马利亚很可能已经生育过,因为福音书提到了耶稣的兄弟姐妹;例如,马可福音 6:3 提到,在会堂里听耶稣讲道的人说,「这不是那木匠么?不是马利亚的儿子,雅各、约西、犹大、西门的长兄么?祂妹妹们不也是在我们这里么?」。

撒拉、利百加和拉结都属于亚伯拉罕家族,她们都患有“家族性”的女性不孕症。撇开神力干预不谈,这或许可以被认为是一种古老而神奇的“辅助生殖”形式,也不论这个故事明显的传奇色彩,我们在此进行一项考古病理生理学研究,探讨这种疾病是否可能存在某种医学原因,以及亚伯拉罕家族女性在多年不孕之后最终获得生育能力的机制。

🪽亚伯拉罕家族生育史

亚伯拉罕

亚伯拉罕家族的传奇故事发生在青铜时代(约公元前 1800 年),但直到所罗门统治时期(公元前 971-931 年),此前口耳相传的族长传统才开始被书写下来。直到公元前五世纪,《妥拉》才形成了真正意义上的文字记载。因此,我们在《创世记》中读到的内容,显然无法构成对一千多年前发生的事情的准确医学记录。然而,这段文字确实为使用世俗医学解读这些事件提供了重要线索。

图 1 他拉的家谱。男性用方块表示,女性用圆圈表示。双线表示近亲,数字表示以色列各支派的创始人。

亚伯拉罕是他拉的三个儿子之一,娶了撒拉为妻。撒拉是他拉与另一个女人所生,因此两人是同父异母的兄妹。创世记 11:30 明确指出撒拉不能生育。此时,一家人从美索不达米亚(迦勒底人的乌尔)迁往迦南地,即今天的巴勒斯坦/以色列地区。这是上帝的旨意,上帝应许亚伯拉罕赐予这片土地,并让他成为一个伟大国家的缔造者。一场饥荒迫使亚伯拉罕迁往埃及,在那里,他四处宣扬撒拉是他的妹妹。之所以这样做,是因为 65 岁高龄的撒拉仍是一位非常美丽的女子,他担心撒拉是他妻子这件事会惹来杀身之祸。毕竟,撒拉是他妹妹的说法并非谎言。法老被撒拉的美貌所吸引,娶她为妻,并赏赐亚伯拉罕许多财物,包括牲畜和奴隶。当法老发现撒拉是亚伯拉罕的妻子时,便将他连同他的所有财物驱逐出埃及,因为娶未丧偶的女人是一种亵渎。就这样,亚伯拉罕变得非常富有,回到巴勒斯坦就定居在希伯伦。从这件事我们可以看出,这对夫妇中不孕的一方是撒拉而非亚伯拉罕。否则,假设撒拉尚未进入更年期(她仍然非常有魅力的细节可能是雌激素分泌的迹象),她可能与法老生儿育女。有报道称,更年期甚至可能发生在超过 65 岁的高龄。

此时,上帝再次应许亚伯拉罕,将赐给他如天上繁星般多的后裔,但撒拉仍然无法生育。撒拉自己提出了一个解决方案,「耶和华使我不能生育,求你和我的使女同房,或者我可以因她得孩子。——创世记 16:2」。亚伯拉罕听从了撒拉的请求,娶了夏甲为妾。夏甲为他生了一个儿子,96 岁的亚伯拉罕给他起名叫以实玛利。从这段故事中,我们得知亚伯拉罕拥有生育能力,并且根据当时的习俗,人们采用“代孕”的方法解决不孕问题。亚伯拉罕和撒拉/夏甲的案例是历史上对代孕的首次描述,尽管与我们今天对这一术语的理解截然不同。在《创世记》时代,人们认为女性仅通过精液生育后代,而不知道雌雄配子的存在。因此,从伦理的角度来说,使用哪个子宫来生育婴儿并不重要,而且在正文中找不到关于撒拉卵巢功能的直接描述。

三年后,亚伯拉罕 99 岁的时候,耶和华再次向他应许「至于你的妻子撒莱,不可再叫撒莱,她的名要叫撒拉。我必赐福给她,也要使你从她得一个儿子。我必赐福给她,她也要作多国之母,必有多民的君王从她而出。——创世记 17:15-16」。这时,亚伯拉罕产生了一些困惑,「一百岁的人还能得孩子么?撒拉已经九十岁了,还能生孩子么?——创世记 17:17」。18 章出现了三个人,受到亚伯拉罕夫妇的隆重款待,并宣布了一件事。「其中一位说,到明年这时候,我必回到你这里;你的妻子撒拉必生一个儿子。——创世记 18:10」。11 节继续说,「亚伯拉罕和撒拉年纪老迈,撒拉的月经已经停了。」由此可知,撒拉年轻时不孕的原因并非闭经。创世记 21 章告诉我们,「当亚伯拉罕年老的时候,撒拉怀了孕……亚伯拉罕为撒拉给他生的儿子起名叫以撒。」

以撒出生后,已为人母的撒拉嫉妒夏甲和长子以实玛利,亚伯拉罕被迫将他们送到巴兰旷野(今内盖夫)。后来,以实玛利与一位埃及妻子生了 12 个儿子,并成为阿拉伯十二个部落的族长。[这里原文用的是“Arab outrages”]

撒拉在 127 岁时去世。亚伯拉罕随后又娶了基土拉,并为他生下 6 个孩子,再次证明亚伯拉罕的生育能力很强。在他 175 岁去世时,所有财产都留给了以撒。

以撒

亚伯拉罕有两个兄弟。其中一位拿鹤是彼土利的父亲,彼土利又是拉班和利百加的父亲。《创世记》24 章详细记述了亚伯拉罕如何派仆人回故乡为以撒寻妻。仆人来到哈兰,找到了利百加,她是彼土利的女儿,彼土利是亚伯拉罕的侄子。仆人与彼土利和利百加的兄弟拉班商定了婚约,并支付了丰厚的嫁妆。当商议都结束时,他们征求了利百加的意见,而利百加也按照习俗接受了这一请求。在当时,尽管会正式征询女方的意见,但通常不影响结果。就这样,以撒娶了一位同族的年轻女子。事实上,在旧约时代,人们很早就会结婚,而且婚姻通常局限在氏族和家庭的小圈子里。与外族女子结婚是不合适的,以免她将外面的信仰和习俗带进来。近亲结婚的传统,可能与现今原始部落中更高的生育率有关。

以撒在 40 岁时娶了年幼的远房堂妹利百加。「以撒因他妻子不能生育,就为她祈求耶和华;耶和华应允他的祈求,他的妻子利百加就怀了孕。——创世记 25:21」。当时以撒已经 60 岁了,因此,这对夫妇在怀孕之前经历了 20 年的原发性不孕。创世记 25 章告诉我们,在分娩时,「先出来的身体发红,浑身有毛,如同毛皮衣;他们就给他起名叫以扫。随后以扫的弟弟也出来了,他的手抓住以扫的脚跟,因此给他起名叫雅各。」从中我们得知,以撒和他的妻子患有原发性不孕症,结婚 20 年之后发生了异卵双胞胎妊娠。《创世记》明确指出利百加不孕,但以撒是近亲结婚的后代,因此我们不能排除影响精子发生的遗传因素。异卵双胞胎妊娠意味着利百加至少在 30-35 岁之后才会出现双排卵。《创世记》没有记载以撒的其他配偶,显然他与利百加只生了这两个儿子。这在当时并不常见,我们可以推测以撒的生育能力可能较低。

以扫和雅各一直是对手:以扫是长子,行动力强,善于狩猎,深受父亲以撒的喜爱;而雅各则文静聪明,深受母亲利百加的喜爱。以撒晚年双目失明,无法分辨儿子。利百加趁机帮助雅各用计谋欺骗父亲,夺走了长子的祝福。以扫失去了长子继承权,并迫害比他级别更高的雅各。为了保护雅各,利百加把他送离家乡,投奔了哥哥拉班。

以扫娶了三个妻子,其中一位是以实玛利的女儿玛哈拉,也是他的堂妹。以扫生育了数个后代,被认为是以东人的始祖。玛哈拉没有记载不育史,但她只有一个孩子,这在当时和以东族中并不常见。

雅各

在雅各前往哈兰的路上,耶和华在梦中向他显现,「我是耶和华你祖亚伯拉罕的神,也是以撒的神。——创世记 18:13」,并应许说,「我要将你现在所躺卧之地,赐给你和你的后裔。你的后裔必像地上的尘沙那样多,必向东西南北开展;地上万族必因你和你的后裔得福。——创世记 18:13-14」。雅各在哈兰的一口井边遇见了一位年轻的牧羊女,她是他叔叔拉班的小女儿拉结,她的姐姐是利亚。「利亚的眼睛没有神气,拉结却生得形貌美丽。——创世记 29:17」。雅各爱上了拉结,便对拉班说:“我愿为你小女儿拉结服事你七年。”拉班答应了,但在七年服侍期满且婚宴结束后,他却欺骗了雅各,「但到了晚上,拉班却将女儿利亚送来给雅各,雅各就与她同房。」这是因为「大女儿还没有给人,先把小女儿给人,在我们这地方没有这样作的。」然而,「你为这一个满了七日,我们就把那一个也给你,只要你再为她服事我七年。」:两个妻子,两份嫁妆。

因此,雅各娶了他的两个表亲。拉结是他最宠爱的,但却不能生育。相反,利亚为了争宠,生了四个儿子。拉结因为嫉妒将自己的使女辟拉嫁给雅各,「使她生子抱在我膝上,我便因她也得孩子。——创世记 30:3」。辟拉生了两个儿子。利亚在生下犹大之后「这才停了生育。——创世记 29:35」,于是将使女悉帕给雅各为妻,以便她名下能有更多子嗣。悉帕又为雅各生了两个儿子。

这些事之后,利亚因无法生育而感到忧愁,便想着用药方。于是在“麦收之时”,派她的长子流便去田里寻找风茄。流便的确把风茄带回给了母亲,但拉结也想拥有这种具有助孕功效的植物。于是,拉结与姐姐利亚做了一笔交易,用风茄换取利亚再次与雅各同房的机会。利亚接受了这个条件。讽刺的是,就在那天晚上,利亚又为他怀上了另一个儿子。在接下来的几年里,她又生了一个儿子和一个女儿。显然,风茄对拉结不起作用,利亚无需药物就能再次生育。

最终,「神记念拉结,垂听了她,使她能生育。——创世记 30:22」。于是,她怀孕生下了约瑟。此后,雅各用诡计和计谋从拉班那里偷取了许多财富和牲畜,然后返回故乡。之后,他再次举家迁徙,在从伯特利前往以法他(今伯利恒)的途中,拉结生下了她的第二个也是最后一个儿子便雅悯,并死于难产。这是雅各的第十二个儿子。假设雅各所有的孩子都是按大致每年一次的频率依次出生的,并且拉结结婚时只有十几岁,那么她生育的时候应该在 35 岁左右。雅各的陈述证实了这一点,他离开拉班之前在拉班家服务了 20 年。

雅各被神改名为以色列,他的十二个儿子构成了以色列的十二支派。

从这个故事中,我们得知雅各的妻子们患有原发性不孕(拉结)或继发性不孕(利亚),而雅各本人虽然是近亲结婚,却拥有很强的生育能力。如同撒拉和亚伯拉罕的例子一样,“代孕”证明了这个家族中男性的生育能力,但以撒的生育能力可能较低。另一方面,利亚患的是继发性不孕,而这种不孕症是可逆的。拉结在 35 岁左右开始生育,我们知道,当他们离开拉班家时,她正值月经来潮,正如她告诉父亲的那样,「现在我身上不便,不能在你面前起来,求我主不要动怒。——创世记 31:35」。此外,我们在现有文献中首次了解到风茄具有催情和增强生育力的特性

🌟彩蛋:风茄和生殖

流便在《创世记》30 章 14 节发现的“爱情果”,希伯来语记作 D¯ud¯a ¯ım,意为欢愉之爱或肉欲。这种果实被认为指的是风茄(mandrake,也译作“曼德拉草”、“曼陀罗”、“毒参茄”)——最有可能具有催情和增强生育力功效的植物,常见于地中海地区并在晚春(小麦收获时节)结出果实。曼德拉属(Mandragora officinarum,茄科)可能是古代最著名的具有此类功效的药用植物,几个世纪以来一直被用于多种病症的治疗,在希波克拉底、阿雷塔乌斯、塞尔苏斯、柏拉图尼阿普列乌斯等人的医学著作中都有详尽记载。

图 2 曼德拉属:(A) 具有拟人化特征的植物各部位的流行形象及药用价值,(B) 花,(C) 浆果,以及 (D) 根。

风茄在许多文学作品中也有提及4,从莎士比亚的《罗密欧与朱丽叶》和《奥赛罗》、马基雅维利的《曼陀罗》、贝克特的《等待戈多》,到罗琳的《哈利·波特》系列。在《哈利·波特与密室》中,斯普劳特教授给二年级学生展示如何给曼德拉草幼苗换盆。后来,当洛丽丝夫人被蛇怪石化时,阿不思·邓布利多提到了曼德拉草魔药作为一种可能的治疗方法,当时正是洛哈特教授特别提到了它,声称“可以一边做梦一边配制”。当哈利·波特在密室与汤姆·里德尔发生冲突时,这位年轻的巫师说曼德拉草魔药很快就会准备好,它可以让所有被石化的人复活,而且与几年前发生的情况不同,没有人会因为被释放的蛇怪而死亡。在《哈利·波特与火焰杯》中也提及这种植物,当时哈利、罗恩·韦斯莱和赫敏·格兰杰正在图书馆翻阅书籍,寻找能够帮助被选中的人通过三强争霸赛第二项任务的东西。

一篇近期发表的论文汇总并回顾了几个世纪以来各国对风茄的药用价值5,并根据该植物的化学和药理学特性,发现了多达 88 种药用信息。从化学角度来看,风茄根所含的众多生物碱中,最重要的是具有副交感神经阻抑作用的东莨菪碱和阿托品。最近,在巴利阿里群岛青铜时代墓葬和祭祀洞穴的考古发掘中,研究人员利用高精度质谱技术从人类毛发中发现了这些生物碱,这表明在亚伯拉罕时代,人类确实在使用药用植物。

在生殖领域,风茄提取物被用于堕胎、不孕症、壮阳以及常规妇科问题。Benitez 等人在其综述中提到,圣经中提到的风茄的壮阳特性可能源于其果实而非根部的香味。在如今的西班牙、土耳其、亚美尼亚、伊拉克、黎巴嫩和摩洛哥,人们出于同样的目的仍在采集此类果实。正是这种果实独特的、具有挑逗性的芳香引发了性欲。然而,迪奥斯科里德斯(Dioscorides)在其著作《药物论》(De materia medica,公元 1 世纪)中提及,风茄根也具有催情功效。据报道,传统犹太人和阿拉伯人至今仍在采集风茄果实来治疗不孕不育5。马基雅维利在喜剧《曼陀罗》中提到了风茄改善生育力的特性,卡利马科斯医生说道:“你必须明白,没有什么比给女人服用曼陀罗制成的药水更能确保她怀孕的了。”时至今日,只需稍加谷歌搜索,任何人都可以找到出售含有风茄提取物(顺势疗法)制剂的帖子。

鉴于风茄含有副交感神经抑制性生物碱,人们不禁要问,民间传说中关于这种植物催情和增强生育力的传说是否有药理学基础?换句话说,胆碱激导性/副交感神经系统在生殖过程中究竟扮演什么角色?尽管风茄对性生活和生育力有着传奇般的积极作用,但动物实验表明,抗胆碱性生物碱具有抑制作用。在雄性大鼠和仓鼠中,东莨菪碱会抑制性行为。在雌性大鼠和恒河猴中也发现了同样的效果,东莨菪碱可改善脊柱前凸。在内分泌性腺功能和生育力方面,抗胆碱能药物已被证明可以通过减少精子传输、增加卵巢卵泡闭锁以及减少颗粒细胞孕酮生成来抑制雄性大鼠的生育力。因此,总体而言,风茄果实/提取物对生育力的影响即使有,也应为负面

在人类医学中,过去曾偶尔使用阻断毒蕈碱受体的抗胆碱能药物来治疗逆行射精,例如用于治疗男性糖尿病患者。目前,这些药物主要用于眼科和吸入性支气管扩张剂。副作用包括口干、视力障碍和畏光、排尿困难,甚至在最严重的情况下会出现精神错乱。目前尚未报告对性行为或生育力的影响。

🩺对亚伯拉罕家族不孕症的医学解释

在《创世记》中,撒拉、利百加和拉结这三位女子为了彰显神更大的荣耀,促成以色列民族的建立而无法生育。因着上帝的旨意,怀孕才成为可能,我们可以将这视为一种“神助生殖”。那么,根据圣经故事,我们能否找出不孕/生育能力低下以及至少在某些情况下最终成功妊娠的医学解释?

亚伯拉罕的家谱因以下一些特点而引人注目:1)多次近亲结婚;2)男性没有生育障碍;3)族长的妻子生育能力低下(不完全外显率?);4)存在原发性和继发性女性不育/生育能力低下;5)不育症不涉及闭经;6)最终怀孕发生在高龄/非常高龄;7)发生异卵双胞胎(双排卵)。此外,如果我们假设女性不孕症是家族遗传性的,那么它似乎是通过父系传播的。从他拉开始,他生下了不育的撒拉,并通过拿鹤将缺陷传给了彼土利,彼土利生下了不育的利百加,并将这个因子传给了拉班,拉班生下了不育/生育能力低下的利亚和拉结。这种“不孕/生育力低下因素”不影响精子发生,也不会自行消失,这表明它可能具有某种进化优势。某种内分泌机制可能介入该领域。

最简单的解释是多囊卵巢综合征 (PCOS) ,这是一种多基因家族性疾病,会降低女性的生育能力,但不伴有男性表型。全基因组关联研究 (GWAS) 已证实多个基因位点与 PCOS 表型相关。

独立研究证实的 PCOS 候选基因包括 DENND1A、FSHB、FSHR 和 LHCGR。这些都是公认的与卵巢功能有关的基因。一个简单的假设是,这些基因中的一个或多个的多态性或轻度失活突变可能导致亚伯拉罕家族中生育力低下女性出现 PCOS 样表型,而不会导致男性出现任何生殖问题。众所周知,FSHR 的失活突变并不一定会导致男性不育。此外,PCOS 风险与晚年绝经的遗传易感性(莎拉的病例)有关,异卵双胞胎(丽贝卡的病例)也与 FSHB 基因座有关。PCOS 也与继发性和暂时性不孕症相符(利亚的病例)。在独立 GWAS 研究中发现的其他 PCOS 易感基因包括表皮生长因子受体基因(ERBB2/HER2、ERBB3/HER3 和 ERBB4/HER4),这些基因参与类固醇生成。

地中海 PCOS 表型的遗传起源证实了古埃及、希腊和罗马文献中对该病症的文字描述,并由 Antonio Vallisneri 于 1721 年在早期意大利医学文献中定义。事实上,先前的研究表明,存在一个源自阿什肯纳兹人和东南欧人群的基因库,它们具有一定程度的相似性。这些数据与南欧和中东地区人群中相对较高的 PCOS 患病率和相似表型(整体雄激素水平)相一致。他们的大致定义是雄激素水平相对较高、多毛症、雄激素性脱发,并具有慢性无排卵和生育力低下的生殖表型。这些临床和生化特征也可能是由非经典类固醇 21-羟化酶缺乏症引起的,这是一种与肾上腺增生相关的疾病,其特征是雄激素过多,在阿什肯纳兹人群中的发病率较高(3%-4%)。总之,《创世纪》中描述的他拉后裔中女性反复出现不孕不育/低生育力的情况可能反映了在以色列人和中东人中仍然存在的 PCOS 表型,并且与多基因疾病和/或部分 21-羟化酶缺乏症有关,已知这些疾病在犹太人中仍然非常普遍。

多囊卵巢综合征(PCOS)仅影响女性,尽管有人假设存在 PCOS 男性表型,即携带 PCOS 相关基因变异和高雄激素性状的个体。从进化的角度来看,人类的高雄激素血症可能是一种基因座内(intralocus)的性冲突,即两性对同一性状的适应度不同。例如,虽然高雄激素血症会导致女性患上多囊卵巢综合征和生育力低下,但它对男性来说可能有利,因为这种性状可能与男性更强壮、更具攻击性有关。这些因素可能为男性带来了进化优势,而女性的生殖适应度则因此受损。

从男性角度看,亚伯拉罕,尤其是雅各,都非常多产,而以撒似乎是一夫一妻制,只有两个儿子。《创世记》中没有以撒与其他女性生育子女的线索,但告诉我们他很晚才当父亲(男性生育能力低下也一样?他是近亲结婚的儿子),之后就失明了。失明和不育症的复杂组合可能与维生素 A 缺乏症、视黄酸信号传导缺陷、衣原体感染、纤毛病或被称为“眼缺损、心脏缺陷、后鼻孔闭锁、生长发育迟缓、生殖器发育不全、耳畸形/耳聋”(CHARGE)综合征有关。一种极小的可能性是,他患有 Richner-Hanhart 综合征,这是一种罕见的常染色体隐性遗传病,在一个阿什肯纳兹血亲家族中被发现。该综合征患者体内游离酪氨酸水平较高,主要表现为畏光、手掌和脚底角化过度以及轻微的神经系统异常,这使得该病的一些特征与以撒的失明联系在一起。然而,这种综合征与生育力低下无关。虽然以撒的案例中,传染病当然是可能的,但我们没有任何证据支持上述任何假设,因此我们仍然认为他只是一夫一妻制,他的失明与年龄有关,例如白内障,而利百加则是单方面不孕。

从世俗的角度来看,亚伯拉罕及其家族的历史应该被视为神话传说;而从宗教的角度来看,强调亚伯拉罕和撒拉怀以撒时已是高龄,是为了强调上帝掌管一切。从医学角度来看,我们现在知道,一方面,高龄产妇与非整倍体和流产率增加有关。另一方面,高龄父亲可能导致性染色体非整倍体以及精子的表观遗传学变化,从而增加后代患神经系统疾病的风险。至少从我们在《创世记》中读到的内容来看,这些对亚伯拉罕和他的妻子来说显然不是什么大问题。然而,这段文字是在经历了数个世纪的口口相传之后才写成的,诚然,其中可能存在一些“叙述偏差”,忽略了年老的负面影响。

🏁尾声

不孕不育是一个古老的医学难题。圣经是记录这种现象的最早文献之一,也记录了在无法进行医学辅助生殖的时代为解决这一问题所采取的措施。这些措施包括祈祷、草药疗法和代孕,这些方法至今仍广受欢迎且仍在使用。在圣经中,不孕不育始终是女性的问题,在全球许多地区,这种观念仍然盛行,而男性不育则被视为一种笼罩在神话中、需要占卜祛邪的“阉”病。与《创世记》时代相比,这确实没有太大的进步。值得注意的是,在圣经中,当神迹作用于不孕不育时,总会兴起一位伟大的先知,这在世俗意义上可以被视为梦寐以求的子女诞生后带来的巨大喜悦,这也是成功接受不孕不育治疗的夫妇的共同经历。

值得注意的是,圣经记载了数例不孕不育,但《创世记》中只展开了亚伯拉罕家族案例的细节,让我们得以从世俗的医学角度来解读这个问题。通过分析这个故事,我们提供了一种医学“结论”:他拉的后代患的是多基因多囊卵巢综合征(PCOS)或部分 21-羟化酶缺乏症。为了保持严肃与诙谐的界限,我们甚至可以推测,亚伯拉罕家族是这种疾病地中海表型的“始祖”。当然,这无法得到证实,本文的创作和想象精神也应保留。作者希望,这种跨学科方法能够吸引到少数好奇的读者,并让他们有所收获。

The generation of scientific knowledge does not always have to be based on impactful technological developments, or promote innovation and generate commercial value.

——Sílvia Socorro Lab

(全文完)

Footnotes

  1. Simoni, M., Tüttelmann, F., & Casarini, L. (2025). Genetics of infertility and “assisted fertilization” in the Bible: The case of Abraham and his family. Andrology.

  2. Livio Casarini - ResearchGate

  3. Manuela Simoni - Google Scholar

  4. 曼德拉草 - 萌娘百科

  5. Benítez, G., Leonti, M., Böck, B., Vulfsons, S., & Dafni, A. (2023). The rise and fall of mandrake in medicine. J. Ethnopharmacol., 36395976. 2

Folo + N8N + 云日历实现简易社媒热图

2025-08-29 16:30:00

头图

简单讲,就是把小红书、抖音、微博、𝕏等等平台发布的动态展示到热图中,类似于 github contributions。点我直达。下面动图是日历 app 中的效果。

:google calendar
LIVE

❓起因

很久之前,因受到 contributions 颜值吸引,仿照开源项目做了一个活动展示组件。后端直接用Google Calendar API,除了国内不能直连外都是优点💯:多端增删改查,安全稳定,有 api key(仅限查询)和 OAuth 两种鉴权方式,可用字段多,免费🤩。

然而热乎劲儿只持续了月余,反思一下,主要问题是手动录入太繁琐✍️,体验不好。比如刚在 B 站刷到了宝藏视频,我第一个想法是马上分享到社交媒体账号,而不是打开谷歌日历新增一条活动。另外,选择活动时间也让人既纠结又头疼🤯。

手头一堆账号都在活跃使用,也渐渐摸出自己的选择标准:

  • 跟学术和科技相关的统统发到𝕏,因为跟现实世界没有交集,也符合马哥平台的气质。
  • :rednote:带娃日常、风景照、线下活动,衣食住行等等发到小红书,原因你懂的。
  • 偶尔兴致来了拍个短视频发到抖音
  • 其他杂七杂八、吃根冰棍、各种无病呻吟发到微博
  • :coolapk:玩机交流发到酷安
  • :rednote:NAS、PT、数码、好物等等发到什么值得买
  • :rednote::rednote:想要分享或者提问的内容发到这两个平台

当然,最重要的分享内容还是会放到博客。至于朋友圈,那肯定发工作相关的了。

⚙️流程

回归正题。首先准备一个 folo 账号,部署 RSShubN8N 并能正常访问。RSShub 是因为有些网站需要指定 cookies 之类的环境变量,或者官方渠道不稳定。经过尝试,什么值得买、酷安、抖音和小红书最好用自己的实例。在 folo 中完成目标账号的订阅,确保能正常更新,顺便还可以发个动态认证订阅源

接下来就是通过 N8N 把 folo、google 日历和 cloudflare worker 串联起来。参考官方文档,必要时借助Apifox调试接口。

folo 的设置比较简单,点侧栏头像👉自动化👉新规则。条件是所有需要聚合的订阅源所在的分类,动作是 n8n 的 webhook 地址。⚠️注意 1:payload 中的 feed title 还是平台提供的默认值,而非你自定义的订阅源名称。关于这个问题我也提交了 issue。⚠️注意 2:Webhook 需要填写 Production URL,而非 Test URL,后者只能手动接收一次网络请求。

n8n 这边略微复杂,因为源标题无法修改,需要加一层 Data transformation,并选择 Edit Fields。

Edit Fields

{{ (()=>{
let alt = new Map([["original title", "new title"], ["original title2", "new title2"]]);
return alt.get($json.body.feed.title) ?? $json.body.feed.title;
})() }}

接下来搜索并添加 google calendar 节点,选择 Create an event,去谷歌云后台根据引导配置好 credential。🚨这里有一个小坑,你需要在OAuth 权限请求页面下的目标对象中把你的谷歌账号添加到测试账户列表中,否则在 n8n 发起认证请求时会弹出 403 错误:禁止访问:“xxxx.com”尚未完成 Google 验证流程。⚠️不要使用测试账户,只有 7 天效期,点发布应用然后在 n8n 重新授权。

create event

填入各项,这里{{ $json.body.entry.publishedAt?.toDateTime() ?? $now }}添加了一个$now的 fallback 是为了方便用 apifox 手动发起无publishedAt字段的请求。比如 linuxdo 这类反爬措施太强以至拿不到 rss 的站。持续时间设定为 15 分钟🕰️,当然也可以根据不同的源灵活设定。ColorId 纯粹为了在日历 app 中更好看一些,代码对应的🎨网上很好查。

CF worker 和博客前端都是之前做好的,完全兼容,一个括号都不用改。这里贴一下关键代码:

worker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
url.host='www.googleapis.com'
url.pathname += '/[email protected]/events'
url.searchParams.set('key', 'xxxxxxxxxxxx')
url.searchParams.set('timeMin', new Date(new Date().setFullYear(new Date().getFullYear() - 1)).toISOString())
return proxyJson(url.href, request)

async function proxyJson(url, request) {
let resp = await proxyRequest(url, request)
const jsonRaw = await resp.json();
const outJson = [];
if (!jsonRaw.items) return new Response(JSON.stringify(jsonRaw), options)
for (let item of jsonRaw.items) {
outJson.push({
type: item.summary,
desc: item.description,
time: item.start.dateTime,
dur: (new Date(item.end.dateTime) - new Date(item.start.dateTime)) / 1000 / 60
})
}
return new Response(JSON.stringify(outJson), options)
}

最后还是放个图吧,以备将来参考

截图

除了谷歌日历,coloros 自带的日历应用在登陆账号后也能同步,包括新增活动项。测试下来比官方日历应用大概延迟个 1 分钟。

就在整理社媒账号时,偶然发现饭否还活着,只是转变成封闭小圈子。正好作为我的私密树洞🌳,哈哈。

使用 Certimate 管理并通过 SSH 自动更新域名证书

2025-08-15 13:40:00

:certimate
Certimate 是一个 SSL 中心化管理工具,方便对多个主机🖥️上的域名证书🔐进行集中管理。只是当作 acme.sh 的前端替代很好上手,但如果你想使用“自动”功能,坑还是不少的。

⏳定时任务失效

这是一个已知 bug,在我提交这个 issue 之前,应该有不少人都遇到过类似问题。简言之,就是某些前端支持的 cron 语法不兼容 crontab,而后台在没有进行转换🔄或拒绝🙅‍♂️的情况下抛出异常。目前的解决方式就是使用 crontab 兼容语法,比如去 crontab.guru 验证一下,就像这条讨论提到的,举个例子:
0 0 * * Tue 👉 ``0 0 * * 2`

:terminal: 无法通过 ssh 上传证书

工作流
授权配置

如果是密码👀认证方式,那么大概率是上传路径填错了。⚠️注意这里的四个“路径”都是指的远端服务器,而非前两个是本地,后两个是被部署端。具体来说📣,证书文件上传路径是 fullchain,证书私钥文件上传路径是 key,服务器证书文件上传路径是 cert,中间证书文件上传路径是 Intermediate,这里中文表述并不十分准确。还有一种可能性是读写权限不够,这里需要确保用于登录的账户对 ssl 证书所在文件夹有执行权限,对证书文件有写权限

如果使用密钥鉴权方式,注意 <SSH 密钥>一栏需要填写本地生成的私钥,并且对应的公钥需要添加到远端主机对应账户的文件夹下。⚠️这里既不是填公钥,也不是远端服务器的私钥🫠。

🔔Telegram 通知不起作用

老版本是在“系统设置👉通知渠道”中进行配置,新版将切换到授权管理模块。需要注意的是,如果从本地无法访问 api.telegram.org,那么通知提醒将直接失败。所以,我更推荐用云函数中转并采用 Webhook 的方式,这样既解决了域名访问受限,也便于统一接口。分享我使用 cloudflare worker 实现的关键代码:

worker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const msg = reqBody['msg'] || '';
const msgParts = msg.split('|');
const messageText = decodeURIComponent(msgParts[0] || '');
const buttonUrl = msgParts[1] || '';
const buttonText = decodeURIComponent(msgParts[2] || '🔗链接');

let param = {
chat_id: env.TG_CHAT_ID,
parse_mode: 'markdown',
text: messageText,
reply_markup: {
inline_keyboard: []
}
}

if (buttonUrl) {
param.reply_markup.inline_keyboard.push([{
text: buttonText,
url: buttonUrl
}]);
}

let resp = await sendMessage(`${env.TG_HOST}${env.TG_TOKEN}/sendMessage`, param);
respBody.push('tgbot: ' + resp.status)

这里还有另一个坑🤧,注意 Webhook 回调地址不能有前后空格,否则也是直接失败,这在复制粘贴的时候容易发生。我想说,对用户输入的信息 trim 一下不是默认操作吗:尴尬:

那么朋友,三个坑你都踩过吗?

另外,失败了不要怕,大胆 debug,正常情况下你应该设置了短时间重复执行工作流不会再次申请证书。发现了新的 bug 记得去仓库提 issue。开源你我他,维护靠大家~

React SPA: Live Photo and motion photo webui for browser context

2025-08-10 22:45:00

:webui header

Introducing my new work, motion live photo webui, or MLPW for short. The name is a blend of “motion photo” and “live photo”, and it also carries a double meaning of “rhythmic life.”

After finishing the live photo component for my blog, I started thinking about a web UI uploader for live photos, similar to the R2 uploader. The initial idea was simple: use ffmpeg.wasm to compress images and videos (splitting them first if it’s Google’s single JPG format), and then upload them to the cloud via browser XHR.

Since I was building a Single Page Application (SPA), React was the obvious choice! It was a great opportunity to practice and get my feet wet with React. So, what started as a simple goal ended up as a feature-rich smorgasbord 🥘, including things like: 📌 creating Google-style single-file motion photos, converting videos to live photos (by extracting a frame for the cover), a Web Hook UI, and more… For a full list of features, check out the project’s README.

As a React newbie 🐣, the development process was full of bumps and bruises, with flaws that required various workarounds. 📌 For example, ffmpeg transcoding would sometimes fail, either due to insufficient memory or just getting stuck for no reason. To address this, I added a cancel button and an option to manually adjust command-line parameters. Browsers aren’t omnipotent; for some heavy-duty transcoding tasks, 📌 like converting images to AVIF or HEIF, or generating AV1-encoded videos, they fall short. 💡 So, I came up with another solution: integrated a ☁️ cloud transcoding feature into the upload module, which only requires configuring a custom API. As for the same-origin policy restrictions on uploads, you can set up your own CORS proxy or, more directly, download the separated image and video, transcode them manually, and then use other tools to upload.

There won’t be any principles and differences between motion photos and live photos here. Instead, let’s dive into the various functional modules of MLPW and talk about the little ideas and details.

🗝️ File Loading: Where It All Begins

Every operation starts with importing a file. In theory, common image and video formats are supported. Native HEIC can’t be displayed in the browser, so MLPW automatically calls heic-to to convert it to a standard JPG. For other image formats, it first tries to extract them as live photos (🖼️ and 🎥). If that fails, they are imported as normal images. All imported files (whether extracted/converted or not) are treated as {RAW} class, including 🖼️ extracted via the [File Conversion module](#⚙️ File Conversion: The Core Function). Newly imported RAW files overwrite old ones by default, and the corresponding {CONVERTED} class files are also destroyed. There’s one exception: when a video extracted from a live photo is about to overwrite an existing RAW video, a confirmation dialog will pop up, since importing an image to overwrite a video is a bit counter-intuitive 🙅.

📺 Multimedia Display: WYSIWYG

Panel Display
LIVE

After a successful file import, you’ll see the status of the three tabs change. If you encounter playback failure with newer video encoding formats like H265, don’t panic. This won’t affect subsequent transcoding operations. The successfully transcoded CONVERTED video will automatically replace the unplayable RAW video. In other cases, the 🎥 tab only displays the RAW video. After transcoding, the 🖼️ tab will show a compare slider, similar to Squoosh’s slider, to easily compare the image quality difference before and after compression. The :livephoto: live photo control refreshes every time you switch to it and prioritizes loading CONVERTED files.

To provide the most authentic experience, I used Apple’s official LivePhotosKit JS for displaying live photos. You can achieve a similar effect with CSS, and it has many advantages. The principle is not complicated: the core idea is to fade out the original image while zooming in and fading in the video.

⚙️ File Conversion: The Core Function

Function Diagram

Most of the operations here are driven by ❤️‍🔥ffmpeg.wasm❤️‍🔥. For convenience, I used the unpkg source directly. After the first successful load, subsequent loads will be read from the browser cache, eliminating the need for repeated downloads. Switching between single/multi-threaded mode requires reloading the library file, so if you encounter strange transcoding issues, try switching threads or reloading the library. It’s often more effective than refreshing the entire page. Before transcoding, remember to check the options in the dropdown menu; by default, it processes both 🖼️ and 🎥. If you want to fine-tune the ffmpeg parameters before execution, for example, using -qscale:v to control image compression quality, just check the ✔️Manual Parameters box. ⚠️Friendly reminder: do not modify the input and output filenames in the parameters. Clicking the download button will expand a dropdown menu; click the title again to start the download. The button next to it allows you to change the filename (without the extension), and this name will be used for both downloading and uploading.

The 2x2 labels above the buttons show the file size and (expected) resolution for 🖼️ and 🎥. The left side of the arrow is RAW, and the right side is CONVERTED. After the conversion is complete, the → on the right will turn a prominent green.

The three buttons to the right of the multi-thread switch correspond to Resize, 🎬Video Transcoding Settings, and 🖼️Image Transcoding Settings.

  • I was once misled by this article, thinking that LivePhotosKit had strict requirements for resolution and format. In reality, it doesn’t restrict resolution, nor does it mandate JPG format, but it really can’t play H265 encoded videos, even if the browser itself supports it. Nevertheless, I still set a friendly default maximum size for web display: 🖼️1008 x 1344 and 🎥720 x 960 (maintaining the aspect ratio). You can reset this at any time with the ⟳ button in the top right corner of the pop-up, or click ⛶ to keep the original size.

  • 🎬 Video settings include whether to keep audio, set the transcoding format (mp4, mov, and webm), and precise trimming. Drag the 🎥 progress bar to your desired position and click 📸 to record the start and end timestamps. If the RAW video cannot be played, you can manually enter the time or transcode it first and then trim it. Additionally, the transcoding format selected here will also affect the extension of the file returned after cloud transcoding (unless you’ve hardcoded it in the API URL).

  • 🖼️ Image settings include transcoding formats (jpg, png, and webp). Currently, there are no parameters for output quality, but if you really need it, you can use manual parameter tuning as a workaround. The HEIC quality factor only takes effect when importing heic/heif images. The screenshot function allows you to easily extract a cover from the video. By default, it will grab the playback progress of the 🎥 when you open the pop-up. You can choose to capture from the original or transcoded video, or just modify the timestamp value. This value will be used for the live photo component’s data-photo-time attribute, as well as the relevant fields in the XMP tags when creating a live photo.

🚩 The following modules are relatively independent. Click the module title to collapse/expand.

✨ Create Live Photo: Prioritizing OPPO

Creation Module

This feature was added last, with a clear goal: use any image 🖼️ and video 🎥 to create a live photo that can be recognized by the native photo album. JuziJun implemented a simple creation function ƒ that can be recognized by :google-photo: Google Photos and Windows Photos. The key is to write the correct XMP tags. Tests found that photos created with the original ƒ could be recognized by the latest versions of TikTok and :rednote: Xiaohongshu, but not by the native photo album on ColorOS. By chance, I discovered that single-file live photos saved from Xiaohongshu could be perfectly recognized by my phone’s photo album! After some research with WinHex, I found the secret: just replace the EXIF part of the file created by MLPW with the EXIF part from the image saved by Xiaohongshu, and you get perfect compatibility 🎉!

So, if you are using an OPPO series phone, you just need to switch to the corresponding XMP meta preset to export the correct live photo. Since I don’t have other brands of phones on hand, the adaptation plan has to be put on hold for now. However, I tried it out at an offline experience store, and both Xiaomi and Honor only export live photos from Xiaohongshu as two separate files, so I guess it will be even more difficult 🤔. Recently, ColorOS updated a video 🎥:livephoto: feature, but it only allows a maximum of 3 seconds, while the live photos exported by MLPW have no time limit 😎.

If you import a live photo, MLPW will, by default, extract the embedded XMP fields into the input box. If the extraction fails, it will display the currently selected preset value. Clicking the in the upper right corner can clear the input box, and clicking ⟳ can restore the initial XMP data from when the image was loaded.

☁️ Upload & Cloud Convert: Connecting to the Cloud

Upload and Cloud Convert

The Web Hook feature is implemented via XHR and supports four HTTP requests: POST, PUT, DELETE, and GET. You can customize the request headers, and the POST method also supports setting the request body. For example, when using the WebDAV protocol, you need to use the PUT method and add two key headers: Authorization: Basic btoa(USERNAME:PASSWORD) and Content-Type: application/xml. Clicking the 💾 in the upper right corner can save the current server configuration (in JSON format), which will then appear in the API URL dropdown menu. The ⿻ button next to it is for importing and exporting configurations. If you have a valid configuration in your clipboard, it will directly write it to local storage or ask if you want to overwrite; if not, it will actively copy the current configuration to the clipboard, making it easy to migrate configurations between different devices.

Currently, I have successfully tested 📌Junze’s Dooong public image host, 📌R2 Uploader, and 📌a PHP script:linuxdo:. In theory, any standard RESTful API should be compatible. In the API URL, you can use {filename} to represent the filename, and in the request body, use {File} to represent the uploaded file itself. The biggest challenge is still the cross-origin issue. A feasible solution is to compile it into a native App or use a CORS forwarding server 🖧 to solve it.

To make it easier to use the uploaded file links, a one-click copy filename button has also been arranged in the dropdown menu ✌.

Cloud conversion ☁️ is essentially using GET requests to access URL-based transcoding services. 📌Well-known public services like WebP Cloud Services and Cloudflare Image both offer a certain amount of free quota. Each free account gets 5000 free conversions per month, and it’s very simple to use:

  1. First, go to the Cloudflare dashboard and enable the transformation service for your domain on the Images👉Transformations page.
  2. Upload the image to R2 cloud storage. Let’s say your image’s direct link is 🔗https://image.yourdomain.com/raw.jpg.
  3. Add conversion parameters to the link. 📌For example, to convert to webp format with a compression quality of 75, the link becomes:
    https://image.yourdomain.com/cdn-cgi/image/format=webp,quality=75,fit=scale-down/raw.jpg.

To be able to explicitly specify the format after conversion, the final format to be filled into the API URL should look like this:
https://image.yourdomain.com/cdn-cgi/image/format={webp},quality=75,fit=scale-down/{filename}. Here, {filename} will be replaced with the RAW filename, and {webp} will be used as the suffix for the cloud-converted file.

📖 Log: Real-time Recording

Log Module

All operation records will be displayed here. Scrolling to the very bottom will also trigger auto-scrolling for easy real-time tracking.

After more than a month of learning and polishing, MLPW has, in my opinion, reached a usable state, so I wrote this article to share and document it. Of course, it still lacks compatibility testing for models other than OPPO 📱, and there may be some bugs hidden in special scenarios 🐞. Currently, the feature I use most is separating and compressing live photos taken with my phone, and then uploading them to my blog as an image host. In the future, the feature I most want to add is cropping , to achieve a cool media editing effect similar to the web version of 𝕏.

crop media

🆕 Important Updates (Lines 84-96 Translation)

2025.8.27

Vendors

  1. Xiaomi’s gallery app (including third-party apps) can now correctly recognize the synthesized Live Photos.
  2. Implemented cross-origin access via a Tampermonkey userscript. Install it here: https://greasyfork.org/zh-CN/scripts/547102-cors-plugin-for-motion-live

2025.8.18
Adjusted the video transcoding settings page and added a button. This lets you obtain the pixel dimensions of videos that fail to play so you can crop them before transcoding. Previously you had to transcode first, and then the app can read width/height from logs. The function depend on ffprobe command, which is more reliable than parsing log string.

2025.8.17

Crop

The Resize module now integrates react-easy-crop to enable image cropping, rotation, and flipping. The plugin also supports video, but since there’s already a video snapshot feature, unifying on image-based cropping keeps the component simpler. A small quirk: if a CONVERTED image exists, the UI only shows the comparison slider—so how do you re-crop? As a workaround, the action to clear the CONVERTED image is tucked into the crop toggle. One remaining issue: what if you only want to rotate without cropping? For now that likely needs a separate option.