Category: computer science

  • clash verge + proxifier only proxies part of your traffic issue

    If you find that your Clash Verge + Proxifier setup only proxies part of your traffic, double-check whether the port opened from Clash Verge to Proxifier is set to Mixed Port or another type. If it is not set to Mixed Port, change it to Mixed Port, and it should work.

    This issue can occur with VS Code and GitHub VS Code.

    On the other hand, for VS Code and Docker, if you are using Proxifier, please change your “localhost” proxy rule to Direct instead of Proxy.

  • 开源开放的番剧推荐计划 – 友声 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 的番剧推荐。

  • 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`;
    })();

  • 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.

  • nextjs SSG + rust web server

    Create components for specific hooks

    If there are some special hooks were used, for example useSearchParams(), wrap it as a component, using suspense to load it. Ex:

    import { Suspense } from 'react';
    import ChatComponent from './ChatComponent';
    
    export default function Page() {
     return (
      <div className="font-sans max-w-3xl mx-auto p-5">
        <h1 className="text-2xl font-bold mb-4 text-center">Yidam Chat</h1>
        <Suspense fallback={<div>Loading chat...</div>}>
         <ChatComponent />
        </Suspense>
      </div>
     );
    }

    Set next.config.ts

    import type { NextConfig } from "next";
    
    const nextConfig: NextConfig = {
     /* config options here */
     reactStrictMode: true,
     output: 'export',
     basePath: '/web',
    };
    
    export default nextConfig;

    Notes

    • Set output: 'export' for npm run build
    • Set basePath: '/web' for the sub-directory of the web root

    Set package.json

     "scripts": {
      "dev": "next dev",
      "build": "next build",
      "start": "next start",
      "lint": "next lint"
     },

    Notes: just keep as it was

    Run command to generate files

    npm run build

    (Optional) Rust web server setting

    use axum::{
      routing::{get, get_service},
      Router,
    };
    
    async fn main() {
      let state = Arc::new(AppState {
        context: Mutex::new(Vec::new()),
      });
    
      let app = Router::new()
      .route("/ws", get(websocket_handler))
      .nest_service("/web", get_service(ServeDir::new("./src/web")))
      .with_state(state);
      const PORT: u16 = 3001;
      // get lan ip
      let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", PORT)).await.unwrap();
      println!("Please open http://{}:{}/web", get_local_ip().unwrap(), PORT);
      axum::serve(listener, app).await.unwrap();
    }
  • Sautoyrs: Erotic literature generation platfrom

    Sautoyrs: Erotic literature generation platfrom

    After one month work, today is the first day that I opened my erotic literature generation platform to the public: https://github.com/sudoghut/Sautoyrs

    It is a web application that generates romantic and explicit sexual stories automatically using a large language model. It creates interactive narratives between two characters with customizable settings. It can only work on Chrome /Firefox on desktop or laptop.

    Sautoyrs came from satyr:

    In Greek mythology, a satyr[a] (Ancient Greek: σάτυρος, romanized: sátyros, pronounced [sátyros]), also known as a silenus[b] or silenos (Ancient Greek: σειληνός, romanized: seilēnós [seːlɛːnós]), and sileni (plural), is a male nature spirit with ears and a tail resembling those of a horse, as well as a permanent, exaggerated erection. Early artistic representations sometimes include horse-like legs, but, by the sixth century BC, they were more often represented with human legs.[4] Comically hideous, they have mane-like hair, bestial faces, and snub noses and they always are shown naked. Satyrs were characterized by their ribaldry and were known as lovers of wine, music, dancing, and women. They were companions of the god Dionysus and were believed to inhabit remote locales, such as woodlands, mountains, and pastures. They often attempted to seduce or rape nymphs and mortal women alike, usually with little success. They are sometimes shown masturbating or engaging in bestiality.

    Since that it can generate conversations automatically, I added auto into satyrs. It became sautoyrs.

    This is the web traffic statistics for the first day. Not as good as I expected:

    Anyway, I have done my best.

  • Debugging Pitfalls: Async forEach and Slice Memory Sharing in JavaScript

    From last Thursday until just now (Saturday), two issues confused me a lot:

    forEach is synchronous, not asynchronous

    This is a fundamental concept, but problems can arise when you assume a block of code will execute synchronously while using forEach. For some reason, if that block of code becomes asynchronous, it could lead to unexpected issues.

    slice creates a shallow copy

    shortConversation = […systemPrompt, …chatThread.current.slice(-(memoryLength + 2 - 1))];

    The slice method causes shortConversation and chatThread to share part of the memory, which led to unexpected behaviors.

    The corrected code is as follows:

    shortConversation = [...systemPrompt, ...structuredClone(chatThread.current.slice(-(memoryLength + 2 - 1)))];

    Using structuredClone ensures that shortConversation and chatThread are completely independent.

  • 【Imghorce】Shopping and gift 背後的故事

    【Imghorce】Shopping and gift 背後的故事

    最近忙著做戀愛腦 AI 自動談戀愛的 project, 好久沒更新圖片了。年底有一個巨大的大眾主題~ 它就是購物和禮物。那麼做一期購物和禮物叭~

    https://imghorce.oopus.info/shopping-and-gift-20241111

    這次用到的 LORA 模型是:

    巧克力咖啡:https://civitai.com/models/197998/chocolate-coffee-style-lora-15sdxl?modelVersionId=890785

    瓷器:https://civitai.com/models/209773/porcelain-style-fluxsdxl?modelVersionId=908818

    沙:https://civitai.com/models/208699/sand-style-fluxsdxl?modelVersionId=908805

    有了之前風格化的經驗,這回輕車熟路。但在第一輪實驗時發現,場景裡面有大量的人物,出圖特別崩。第二輪正式做的時候,決定完全擯棄人物。效果不錯。本來還想做一隻貓貓在電腦前購物,它頭上有一個巨大的幻象。幻象是張牙舞爪的貓貓靈四處出擊(前段時間巨大動物靈在 reddit 上特別流行)。但考慮到這個項目的商業屬性,當前還是盡量少些個人化整活啦~ 另外,關於購物和禮物主題的文案可以寫蠻多,不過年底還有其他的機會。打算留給之後寫,所以這期的文案就幾乎為零了。圖片也純糖水~

    今天也測試了自動化 markdown 生成,暨自動化圖片重命名工具 https://github.com/sudoghut/bookworm-light-astro/tree/main/generate_data,大大增加了部署的效率。

    週末的時候約一位之前很好的朋友月底看演唱會,得知對方剛剛有了男朋友,要和男朋友去。寂し, 挺難過的。

  • Imghorce is online – Halloween

    Imghorce is online – Halloween

    一直想要邊學圖片生成相關技術一邊發表自己的圖片。可絕大多數圖片站或者不接受 AI 生成的圖片;或者對圖片上傳數量比較大的限制,對於邊學習邊發表記錄歷程的想法就很不友好。於是,自己做一個叭~ 它叫做 Imghorece. 來自 Image 和 Force.

    網站的架構是 Astro, 能容易地部署在 netlify 上. 就像 Astro 的宣言 The web framework for content-driven websites. 部署之後,雖然需要改一些代碼,但完成基本的網頁功能簡化和平面設計之後,接下來確實就可以安心專注內容了。至於圖片站 blog 裡面 md 格式,被我設計得有些複雜,並且設計還在修改中。這個 md 格式手寫起來有點麻煩,但在基本內容板塊設計穩定下來之後,用 python 寫一個 md 生成器就很方便了。

    圖片之前考慮過部署在 GitHub. 仔細考慮之後,還是放在自己在 Hostgator 的網站上,因為 Astro 這類非數據庫類型的平台很少有點擊數量追蹤功能。我還蠻希望在網站的運營過程中知道各圖片受歡迎的程度。而文件被訪問的記錄,透過 Hostgator 很輕鬆地可以知道。

    關於圖片生成,當前選型是 Flux.1 Schunell. 這個模型允許商用,所以網站分享出來之後,無論別人如何使用,還是我自己未來在網站上加廣告條也都少了很多麻煩。另一方面,如果未來如果運營得健康,買 Flux 的會員用 dev 或者 pro 生可商用的圖也是未來可能的方案。許可證選擇了比較寬鬆的:Creative Commons Attribution 4.0 International licence.

    這裡記錄了 Imghorce 圖片站的宗旨:https://imghorce.oopus.info/authors/img-oop/

    第一个 POST 是即將到來 Halloween 相關的圖片: https://imghorce.oopus.info/post-halloween-20241020/

    這裡也分享一張沒有上傳到圖片站上的圖片(這張圖用的模型是 Flux.1 Dev. 對於商用並沒有那麼自由)。它叫做 Greeky. 在 Morton, Lisa. 2012. Trick or Treat : A History of Halloween. London: Reaktion Books 中提到 Halloween 的起源:

    “Samhain was also an important day for administration, akin to the US Tax Day in modern times. A yearly gathering was held at Tara, the ancient seat of kings, where three days’ worth of feasting and sporting alternated with debt repayment and trials (those who were found guilty of particularly severe crimes were executed then as well).” Deepl 翻譯: Samhain 也是一個重要的行政日,類似於現代美國的納稅日。一年一度的聚會在古代國王的所在地塔拉 (Tara) 舉行,三天的宴會和體育活動與還債和審判交替進行(那些被判犯有特別嚴重罪行的人也會在這一天被處決)。

    於是生成的思路是對王權(國王)和財權(商人)之間在 Samhain 節相互博弈的想象。