Blog

  • 开源开放的番剧推荐计划 – 友声 Anime Episode Recommender

    帖子发在 bangumi https://bangumi.tv/group/topic/430934

    这个持续开发了将近半年,由七个 GitHub 开源 repo,加上一个暂时还没放到 GitHub 的 repo 共同支撑的项目终于上线啦~

    最开始开发的是 eplot 这个摘要网站。它是许多想法的 base. Epr (友声 Anime Episode Recommender)便是 eplot 项目的一个扩展。Eplot 的一个大缺陷是,用 LLM 生成摘要一定会有错误。而用它来做内容推荐,能极大规避 LLM 摘要错误的影响。

    接下来,一方面根据 bangumi 的反馈继续开发 epr 项目。另一方面,开始考虑基于 eplot 摘要的其他可能性。


    【网页】

    https://epr.oopus.info/

    【使用方法】

    这是一个基于大语言模型 Gemini 2.5 Flash 的开源开放的番剧推荐计划。它的功能专注于番剧推荐。

    使用方法很简单,在网页界面中: https://epr.oopus.info/ , 您可以选择两部您喜欢的番剧,系统就会把这两部番剧的摘要提交给 Gemini 2.5 Flash 模型,向您推荐两到三部和这两部类似的番剧。

    【起因】

    面向影视、书籍的推荐系统很多。我希望做一个侧重于内容相似性的推荐系统,因为内容包含的信息量相比元数据(监督、原画、风格、时代等等)更丰富。

    番剧本身作为画面、时间、语言、声音等的集合,什么是「内容」,以及如何提取「内容」?这需要好好考量。我自己对语义处理更熟悉,所以把问题简化到字幕组的字幕。使用大语言模型对字幕进行总结,反推出故事的「内容」文本。

    一开始的模型使用 deepseek,后来 gemini 2.5 flash 和 pro 发布,它们的汇总能力以及输出速度当时都更好,并且 gemini api 有免费的额度,于是转向 gemini,一直到现在。

    解决了「内容」的问题之后,一开始想要基于 sentence embedding 做内容相似度为主,用元数据作为辅助的番剧推荐系统。这个思路的问题是,它没有办法推荐我还没收集的番剧。由于制作摘要本身耗费时间,当前的主要收录 2022 年至今 bangumi 收藏量前三的一月,四月,七月,十月番。如果推荐系统总是推那些大家都知道的番,它推荐的价值就很低。

    于是试着把番剧的摘要提交给 gemini, 让它依据自己的知识来推荐。测试的结果相当好。在测试中会推荐一些特别冷门但很有道理、有趣的番。

    另一个问题,包含更多细节的「内容」对推荐有价值,但把番剧过于详细的内容提交给 LLM,边际效用会递减。基于这个前提,包含足够细节的文本量,超过两部可能就太多了(input token 大概 1.5K 字左右,output token 大概 1K 字左右)。所以我把这个项目设置为只能选择两部来做推荐的模式。

    【开源的技术实现】

    这是查询界面:

    https://github.com/sudoghut/ep-rec-interface

    这是查询界面后台数据库(所有番剧摘要的数据都开放在它所 clone 的 eplot-data-compiler 项目里面):

    https://github.com/sudoghut/ep-rec-api

    这是用于制作后台数据库的项目(所有生成的番剧摘要数据都在 data.db 这个 sqlite 数据库中,欢迎下载和二次开发) :

    https://github.com/sudoghut/eplot-data-compiler

    这是把所有摘要展示在网页上的另一个开源项目(您可以直接在这里看到 LLM 生成的摘要,也可以使用网页查询内容):

    网页: https://eplot.oopus.info/
    开源代码: https://github.com/sudoghut/eplot

    这是用来轮询 Gemini token 的后端:

    https://github.com/sudoghut/safe-trigger

    这是排队器:

    https://github.com/sudoghut/queue-single-line

    因为使用免费的 token 资源,所以当前的队列长度是最多能容纳 50 个人的单线程排队。每次生成的等待时间大约是半分钟。排队的人多了可能等待时间比较久,大家可以根据当前所在队列的位置预估排到的时间。
    另外,为了应对上线未知的流量,当前的 token 是从咸鱼买来的 50 个。卖家肯定有一 token 多卖的情况,当前测试的状况是一天至少会有两个 token 被封。【注意:如果大家辛苦排完队提示 token 错误,又从队尾开始排,先向大家抱歉。】建议如果在队列中,就把浏览器 tab 放一边,别故意等它。

    这是用于检查 gemini api 是否被封的小工具:

    https://github.com/sudoghut/token-validator

    【欢迎来自您的协力】

    当前这个项目因为对我自己也很有帮助,所以无论如何会至少一个人长期维护。如果您对共同维护这个项目感兴趣,也欢迎一起来维护。当前想到的贡献方式有三种。

    首先,是 eplot https://github.com/sudoghut/eplot 纠错。字幕只是番剧内容的一个侧面,根据画面和声音,容易理解的剧情,但只有字幕,有时人类也可能猜错剧情,更不要说 LLM 了。在使用 eplot 的时候,如果您看到错误的剧情摘要,欢迎直接提交 pr 或者 issue 修改。一定希望提交修改后的内容,而不只是指出错误。因为指出错误之后, LLM 有时也没办法根据有限的字幕信息改对。不过剧情些许错误对 epr (推荐生成)的影响不大。

    第二,番剧的 ass 字幕,有字幕轨的视频文件,或者直接贡献摘要。当前字幕文件主要从 mikan 和 dmhy 下载。感谢这两个项目!大多数是直接从有中文字幕轨(不是烧录在视频里面)的视频里面提取,一小部分字幕组提供了 ass 字幕下载。如果您感兴趣的番剧还没有被 epr 项目收录,欢迎在本帖下回复资源(番剧的 ass 字幕,有字幕轨的视频文件),或者发送站内信。

    如果您熟悉 github,也欢迎直接往 eplot 项目 https://github.com/sudoghut/eplot/tree/main/src/content/blog 里面提交摘要.

    文件名格式是:英文名_集数.md

    内容的格式是:

    ---
    title: "中文标题 集数"
    description: "短摘要"
    date: yyyy-mm-mm
    tags: ["中文名", "英文名", "开播时间,格式为 yyyymm"]
    ---
    
    长摘要

    您也直接参考已有的例子来写 md 文件:

    https://raw.githubusercontent.com/sudoghut/eplot/refs/heads/main/src/content/blog/Apocalypse%20Hotel_01.md

    第三,免费的 gemini token. 如果您有闲置的免费 tier (一定确认不要分享付费的 tiers)的 gemini token, 并且愿意分享,也欢迎通过站内信分享。在 safe-trigger 程序里面,设置的使用频率是不快于每分钟使用一次(当前准备了 50 个 tokens 用于轮询, 每个 token 最快也是二十五分钟使用一次。您也可以明确告诉我,希望轮询您的 token 最短的时间),每次 request 使用的 input token 数量大概是 1500,输出字数大概是 1000. 确认这样的使用下不会影响到您自己 quota 的前提下,欢迎分享。您的 token 将会只用于 epr 的番剧推荐。

  • 模仿者 – 20250702 draft

    思考當前全世界全球化與反全球化博弈時,會看到反全球化以及地方保護主義確實可以帶來本地經濟的增長。當一個全球化企業在本地發展遇到困難(或者並未遇到困難)時,地方性企業通過模仿以及熟諳本地社會的優勢,可以迅速增長。在中國淘寶、騰訊、京東、百度,小米都可以看做這種類型。

    這樣的地域性模仿也確實給本地經濟帶來活力,給本地創造更多的就業,更容易把本地社群中的需要轉化為產品。這個模型從中國作為輸出者也有兩個例子,共享經濟與短視頻。共享經濟和短視頻的起源都可以追述到中國以外,但中國也是這兩種經濟模式巨大發展和成長的區域。這兩種模式重新影響到西方,uber 基於自己的優勢,建立了網約車加共享快遞員的形式。而短視頻的風靡讓全世界各視頻公司都開始建立自己的短視頻產品。

    複製成功模式加上本地化似乎已經從跨文化全球化的巨型企業模式中建立了全新的商用形式(bilibili 在成功之後向更廣泛的群體擴展被許多人詬病,而小紅書繼續深耕「她」經濟至少在當前獲得了巨大的成功)。AI 時代各文化內自己的 AI 產品也同樣如此。

    這不禁讓我想到中國的朝貢體系,以及亞洲各國以自己為中心模仿朝貢體系建立的自己的「天下」參考系。

    這種成功-模仿-碎片的模型也許有助於我們理解當前日益碎片化與對抗的區域文化。比如:對成功的模仿依賴於全球化,而模仿之後,反全球的地方化和區域保護在當前的大量案例中對模仿者盈利反而更有幫助。進一步,強調區域性文化,建立小社群的共同價值觀更容易。再進一步,小社群性的共同價值觀也更容易形成信息繭房。

  • 《周易》兩次占卜結果相同的概率

    截圖來自 Edward Shaughnessy 「『周易』的起源及早期演變 — 」。概率倒是不難算,(1/64)(1/64) = 0.024%

  • Automatic loop any videos

    In your browser’s console, run the following JavaScript code. All videos (elements with <video> tags) will be set to loop automatically.

    CODES:

    // Script to make all videos on the page loop
    (function() {
        // First, handle standard HTML5 video elements
        const videos = document.querySelectorAll('video');
        videos.forEach(video => {
            // Set native loop attribute
            video.loop = true;
            console.log('Set native loop for video:', video);
            
            // Add ended event listener as a fallback
            video.addEventListener('ended', function() {
                this.currentTime = 0;
                this.play();
                console.log('Restarted video via event listener');
            });
        });
        
        // Handle Plyr instances specifically
        if (window.Plyr) {
            console.log('Plyr detected, configuring all instances');
            document.querySelectorAll('.plyr').forEach(plyrElement => {
                const plyrInstance = plyrElement.plyr;
                if (plyrInstance) {
                    plyrInstance.loop = true;
                    console.log('Set loop for Plyr instance:', plyrInstance);
                }
            });
        } else {
            console.log('No Plyr global object found, checking for individual instances');
            // Try to find Plyr instances attached to DOM elements
            document.querySelectorAll('.plyr').forEach(plyrElement => {
                // Look for plyr data in the element
                const video = plyrElement.querySelector('video');
                if (video) {
                    video.loop = true;
                    console.log('Set loop on video within Plyr container');
                }
            });
        }
        
        console.log('✅ Loop enabled for all videos on page');
        return `Modified ${videos.length} video elements`;
    })();

  • 反接 DC 頭電源正負極

    反接 DC 頭電源正負極

    德生 PL-660 的原裝充電線只能進 220V 電壓,在美期間一直沒有辦法讓它外接 110V 市電(而不使用電池)。之後買來萬能可調電源,卻仍然沒辦法充電。以為官方做了什麼握手加密,覺得很神秘。偶然看到一篇文章:

    https://www.hellocq.net/forum/read.php?tid=371819

    才知道 PL 系列原裝充電器(DC-06)正負極是接反的,用萬用表一測,果然如此。接下來就簡單了。買一根 DC 延長線,中間剪斷,正負反接:

    然後外接電源就成功接通啦~

    好久沒有經歷成功的 EE 小手工了(這幾年修什麼壞什麼),雖然是超小的實踐,活也有點糙,但修好東西的快樂是實在的~

  • A special day

    A special day

    超長的這一天發生了那麼多

    • 最終租下週末的 airbnb, 拒絕 partner 來訪
    • 在老爸老媽和老爸家的多方爭執中保持老爸和老媽堅持離婚的立場
    • 2025 H1B 抽籤第二次失敗,今天開始準備離開波士頓
    • 明確拒絕了一份來自歐洲的工作
  • 20250314-15 Columbus, Ohio

    20250314-15 Columbus, Ohio

    Columbus, 一次從起因到結束都充滿未知和意外的旅程。如果不是 Association for Asian Studies(AAS), 大概很難會旅行到 Ohio 的 Columbus.
    本來自己的 project 並不太支持我去 AAS,幾年前有幾次提過,收到的反饋都是和 project 關係不大,如果去只能是自費。還蠻奇怪,如果去 present, 除了所在的這個 project ,我大概也不太願意 present 其他的內容。但無論如何,因為這個淵源,即使在家邊的 2023 Boston AAS, 也沒有動力去參加。經歷過去年 2024.8 和 2025.2 的一些事後,對於工作的狀態從建設有部分共同認知學術社區,變成:站在 project manager 的角色上,完美做好輸出和發展的規劃。把情緒從工作中抽離出來。並且在工作之外匿名創造:完全由自己的創造力、感性、理性生成的,有生命力的,能和世界更多部分以純粹「就事論事」方式互動的,有反饋的作品。
    今年因為有一個 panel 涉及一些奇妙的「政治張力」,所以項目有限地支持我去 AAS. 於是,三月十四號晚上出發,十五號 present 完,當晚回家。Present 過程很享受。大概因為來去匆忙,不容易陷入怪事情,所以也 enjoy 許多和熟人的見面。不過也 detect 到一些人「政治性」的變化。這樣想來,充滿學術政治博弈的 AAS,未來還是遠離一些比較好。不過反過來,如果不是一個大型學術政治舞台,也不會吸引這麼多人。
    會議期間老爸身體檢查血色素異常低,可能是胃癌,然後一個人去檢查、住院,以及和老媽繼續你死我活就不離婚的爭鬥,以及在他們家族群裡面抱怨死活,幾個姑姑叔叔向我瘋狂打電話,發消息。給整個旅行增加了一條有張力的故事線。
    以及降落波士頓之後,能 track 到行李已經抵達波士頓,但它就是不存在。給機場留了電話,第二天早晨愉快地收到行李快遞。本來為了避免丟行李,一直盡量不托運,但 Delta E175 飛機的隨身行李有限(預計 Delta 2026 年才能完成 E175 所有行李架的擴容),所以被要求免費托運登機箱。下次旅行的時候即使是登機箱,也一定要放一個 apple tag. 這也為旅行增加了段奇妙的故事。
    從二月開始,恢復非工作時間退出微信。確保自己獨處、獨自反思和獨斷創造的時間。在新生活方式下,還蠻享受上述所有這些來自生活的點點滴滴。如果人生目標是成魔而獨立,進而用自己的方式擴張認知,干涉宇宙。那麼這些又算是什麼呢?回來的晚上是 Saint Patrick’s Day,加上是齋月的晚上,整個城市沉浸在節日氛圍裡面。到家~ 叫炸雞~ 開啤酒~ 歡度周末到凌晨四點~ 超開心!
    雖然從出家門到回家,旅行時長大概只有三十一個小時,但仍然抽出兩段時間來 city walk. 那麼接下來就是看圖話說~

    Ohio statehouse

    文藝復興式建築風格的三角楣 pediment 和圓頂 cupola 讓它在 Columbus 的 downtown 裡特別引入注目。非常神奇的是,建築物上有一些十九世紀 Ohio 監獄囚犯的塗鴉。關於這棟建築,這裡有非常詳細的說明:https://www.ohiostatehouse.org/about/capitol-square/statehouse
    臨街的雕像是 William McKinley, Ohio 出生的州长和以及美國第二十五任總統。雕像下方是对 William McKinley 的引用:
    Let us ever remember that our interest is in concord, not in conflict; and that our real eminence rests in the victories of peace, not those of war.
    God will graciously vouchsafe prosperity, happiness and peace to all our neighbors, and like blessings to all the peoples and powers of earth.

    俄司庫鼎


    看到 Ohio National Bank 外面這件青銅器格外親切。按照青銅器的命名,也許會被叫做俄(Ohio state)司庫(bank)無耳雷紋圓底獅面長足鼎。看到它,開始琢磨它的命名方式,是當晚最可樂的事。

    The Dream of the City

    Columbus Commons 前的一段通道。右邊是戲仿葛飾北齋章魚與海女的章魚與城市。這段通道的直觀感受,對我來說,是一條長長的陰道。

    Columbus Union Station Arch

    Columbus 聯合車站拱門。1897 年完工,曾經是有軌電車與火車的交通樞紐与 Ohio 地標。今已經完全廢棄成為公園。

    變壓器與建築遺跡

    在 Nationwide Arena 附近。一開始以為是墓地,走進才發現是建築構件。沒發現和這些建築構建相關的的信息。

    Olentangy river

    Columbus 两条重要的河流之一(另一条是 Scioto river)。特拉華印第安人(Leni Lenape)把它叫做 Keenhongsheconsepung,表示 sharp/more and more/tool river,被解讀為因河邊有大量可以用來磨刀的頁岩(如照片所見)而得名。殖民者根據磨刀石把它直譯成 Whetstone. 1833 年 Ohio 立法希望恢復印第安語水道名的時候,不知道誰說,它叫:Olentangy. 您這麼一說,我這麼一記,得嘞。而實際上叫做 Olentangy 的水道在這張照片的更西邊。今天叫做 Big Darby,並且是 creek 溪流,一條小水道。於是 Olentangy 錯失被正確命名為 Keenhongsheconsepung 的機會。也有可能是當時大家嫌棄 Keenhongsheconsepung 這個詞太長了。

    Scioto river

    Columbus 的最大的水流,是 Ohio 境內最長的水流,也是 Ohio 州徽(Ohio state seal)上水流名字。水面相比 Olentangy 寬得多,岸邊坡道很緩,以前沿河有許多居民區(見河邊的歷史照片),河面上的鐵路橋沿用至今(見照片)。

  • Parassis 上線~

    Parassis 上線~

    經過一個月做做停停,Parassis https://pa.oopus.info 初始版本終於上線啦~~~

    Parassis 是 paragraph assistant 的縮寫。它會使用 LLM 對文本(主要是故事型文本)進行分析,以便閱讀。

    登錄網站的時候需要輸入 LLM token, 暫時用的都是最省錢的模型,效果也不錯。

    輸入 token 之後,可以通過 下圖的(1)按鈕上傳文檔,文檔必須是 markdown 格式。

    在使用上圖(3)或者(4)翻頁的時候,下方 Assistant 區域會自動根據當前文本和前文對文本中自動梳理故事線索。

    (5)是目錄,它會根據 markdown 文件中的 headings(標題)自動生成文本目錄。要注意,跳轉到某頁,或者剛剛上傳文本的之後不會自動觸發任何 AI 功能,如果需要在跳轉和上傳後梳理當前頁的線索,可以使用(2)圖標,手動生成當前頁的線索梳理。

    (6)可以重新打開設置按鈕來選擇其他 LLM,或者改變輸出文本的語言。

    初始設計的時候還有一些其他功能,包括根據特定主題來分析當前頁,與當前頁對話,基於某些原則修改當前頁的內容等等。未來慢慢再加。因為最近想要開一個新 project, 而且和 parassis 有關係。所以就愉快把開發了一半的 parassis 上線吧~ 一方面便於自己使用,以及積累後續開發和修改思路。另一方面,也把開發重心轉向新 project.

    Parassis 項目中大量使用 cline 和 Github copliot來實現功能以及修問題。大大增加開發進度,以及解決問題的效率。會學到很多。在開發後期,用 copliot 協助自定義的細節的頻率遠遠多於初期。在部署到 vercel 的時候,把 vite.config.ts proxy 轉寫成 vercel.json 完全是讓 cline 寫,非常舒適。不過確認要用 vercel.json 解決 vercel proxy 問題還是手動搜索解決方案,cline 並沒有發現這個問題的解決方案。

  • 與一位失業者的對話

    今天和一位七十年代的本科生進行了一次工作談話。對方在美國獲得博士學位,去年自己的公司參加了一起收購案,被收購後,員工被裁員,輸得乾乾淨淨。今年從零開始找工作。

    思考並學到(更確認)的有:

    • 一旦參加博弈,輸了只是自己蠢。不依賴、責怪任何人分享經驗或者建議。
    • 不在不熟悉的情況下僅憑利益做決定。
    • 不要僅憑興趣沉溺在某個領域。
    • 確保足夠的自己的時間思考過去、當下、未來。
    • 命運(已經、並將)決定在自己的手裡,而不是任何人,任何集體,任何國家。
    • 工作的專業性是把活和需求在規定時間內高質量做好。其他一切負面的情緒都是需要解決掉的問題。
    • 時間只有在(無論是憑理性還是憑感性)規劃/衝動下才有。否則,永遠不會有時間。
  • Using mkvtoolnix to extract subtitles from MKV files

    Download mkvtoolnix

    https://mkvtoolnix.download/downloads.html#windows

    Finding your subtitle id

    • Open mkvtoolnix-gui from the mkvtoolnix folder;
    • (See the screenshot below)Add your MKV file to (1). If (2) is .sub or other subtitle format that you are familiar with, you can extract it by clicking Start multiplexing. That’s it! If not, go to (3) and then remember the TRACK_ID

    Extract the subtitle

    • Use command line or terminal tool to get into your mkvtoolnix folder. For me, it is: cd C:\Users\{user}\Downloads\mkvtoolnix
    • Run the following command: (replace it by your own path and the TRACK_ID which is a number that you got from the previous step)
    mkvextract tracks "C:\Users\{your path and file name}.mkv" {TRACK_ID}:"C:\Users\{your path and file name}subtitle.ass"

    After getting the ass file

    You can use any text editing tool to open this .ass file, then you will see all the subtitles. You can also use ffmpeg, ChatGPT or other tools to convert it into .srt file.